protected override async Task SaveValidateAsync(List <ResponsibilityCenterForSave> entities)
        {
            // Check that codes are not duplicated within the arriving collection
            var duplicateCodes = entities.Where(e => e.Code != null).GroupBy(e => e.Code).Where(g => g.Count() > 1);

            if (duplicateCodes.Any())
            {
                // Hash the entities' indices for performance
                Dictionary <ResponsibilityCenterForSave, int> indices = entities.ToIndexDictionary();

                foreach (var groupWithDuplicateCodes in duplicateCodes)
                {
                    foreach (var entity in groupWithDuplicateCodes)
                    {
                        // This error indicates a bug
                        var index = indices[entity];
                        ModelState.AddModelError($"[{index}].Code", _localizer["Error_TheCode0IsDuplicated", entity.Code]);
                    }
                }
            }

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.ResponsibilityCenters_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #2
0
        protected override async Task SaveValidateAsync(List <CurrencyForSave> entities)
        {
            foreach (var(entity, index) in entities.Select((e, i) => (e, i)))
            {
                // Ensure that Id is supplied
                if (string.IsNullOrEmpty(entity.Id))
                {
                    string path = $"[{index}].{nameof(entity.Id)}";
                    string msg  = _localizer[nameof(RequiredAttribute), _localizer["Code"]];

                    ModelState.AddModelError(path, msg);
                }
                else if (entity.Id.Length > 3)
                {
                    string path = $"[{index}].{nameof(entity.Id)}";
                    string msg  = _localizer[nameof(StringLengthAttribute), _localizer["Code"], 3];

                    ModelState.AddModelError(path, msg);
                }
            }

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                // null Ids will cause an error when calling the SQL validation
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.Currencies_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #3
0
        protected override async Task SaveValidateAsync(List <ResourceDefinitionForSave> entities)
        {
            int index = 0;

            entities.ForEach(entity =>
            {
                if (entity.DefaultVatRate < 0m || entity.DefaultVatRate > 1m)
                {
                    var path = $"[{index}].{nameof(ResourceDefinition.DefaultVatRate)}";
                    var msg  = _localizer["Error_VatRateMustBeBetweenZeroAndOne"];

                    ModelState.AddModelError(path, msg);
                }

                index++;
            });

            ModelState.ThrowIfInvalid();

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.ResourceDefinitions_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
        protected override async Task SaveValidate(FinancialSettingsForSave entity)
        {
            // Attribute Validation
            var meta = _metadataPrvider.GetMetadata(_tenantIdAccessor.GetTenantId(), typeof(FinancialSettingsForSave));

            ValidateEntity(entity, meta);

            // Make sure the archive date is not in the future
            if (entity.ArchiveDate != null && entity.ArchiveDate.Value > DateTime.Today.AddDays(1))
            {
                ModelState.AddModelError(nameof(entity.ArchiveDate),
                                         _localizer["Error_DateCannotBeInTheFuture"]);
            }

            if (ModelState.HasReachedMaxErrors)
            {
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.FinancialSettings_Validate__Save(entity, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);

            return;
        }
Beispiel #5
0
        protected override async Task DeleteValidateAsync(List <int> ids)
        {
            int jvDefId = _defCache.GetCurrentDefinitionsIfCached()?.Data?.ManualJournalVouchersDefinitionId ??
                          throw new BadRequestException("The Manual Journal Voucher Id is not defined");

            int index = 0;

            ids.ForEach(id =>
            {
                if (id == jvDefId)
                {
                    string path = $"[{index}]";
                    string msg  = _localizer["Error_CannotModifySystemItem"];

                    ModelState.AddModelError(path, msg);
                }

                index++;
            });

            // No point carrying on
            ModelState.ThrowIfInvalid();

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.DocumentDefinitions_Validate__Delete(ids, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #6
0
        protected override async Task SaveValidateAsync(List <AccountForSave> entities)
        {
            //foreach (var (entity, index) in entities.Select((e, i) => (e, i)))
            //{
            //    if (ModelState.HasReachedMaxErrors)
            //    {
            //        // No need to keep going forever
            //        break;
            //    }
            //}

            //// No need to invoke SQL if the model state is full of errors
            //if (ModelState.HasReachedMaxErrors)
            //{
            //    // null Ids will cause an error when calling the SQL validation
            //    return;
            //}

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.Accounts_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
        protected override async Task ValidateDeleteWithDescendantsAsync(List <int> ids)
        {
            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.ResponsibilityCenters_Validate__DeleteWithDescendants(ids, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #8
0
        protected override async Task SaveValidateAsync(List <UnitForSave> entities)
        {
            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.Units_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #9
0
        protected override async Task DeleteValidateAsync(List <int> ids)
        {
            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.Units_Validate__Delete(ids, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #10
0
        protected override async Task SaveValidateAsync(List <UserForSave> entities)
        {
            // Hash the indices for performance
            var indices = entities.ToIndexDictionary();

            // Check that line ids are unique and that they have supplied a RoleId
            var duplicateLineIds = entities
                                   .SelectMany(e => e.Roles) // All lines
                                   .Where(e => e.Id != 0)
                                   .GroupBy(e => e.Id)
                                   .Where(g => g.Count() > 1)        // Duplicate Ids
                                   .SelectMany(g => g)
                                   .ToDictionary(e => e, e => e.Id); // to dictionary

            foreach (var entity in entities)
            {
                var lineIndices = entity.Roles.ToIndexDictionary();
                foreach (var line in entity.Roles)
                {
                    if (duplicateLineIds.ContainsKey(line))
                    {
                        // This error indicates a bug
                        var index     = indices[entity];
                        var lineIndex = lineIndices[line];
                        var id        = duplicateLineIds[line];
                        ModelState.AddModelError($"[{index}].{nameof(entity.Roles)}[{lineIndex}].{nameof(entity.Id)}",
                                                 _localizer["Error_TheEntityWithId0IsSpecifiedMoreThanOnce", id]);
                    }

                    if (line.RoleId == null)
                    {
                        var index           = indices[entity];
                        var lineIndex       = lineIndices[line];
                        var propName        = nameof(RoleMembershipForSave.RoleId);
                        var propDisplayName = _metadataProvider.GetMetadataForProperty(typeof(RoleMembershipForSave), propName)?.DisplayName ?? propName;
                        ModelState.AddModelError($"[{index}].{nameof(entity.Roles)}[{lineIndex}].{nameof(RoleMembershipForSave.RoleId)}",
                                                 _localizer[nameof(RequiredAttribute), propDisplayName]);
                    }
                }
            }

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _appRepo.Users_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
        private async Task PermissionsPreprocessAndValidate(int accountId, int custodyId, ReconciliationSavePayload payload)
        {
            // Authorized access (Criteria are not supported here)
            var permissions = await _repo.PermissionsFromCache(VIEW, Constants.Update, default);

            if (!permissions.Any())
            {
                throw new ForbiddenException();
            }

            // Makes the subsequent logic simpler
            payload.ExternalEntries ??= new List <ExternalEntryForSave>();
            payload.Reconciliations ??= new List <ReconciliationForSave>();
            foreach (var reconciliation in payload.Reconciliations)
            {
                reconciliation.Entries ??= new List <ReconciliationEntryForSave>();
                reconciliation.ExternalEntries ??= new List <ReconciliationExternalEntryForSave>();
            }

            // Trim the only string property
            payload.ExternalEntries.ForEach(e =>
            {
                if (e != null && e.ExternalReference != null)
                {
                    e.ExternalReference = e.ExternalReference.Trim();
                }
            });

            // C# Validation
            int?tenantId = _tenantIdAccessor.GetTenantIdIfAny();

            var exEntryMeta = _metadata.GetMetadata(tenantId, typeof(ExternalEntryForSave));

            ValidateList(payload.ExternalEntries, exEntryMeta, "ExternalEntries");

            var reconciliationMeta = _metadata.GetMetadata(tenantId, typeof(ReconciliationForSave));

            ValidateList(payload.Reconciliations, reconciliationMeta, "Reconciliation");

            ModelState.ThrowIfInvalid();

            // SQL Validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.Reconciliations_Validate__Save(
                accountId : accountId,
                custodyId : custodyId,
                externalEntriesForSave : payload.ExternalEntries,
                reconciliations : payload.Reconciliations,
                top : ModelState.MaxAllowedErrors);

            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
            ModelState.ThrowIfInvalid();
        }
Beispiel #12
0
        protected override async Task SaveValidateAsync(List <AgentForSave> entities)
        {
            // TODO: Add definition validation and defaults here

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.Agents_Validate__Save(DefinitionId, entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #13
0
        protected override async Task SaveValidateAsync(List <DocumentDefinitionForSave> entities)
        {
            int docDefIndex = 0;

            entities?.ForEach(docDef =>
            {
                if (docDef.LineDefinitions == null || docDef.LineDefinitions.Count == 0)
                {
                    string path = $"[{docDefIndex}].{nameof(DocumentDefinition.LineDefinitions)}";
                    string msg  = _localizer["Error_OneLineDefinitionIsRquired"];

                    ModelState.AddModelError(path, msg);
                }
                else
                {
                    // Line Definitions that are duplicated within the same document
                    var duplicateIndices = docDef.LineDefinitions
                                           .Select((entity, index) => (entity.LineDefinitionId, index))
                                           .GroupBy(pair => pair.LineDefinitionId)
                                           .Where(g => g.Count() > 1)
                                           .SelectMany(g => g)
                                           .Select((_, index) => index);

                    foreach (var index in duplicateIndices)
                    {
                        string path = $"[{docDefIndex}].{nameof(DocumentDefinition.LineDefinitions)}[{index}].{nameof(DocumentDefinitionLineDefinition.LineDefinitionId)}";
                        string msg  = _localizer["Error_DuplicateLineDefinition"];

                        ModelState.AddModelError(path, msg);
                    }
                }

                docDefIndex++;
            });

            // No point carrying on if invalid
            ModelState.ThrowIfInvalid();

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.DocumentDefinitions_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
        protected override async Task SaveValidateAsync(List <ReportDefinitionForSave> entities)
        {
            var defs     = _defCache.GetCurrentDefinitionsIfCached().Data;
            var settings = _settingsCache.GetCurrentSettingsIfCached().Data;

            foreach (var(entity, index) in entities.Select((e, i) => (e, i)))
            {
                if (entity.ShowInMainMenu ?? false)
                {
                    if (string.IsNullOrWhiteSpace(entity.Title))
                    {
                        string path = $"[{index}].{nameof(entity.Title)}";
                        string msg  = _localizer["Error_TitleIsRequiredWhenShowInMainMenu"];

                        ModelState.AddModelError(path, msg);
                    }
                }

                foreach (var(parameter, paramIndex) in entity.Parameters.Select((e, i) => (e, i)))
                {
                    // TODO: Need to figure out how to retrieve the default control
                    var errors = ControllerUtilities.ValidateControlOptions(parameter.Control, parameter.ControlOptions, _localizer, settings, defs);
                    foreach (var msg in errors)
                    {
                        ModelState.AddModelError($"[{index}].{nameof(entity.Parameters)}[{paramIndex}].{nameof(parameter.ControlOptions)}", msg);
                    }
                }
            }

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                // null Ids will cause an error when calling the SQL validation
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.ReportDefinitions_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #15
0
        protected override async Task DeleteValidateAsync(List <int> ids)
        {
            // Make sure the user is not deleting his/her own account
            var userInfo = await _appRepo.GetUserInfoAsync();

            var index = ids.IndexOf(userInfo.UserId.Value);

            if (index >= 0)
            {
                ModelState.AddModelError($"[{index}]", _localizer["Error_CannotDeleteYourOwnUser"].Value);
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _appRepo.Users_Validate__Delete(ids, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #16
0
        protected override async Task SaveValidateAsync(List <ReportDefinitionForSave> entities)
        {
            foreach (var(entity, index) in entities.Select((e, i) => (e, i)))
            {
                // Ensure that Id adhers to maximum size
                if (entity.Id.Length > 50)
                {
                    string path = $"[{index}].{nameof(entity.Id)}";
                    string msg  = _localizer[nameof(StringLengthAttribute), _localizer["Id"], 3];

                    ModelState.AddModelError(path, msg);
                }

                if (entity.ShowInMainMenu ?? false)
                {
                    if (string.IsNullOrWhiteSpace(entity.Title))
                    {
                        string path = $"[{index}].{nameof(entity.Title)}";
                        string msg  = _localizer["Error_TitleIsRequiredWhenShowInMainMenu"];

                        ModelState.AddModelError(path, msg);
                    }
                }
            }

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                // null Ids will cause an error when calling the SQL validation
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.ReportDefinitions_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #17
0
        protected override async Task SaveValidateAsync(List <AccountForSave> entities)
        {
            foreach (var(entity, index) in entities.Select((e, i) => (e, i)))
            {
                //if (entity.IsSmart ?? false)
                //{
                //    // These are required for smart accounts
                //    if(entity.ContractType == null)
                //    {
                //        string path = $"[{index}].{nameof(entity.ContractType)}";
                //        string propDisplayName = _localizer["Account_ContractType"];
                //        string errorMsg = _localizer[nameof(RequiredAttribute), propDisplayName];

                //        ModelState.AddModelError(path, errorMsg);
                //    }
                //}

                if (ModelState.HasReachedMaxErrors)
                {
                    // No need to keep going forever
                    break;
                }
            }

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                // null Ids will cause an error when calling the SQL validation
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.Accounts_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #18
0
        protected override async Task SaveValidateAsync(List <ResourceForSave> entities)
        {
            var definition = Definition();

            // Set default values
            SetDefaultValue(entities, e => e.Identifier, definition.IdentifierDefaultValue);
            SetDefaultValue(entities, e => e.CurrencyId, definition.CurrencyDefaultValue);
            SetDefaultValue(entities, e => e.MonetaryValue, definition.MonetaryValueDefaultValue);
            SetDefaultValue(entities, e => e.CountUnitId, definition.CountUnitDefaultValue);
            SetDefaultValue(entities, e => e.Count, definition.CountDefaultValue);
            SetDefaultValue(entities, e => e.MassUnitId, definition.MassUnitDefaultValue);
            SetDefaultValue(entities, e => e.Mass, definition.MassDefaultValue);
            SetDefaultValue(entities, e => e.VolumeUnitId, definition.VolumeUnitDefaultValue);
            SetDefaultValue(entities, e => e.Volume, definition.VolumeDefaultValue);
            SetDefaultValue(entities, e => e.TimeUnitId, definition.TimeUnitDefaultValue);
            SetDefaultValue(entities, e => e.Time, definition.TimeDefaultValue);
            //SetDefaultValue(entities, e => e.Description, definition.DescriptionDefaultValue);
            //SetDefaultValue(entities, e => e.Description2, definition.Description2DefaultValue);
            //SetDefaultValue(entities, e => e.Description3, definition.Description3DefaultValue);
            SetDefaultValue(entities, e => e.AvailableSince, definition.AvailableSinceDefaultValue);
            SetDefaultValue(entities, e => e.AvailableTill, definition.AvailableTillDefaultValue);
            SetDefaultValue(entities, e => e.Lookup1Id, definition.Lookup1DefaultValue);
            SetDefaultValue(entities, e => e.Lookup2Id, definition.Lookup2DefaultValue);
            //SetDefaultValue(entities, e => e.Lookup3Id, definition.Lookup3DefaultValue);
            //SetDefaultValue(entities, e => e.Lookup4Id, definition.Lookup4DefaultValue);
            //SetDefaultValue(entities, e => e.Lookup5Id, definition.Lookup5DefaultValue);

            // Validate required stuff
            ValidateIfRequired(entities, e => e.Identifier, definition.IdentifierVisibility);
            ValidateIfRequired(entities, e => e.CurrencyId, definition.CurrencyVisibility);
            ValidateIfRequired(entities, e => e.MonetaryValue, definition.MonetaryValueVisibility);
            ValidateIfRequired(entities, e => e.CountUnitId, definition.CountUnitVisibility);
            ValidateIfRequired(entities, e => e.Count, definition.CountVisibility);
            ValidateIfRequired(entities, e => e.MassUnitId, definition.MassUnitVisibility);
            ValidateIfRequired(entities, e => e.Mass, definition.MassVisibility);
            ValidateIfRequired(entities, e => e.VolumeUnitId, definition.VolumeUnitVisibility);
            ValidateIfRequired(entities, e => e.Volume, definition.VolumeVisibility);
            ValidateIfRequired(entities, e => e.TimeUnitId, definition.TimeUnitVisibility);
            ValidateIfRequired(entities, e => e.Time, definition.TimeVisibility);
            ValidateIfRequired(entities, e => e.Description, definition.DescriptionVisibility);
            ValidateIfRequired(entities, e => e.Description2, definition.DescriptionVisibility);
            ValidateIfRequired(entities, e => e.Description3, definition.DescriptionVisibility);
            ValidateIfRequired(entities, e => e.AvailableSince, definition.AvailableSinceVisibility);
            ValidateIfRequired(entities, e => e.AvailableTill, definition.AvailableTillVisibility);
            ValidateIfRequired(entities, e => e.Lookup1Id, definition.Lookup1Visibility);
            ValidateIfRequired(entities, e => e.Lookup2Id, definition.Lookup2Visibility);
            //ValidateIfRequired(entities, e => e.Lookup3Id, definition.Lookup3Visibility);
            //ValidateIfRequired(entities, e => e.Lookup4Id, definition.Lookup4Visibility);
            //ValidateIfRequired(entities, e => e.Lookup5Id, definition.Lookup5Visibility);

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                // null Ids will cause an error when calling the SQL validation
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.Resources_Validate__Save(DefinitionId, entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #19
0
        protected override async Task SaveValidateAsync(List <ExchangeRateForSave> entities)
        {
            int index        = 0;
            var functionalId = _settingsCache.GetCurrentSettingsIfCached()?.Data?.FunctionalCurrencyId;

            var currencyDateHash = entities
                                   .GroupBy(e => new { e.CurrencyId, e.ValidAsOf })
                                   .Where(g => g.Count() > 1)
                                   .SelectMany(g => g)
                                   .ToHashSet();

            // Get the currencies that contribute to duplications, so that we can put their names in the error message
            // Get them outside the loop in a single DB query (for performance)
            var currencyIdArray = currencyDateHash.Select(e => e.CurrencyId).Distinct().ToArray();
            var currencies      = (await _repo.Currencies.FilterByIds(currencyIdArray).ToListAsync(cancellation: default)).ToDictionary(e => e.Id);

            foreach (var entity in entities)
            {
                // Currency cannot be functional
                if (entity.CurrencyId == functionalId)
                {
                    ModelState.AddModelError($"[{index}].{nameof(ExchangeRate.CurrencyId)}",
                                             _localizer["Error_TheCurrencyMustBeDifferentThanFunctional"]);
                }

                if (entity.ValidAsOf > DateTime.Today.AddDays(1))
                {
                    ModelState.AddModelError($"[{index}].{nameof(ExchangeRate.ValidAsOf)}",
                                             _localizer["Error_TheValidAsOfDateCannotBeInTheFuture"]);
                }

                // Amounts must be >= 1
                if (entity.AmountInCurrency <= 0m)
                {
                    ModelState.AddModelError($"[{index}].{nameof(ExchangeRate.AmountInCurrency)}",
                                             _localizer["Error_TheAmountInCurrencyMustBeGreaterThanZero"]);
                }

                // Amounts must be >= 1
                if (entity.AmountInFunctional <= 0m)
                {
                    ModelState.AddModelError($"[{index}].{nameof(ExchangeRate.AmountInFunctional)}",
                                             _localizer["Error_TheAmountInFunctionalMustBeGreaterThanZero"]);
                }

                // Currency and date must not be duplicated in the uploaded list
                if (currencyDateHash.Contains(entity))
                {
                    var currency     = currencies[entity.CurrencyId];
                    var currencyName = _repo.GetTenantInfo().Localize(currency.Name, currency.Name2, currency.Name3);

                    ModelState.AddModelError($"[{index}].{nameof(ExchangeRate.CurrencyId)}",
                                             _localizer["Error_TheCurrency0Date1AreDuplicated", currencyName, entity.ValidAsOf.Value.ToString("yyyy-MM-dd")]);
                }

                if (ModelState.HasReachedMaxErrors)
                {
                    // No need to keep going forever
                    return;
                }

                index++;
            }


            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                // null Ids will cause an error when calling the SQL validation
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.ExchangeRates_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #20
0
        protected override async Task SaveValidateAsync(List <DashboardDefinitionForSave> entities)
        {
            const int maxOffset = 1000;
            const int maxSize   = 16;

            var defs     = _defCache.GetCurrentDefinitionsIfCached().Data;
            var settings = _settingsCache.GetCurrentSettingsIfCached().Data;

            foreach (var(entity, index) in entities.Select((e, i) => (e, i)))
            {
                if (entity.ShowInMainMenu ?? false)
                {
                    if (string.IsNullOrWhiteSpace(entity.Title))
                    {
                        string path = $"[{index}].{nameof(entity.Title)}";
                        string msg  = _localizer["Error_TitleIsRequiredWhenShowInMainMenu"];

                        ModelState.AddModelError(path, msg);
                    }
                }

                var duplicateReportIds = entity.Widgets
                                         .GroupBy(e => e.ReportDefinitionId)
                                         .Where(g => g.Count() > 1)
                                         .Select(g => g.Key)
                                         .ToHashSet();

                foreach (var(widget, widgetIndex) in entity.Widgets.Select((e, i) => (e, i)))
                {
                    if (widget.OffsetX < 0)
                    {
                        string path = $"[{index}].{nameof(entity.Widgets)}[{widgetIndex}].{nameof(widget.OffsetX)}";
                        string msg  = _localizer["Error_TheField0CannotBeNegative", _localizer["DashboardDefinition_OffsetX"]];

                        ModelState.AddModelError(path, msg);
                    }

                    if (widget.OffsetX >= maxOffset)
                    {
                        string path = $"[{index}].{nameof(entity.Widgets)}[{widgetIndex}].{nameof(widget.OffsetX)}";
                        string msg  = _localizer["Error_Field0MaximumIs1", _localizer["DashboardDefinition_OffsetX"], maxOffset];

                        ModelState.AddModelError(path, msg);
                    }

                    if (widget.OffsetY < 0)
                    {
                        string path = $"[{index}].{nameof(entity.Widgets)}[{widgetIndex}].{nameof(widget.OffsetY)}";
                        string msg  = _localizer["Error_TheField0CannotBeNegative", _localizer["DashboardDefinition_OffsetY"]];

                        ModelState.AddModelError(path, msg);
                    }

                    if (widget.OffsetY >= maxOffset)
                    {
                        string path = $"[{index}].{nameof(entity.Widgets)}[{widgetIndex}].{nameof(widget.OffsetY)}";
                        string msg  = _localizer["Error_Field0MaximumIs1", _localizer["DashboardDefinition_OffsetY"], maxOffset];

                        ModelState.AddModelError(path, msg);
                    }

                    if (widget.Width < 0)
                    {
                        string path = $"[{index}].{nameof(entity.Widgets)}[{widgetIndex}].{nameof(widget.Width)}";
                        string msg  = _localizer["Error_TheField0CannotBeNegative", _localizer["DashboardDefinition_Width"]];

                        ModelState.AddModelError(path, msg);
                    }

                    if (widget.Width >= maxSize)
                    {
                        string path = $"[{index}].{nameof(entity.Widgets)}[{widgetIndex}].{nameof(widget.Width)}";
                        string msg  = _localizer["Error_Field0MaximumIs1", _localizer["DashboardDefinition_Width"], maxSize];

                        ModelState.AddModelError(path, msg);
                    }

                    if (widget.Height < 0)
                    {
                        string path = $"[{index}].{nameof(entity.Widgets)}[{widgetIndex}].{nameof(widget.Height)}";
                        string msg  = _localizer["Error_TheField0CannotBeNegative", _localizer["DashboardDefinition_Height"]];

                        ModelState.AddModelError(path, msg);
                    }

                    if (widget.Height >= maxSize)
                    {
                        string path = $"[{index}].{nameof(entity.Widgets)}[{widgetIndex}].{nameof(widget.Height)}";
                        string msg  = _localizer["Error_Field0MaximumIs1", _localizer["DashboardDefinition_Height"], maxSize];

                        ModelState.AddModelError(path, msg);
                    }

                    if (duplicateReportIds.Contains(widget.ReportDefinitionId))
                    {
                        defs.Reports.TryGetValue(widget.ReportDefinitionId.Value, out ReportDefinitionForClient reportDef);
                        string reportName = reportDef == null ? null : settings.Localize(reportDef.Title, reportDef.Title2, reportDef.Title3);
                        string path       = $"[{index}].{nameof(entity.Widgets)}[{widgetIndex}].{nameof(widget.ReportDefinitionId)}";
                        string msg        = _localizer["Error_The01IsDuplicated", _localizer["DashboardDefinition_ReportDefinition"], reportName ?? widget.ReportDefinitionId.ToString()];

                        ModelState.AddModelError(path, msg);
                    }
                }
            }

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                // null Ids will cause an error when calling the SQL validation
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.DashboardDefinitions_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #21
0
        protected override async Task SaveValidateAsync(List <DocumentForSave> docs)
        {
            // TODO: Add definition validation and defaults here

            // Find lines with duplicate Ids
            var duplicateLineIds = docs.SelectMany(doc => doc.Lines)                                               // All lines
                                   .Where(line => line.Id != 0).GroupBy(line => line.Id).Where(g => g.Count() > 1) // Duplicate Ids
                                   .SelectMany(g => g).ToDictionary(line => line, line => line.Id);                // to dictionary

            // Find entries with duplicate Ids
            var duplicateEntryIds = docs.SelectMany(doc => doc.Lines).SelectMany(line => line.Entries)
                                    .Where(entry => entry.Id != 0).GroupBy(entry => entry.Id).Where(g => g.Count() > 1)
                                    .SelectMany(g => g).ToDictionary(entry => entry, entry => entry.Id);

            var settings = _settingsCache.GetCurrentSettingsIfCached().Data;

            ///////// Document Validation
            int docIndex = 0;

            foreach (var doc in docs)
            {
                // Check that the date is not in the future
                if (doc.DocumentDate > DateTime.Today.AddDays(1))
                {
                    ModelState.AddModelError($"[{docIndex}].{nameof(doc.DocumentDate)}",
                                             _localizer["Error_DateCannotBeInTheFuture"]);
                }

                // Check that the date is not before archive date
                if (doc.DocumentDate <= settings.ArchiveDate)
                {
                    ModelState.AddModelError($"[{docIndex}].{nameof(doc.DocumentDate)}",
                                             _localizer["Error_DateCannotBeBeforeArchiveDate0", settings.ArchiveDate.ToString("yyyy-MM-dd")]);
                }

                ///////// Line Validation
                int lineIndex = 0;
                foreach (var line in doc.Lines)
                {
                    // Prevent duplicate line Ids
                    if (duplicateLineIds.ContainsKey(line))
                    {
                        // This error indicates a bug
                        var id = duplicateLineIds[line];
                        ModelState.AddModelError($"[{docIndex}].{nameof(doc.Lines)}[{lineIndex}].{nameof(line.Id)}",
                                                 _localizer["Error_TheEntityWithId0IsSpecifiedMoreThanOnce", id]);
                    }

                    ///////// Entry Validation
                    int entryIndex = 0;
                    foreach (var entry in line.Entries)
                    {
                        // Prevent duplicate entry Ids
                        if (duplicateEntryIds.ContainsKey(entry))
                        {
                            var id = duplicateEntryIds[entry];
                            ModelState.AddModelError($"[{docIndex}].{nameof(doc.Lines)}[{lineIndex}].{nameof(line.Entries)}[{entryIndex}].{nameof(entry.Id)}",
                                                     _localizer["Error_TheEntityWithId0IsSpecifiedMoreThanOnce", id]);
                        }

                        // If the currency is functional, value must equal monetary value
                        if (entry.CurrencyId == settings.FunctionalCurrencyId && entry.Value != entry.MonetaryValue)
                        {
                            var currencyDesc = _tenantInfoAccessor.GetCurrentInfo()
                                               .Localize(settings.FunctionalCurrencyDescription,
                                                         settings.FunctionalCurrencyDescription2,
                                                         settings.FunctionalCurrencyDescription3);

                            // TODO: Use the proper field name from definition, instead of "Amount"
                            ModelState.AddModelError($"[{docIndex}].{nameof(doc.Lines)}[{lineIndex}].{nameof(line.Entries)}[{entryIndex}].{nameof(entry.MonetaryValue)}",
                                                     _localizer["TheAmount0DoesNotMatchTheValue1EvenThoughBothIn2", entry.MonetaryValue ?? 0, entry.Value ?? 0, currencyDesc]);
                        }

                        entryIndex++;
                    }

                    lineIndex++;
                }

                ///////// Attachment Validation
                int attIndex = 0;
                foreach (var att in doc.Attachments)
                {
                    if (att.Id != 0 && att.File != null)
                    {
                        ModelState.AddModelError($"[{docIndex}].{nameof(doc.Attachments)}[{attIndex}]",
                                                 _localizer["Error_OnlyNewAttachmentsCanIncludeFileBytes"]);
                    }

                    if (att.Id == 0 && att.File == null)
                    {
                        ModelState.AddModelError($"[{docIndex}].{nameof(doc.Attachments)}[{attIndex}]",
                                                 _localizer["Error_NewAttachmentsMustIncludeFileBytes"]);
                    }

                    attIndex++;
                }

                docIndex++;
            }

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.Documents_Validate__Save(DefinitionId, docs, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #22
0
        protected override async Task SaveValidateAsync(List <RoleForSave> entities)
        {
            // Hash the indices for performance
            var indices = entities.ToIndexDictionary();

            // Check that line ids are unique
            entities.ForEach(e => { if (e.Permissions == null)
                                    {
                                        e.Permissions = new List <PermissionForSave>();
                                    }
                             });
            var duplicatePermissionId = entities.SelectMany(e => e.Permissions)                             // All lines
                                        .Where(e => e.Id != 0).GroupBy(e => e.Id).Where(g => g.Count() > 1) // Duplicate Ids
                                        .SelectMany(g => g).ToDictionary(e => e, e => e.Id);                // to dictionary

            // Check that line ids are unique
            entities.ForEach(e => { if (e.Members == null)
                                    {
                                        e.Members = new List <RoleMembershipForSave>();
                                    }
                             });
            var duplicateMembershipId = entities.SelectMany(e => e.Members)                                 // All lines
                                        .Where(e => e.Id != 0).GroupBy(e => e.Id).Where(g => g.Count() > 1) // Duplicate Ids
                                        .SelectMany(g => g).ToDictionary(e => e, e => e.Id);                // to dictionary

            foreach (var entity in entities)
            {
                var permissionIndices = entity.Permissions.ToIndexDictionary();
                foreach (var line in entity.Permissions)
                {
                    if (duplicatePermissionId.ContainsKey(line))
                    {
                        // This error indicates a bug
                        var index     = indices[entity];
                        var lineIndex = permissionIndices[line];
                        var id        = duplicatePermissionId[line];
                        ModelState.AddModelError($"[{index}].{nameof(entity.Permissions)}[{lineIndex}].{nameof(entity.Id)}",
                                                 _localizer["Error_TheEntityWithId0IsSpecifiedMoreThanOnce", id]);
                    }
                }

                var membersIndices = entity.Members.ToIndexDictionary();
                foreach (var line in entity.Members)
                {
                    if (duplicateMembershipId.ContainsKey(line))
                    {
                        // This error indicates a bug
                        var index     = indices[entity];
                        var lineIndex = membersIndices[line];
                        var id        = duplicateMembershipId[line];
                        ModelState.AddModelError($"[{index}].{nameof(entity.Members)}[{lineIndex}].{nameof(entity.Id)}",
                                                 _localizer["Error_TheEntityWithId0IsSpecifiedMoreThanOnce", id]);
                    }
                }
            }

            // TODO Validate Criteria

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.Roles_Validate__Save(entities, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);
        }
Beispiel #23
0
        protected override async Task SaveValidate(GeneralSettingsForSave entity)
        {
            // Attribute Validation
            var meta = _metadataPrvider.GetMetadata(_tenantIdAccessor.GetTenantId(), typeof(GeneralSettingsForSave));

            ValidateEntity(entity, meta);

            // C# validation
            if (!string.IsNullOrWhiteSpace(entity.SecondaryLanguageId) || !string.IsNullOrWhiteSpace(entity.TernaryLanguageId))
            {
                if (string.IsNullOrWhiteSpace(entity.PrimaryLanguageSymbol))
                {
                    ModelState.AddModelError(nameof(entity.PrimaryLanguageSymbol),
                                             _localizer[Constants.Error_Field0IsRequired, _localizer["Settings_PrimaryLanguageSymbol"]]);
                }
            }

            if (string.IsNullOrWhiteSpace(entity.SecondaryLanguageId))
            {
                entity.SecondaryLanguageSymbol = null;
            }
            else
            {
                if (string.IsNullOrWhiteSpace(entity.SecondaryLanguageSymbol))
                {
                    ModelState.AddModelError(nameof(entity.SecondaryLanguageSymbol),
                                             _localizer[Constants.Error_Field0IsRequired, _localizer["Settings_SecondaryLanguageSymbol"]]);
                }

                if (entity.SecondaryLanguageId == entity.PrimaryLanguageId)
                {
                    ModelState.AddModelError(nameof(entity.SecondaryLanguageId),
                                             _localizer["Error_SecondaryLanguageCannotBeTheSameAsPrimaryLanguage"]);
                }
            }

            if (string.IsNullOrWhiteSpace(entity.TernaryLanguageId))
            {
                entity.TernaryLanguageSymbol = null;
            }
            else
            {
                if (string.IsNullOrWhiteSpace(entity.TernaryLanguageSymbol))
                {
                    ModelState.AddModelError(nameof(entity.TernaryLanguageSymbol),
                                             _localizer[Constants.Error_Field0IsRequired, _localizer["Settings_TernaryLanguageSymbol"]]);
                }

                if (entity.TernaryLanguageId == entity.PrimaryLanguageId)
                {
                    ModelState.AddModelError(nameof(entity.TernaryLanguageId),
                                             _localizer["Error_TernaryLanguageCannotBeTheSameAsPrimaryLanguage"]);
                }

                if (entity.TernaryLanguageId == entity.SecondaryLanguageId)
                {
                    ModelState.AddModelError(nameof(entity.TernaryLanguageId),
                                             _localizer["Error_TernaryLanguageCannotBeTheSameAsSecondaryLanguage"]);
                }
            }

            if (!string.IsNullOrWhiteSpace(entity.SecondaryCalendar))
            {
                if (entity.PrimaryCalendar == entity.SecondaryCalendar)
                {
                    ModelState.AddModelError(nameof(entity.SecondaryCalendar),
                                             _localizer["Error_SecondaryCalendarCannotBeTheSameAsPrimaryCalendar"]);
                }
            }

            // Make sure the color is a valid HTML color
            // Credit: https://bit.ly/2ToV6x4
            if (!string.IsNullOrWhiteSpace(entity.BrandColor) && !Regex.IsMatch(entity.BrandColor, "^#(?:[0-9a-fA-F]{3}){1,2}$"))
            {
                ModelState.AddModelError(nameof(entity.BrandColor),
                                         _localizer["Error_TheField0MustBeAValidColorFormat", _localizer["Settings_BrandColor"]]);
            }

            if (ModelState.HasReachedMaxErrors)
            {
                return;
            }

            // SQL validation
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var sqlErrors           = await _repo.GeneralSettings_Validate__Save(entity, top : remainingErrorCount);

            // Add errors to model state
            ModelState.AddLocalizedErrors(sqlErrors, _localizer);

            return;
        }