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);
        }
Example #2
0
        public ActionResult <DataWithVersion <DefinitionsForClient> > DefinitionsForClient()
        {
            try
            {
                // Simply retrieves the cached definitions, which were refreshed by ApiController
                var result = _definitionsCache.GetCurrentDefinitionsIfCached();
                if (result == null)
                {
                    throw new InvalidOperationException("The definitions were missing from the cache");
                }

                return(Ok(result));
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error: {ex.Message} {ex.StackTrace}");
                return(BadRequest(ex.Message));
            }
        }
Example #3
0
 private AgentDefinitionForClient Definition() => _definitionsCache.GetCurrentDefinitionsIfCached()?.Data?.Agents?
 .GetValueOrDefault(DefinitionId) ?? throw new InvalidOperationException($"Definition for '{DefinitionId}' was missing from the cache");
Example #4
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);
        }
Example #5
0
 private LookupDefinitionForClient Definition() => _definitionsCache.GetCurrentDefinitionsIfCached()?.Data?.Lookups?
 .GetValueOrDefault(DefinitionId.Value) ?? throw new InvalidOperationException($"Lookup Definition with Id = {DefinitionId} is missing from the cache");
        protected override async Task SaveValidateAsync(List <LineDefinitionForSave> entities)
        {
            var defs     = _defCache.GetCurrentDefinitionsIfCached().Data;
            var settings = _settingsCache.GetCurrentSettingsIfCached().Data;

            // C# validation
            int lineDefinitionIndex = 0;

            entities.ForEach(lineDefinition =>
            {
                // Columns
                int columnIndex = 0;
                lineDefinition.Columns.ForEach(column =>
                {
                    int index = column.EntryIndex.Value;
                    if (index < 0)
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Columns)}[{columnIndex}].{nameof(LineDefinitionColumn.EntryIndex)}";
                        string msg  = _localizer["Error_IndexMustBeGreaterOrEqualZero"];

                        ModelState.AddModelError(path, msg);
                    }
                    else if (index > (lineDefinition.Entries?.Count ?? 0))
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Columns)}[{columnIndex}].{nameof(LineDefinitionColumn.EntryIndex)}";
                        string msg  = _localizer["Error_NoEntryCorrespondsToIndex0", index];

                        ModelState.AddModelError(path, msg);
                    }

                    // Required state should always be <= ReadOnlyState
                    if (column.RequiredState > column.ReadOnlyState)
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Columns)}[{columnIndex}].{nameof(LineDefinitionColumn.ReadOnlyState)}";
                        string msg  = _localizer["Error_ReadOnlyStateCannotBeBeforeRequiredState"];;

                        ModelState.AddModelError(path, msg);
                    }

                    columnIndex++;
                });

                // GenerateScript
                if (!string.IsNullOrWhiteSpace(lineDefinition.GenerateScript))
                {
                    // If auto-generate script is specified, DefaultsToForm must be false
                    if (lineDefinition.ViewDefaultsToForm.Value)
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.GenerateScript)}";
                        string msg  = _localizer["Error_CannotHaveGenerateScriptWithDefaultsToForm"];

                        ModelState.AddModelError(path, msg);
                    }
                }

                // Generate parameters
                int paramIndex = 0;
                lineDefinition.GenerateParameters.ForEach(parameter =>
                {
                    var errors = ControllerUtilities.ValidateControlOptions(parameter.Control, parameter.ControlOptions, _localizer, settings, defs);
                    foreach (var msg in errors)
                    {
                        ModelState.AddModelError($"[{lineDefinitionIndex}].{nameof(lineDefinition.GenerateParameters)}[{paramIndex}].{nameof(parameter.ControlOptions)}", msg);
                    }

                    paramIndex++;
                });

                // Workflows
                int workflowIndex = 0;
                lineDefinition.Workflows.ForEach(workflow =>
                {
                    int signatureIndex = 0;
                    workflow.Signatures?.ForEach(signature =>
                    {
                        // Role is required
                        if (signature.RuleType == RuleTypes.ByRole && signature.RoleId == null)
                        {
                            string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.RoleId)}";
                            string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["WorkflowSignature_Role"]];

                            ModelState.AddModelError(path, msg);
                        }

                        // User is required
                        if (signature.RuleType == RuleTypes.ByUser && signature.UserId == null)
                        {
                            string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.UserId)}";
                            string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["WorkflowSignature_User"]];

                            ModelState.AddModelError(path, msg);
                        }

                        if (signature.RuleType == RuleTypes.ByCustodian && signature.RuleTypeEntryIndex == null)
                        {
                            // Entry index is required
                            if (signature.RuleTypeEntryIndex == null)
                            {
                                string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.RuleTypeEntryIndex)}";
                                string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["WorkflowSignature_RuleTypeEntryIndex"]];

                                ModelState.AddModelError(path, msg);
                            }
                            else
                            {
                                // Make sure Entry index is not out of bounds
                                int index = signature.RuleTypeEntryIndex.Value;
                                if (index < 0)
                                {
                                    string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.RuleTypeEntryIndex)}";
                                    string msg  = _localizer["Error_IndexMustBeGreaterOrEqualZero"];

                                    ModelState.AddModelError(path, msg);
                                }
                                else if (index > (lineDefinition.Entries?.Count ?? 0))
                                {
                                    string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.RuleTypeEntryIndex)}";
                                    string msg  = _localizer["Error_NoEntryCorrespondsToIndex0", index];

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

                        if (signature.PredicateType == PredicateTypes.ValueGreaterOrEqual)
                        {
                            // Value is required
                            if (signature.Value == null)
                            {
                                string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.Value)}";
                                string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["WorkflowSignature_Value"]];

                                ModelState.AddModelError(path, msg);
                            }

                            // Entry Index is required
                            if (signature.PredicateTypeEntryIndex == null)
                            {
                                string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.PredicateTypeEntryIndex)}";
                                string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["WorkflowSignature_PredicateTypeEntryIndex"]];

                                ModelState.AddModelError(path, msg);
                            }
                            else
                            {
                                // Make sure Entry index is not out of bounds
                                int index = signature.PredicateTypeEntryIndex.Value;
                                if (index < 0)
                                {
                                    string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.PredicateTypeEntryIndex)}";
                                    string msg  = _localizer["Error_IndexMustBeGreaterOrEqualZero"];

                                    ModelState.AddModelError(path, msg);
                                }
                                else if (index > (lineDefinition.Entries?.Count ?? 0))
                                {
                                    string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.PredicateTypeEntryIndex)}";
                                    string msg  = _localizer["Error_NoEntryCorrespondsToIndex0", index];

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

                        signatureIndex++;
                    });

                    workflowIndex++;
                });

                // Barcode
                if (lineDefinition.BarcodeColumnIndex != null)
                {
                    // If barcode is enabled, BarcodeProperty must be specified
                    if (string.IsNullOrWhiteSpace(lineDefinition.BarcodeProperty))
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeProperty)}";
                        string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["LineDefinition_BarcodeProperty"]];

                        ModelState.AddModelError(path, msg);
                    }

                    // If barcode is enabled, BarcodeExistingItemHandling must be specified
                    if (string.IsNullOrWhiteSpace(lineDefinition.BarcodeExistingItemHandling))
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeExistingItemHandling)}";
                        string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["LineDefinition_BarcodeExistingItemHandling"]];

                        ModelState.AddModelError(path, msg);
                    }

                    // If barcode is enabled, DefaultsToForm must be false
                    if (lineDefinition.ViewDefaultsToForm.Value)
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                        string msg  = _localizer["Error_CannotHaveBarcodeWithDefaultsToForm"];

                        ModelState.AddModelError(path, msg);
                    }

                    // BarcodeColumnIndex must be within Columns range
                    var colIndex = lineDefinition.BarcodeColumnIndex.Value;
                    if (colIndex >= lineDefinition.Columns.Count)
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                        string msg  = _localizer["Error_BarcodeColumnIndexOutOfRange"];

                        ModelState.AddModelError(path, msg);
                    }
                    else
                    {
                        // Barcode Column cannot inherit from headers
                        var colDef = lineDefinition.Columns[colIndex];
                        if (colDef.InheritsFromHeader > 0)
                        {
                            string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                            string msg  = _localizer["Error_BarcodeColumnCannotInheritFromHeaders"];

                            ModelState.AddModelError(path, msg);
                        }

                        // Barcode Column must be visible from DRAFT
                        if (colDef.VisibleState > 0)
                        {
                            string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                            string msg  = _localizer["Error_BarcodeColumnMustBeVisibleFromDraft"];

                            ModelState.AddModelError(path, msg);
                        }

                        // Barcode Column must be editable from DRAFT
                        if (colDef.ReadOnlyState == 0)
                        {
                            string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                            string msg  = _localizer["Error_BarcodeColumnCannotBeReadOnlyFromDraft"];

                            ModelState.AddModelError(path, msg);
                        }

                        Dictionary <string, Type> acceptableColumnNames = new Dictionary <string, Type> {
                            { "CustodianId", typeof(Relation) },
                            { "CustodyId", typeof(Custody) },
                            { "ParticipantId", typeof(Relation) },
                            { "ResourceId", typeof(Resource) }
                        };

                        if (string.IsNullOrWhiteSpace(colDef.ColumnName))
                        {
                            // Error handled earlier
                        }
                        else if (!acceptableColumnNames.TryGetValue(colDef.ColumnName, out Type colType))
                        {
                            // Barcode Column must have on of the supported column names
                            string path  = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                            string names = string.Join(", ", acceptableColumnNames.Keys.Select(e => _localizer["Entry_" + e[0..^ 2]]));
Example #7
0
        public async Task <(List <DocumentDefinition>, Extras)> UpdateState(List <int> ids, UpdateStateArguments args)
        {
            // Make sure
            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();

            // Check user permissions
            var action       = "State";
            var actionFilter = await UserPermissionsFilter(action, cancellation : default);

            ids = await CheckActionPermissionsBefore(actionFilter, ids);

            // C# Validation
            if (string.IsNullOrWhiteSpace(args.State))
            {
                throw new BadRequestException(_localizer[Constants.Error_Field0IsRequired, _localizer["State"]]);
            }

            if (!DefStates.All.Contains(args.State))
            {
                string validStates = string.Join(", ", DefStates.All);
                throw new BadRequestException($"'{args.State}' is not a valid definition state, valid states are: {validStates}");
            }

            // Transaction
            using var trx = ControllerUtilities.CreateTransaction();

            // Validate
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var errors = await _repo.DocumentDefinitions_Validate__UpdateState(ids, args.State, top : remainingErrorCount);

            ControllerUtilities.AddLocalizedErrors(ModelState, errors, _localizer);
            ModelState.ThrowIfInvalid();

            // Execute
            await _repo.DocumentDefinitions__UpdateState(ids, args.State);

            // Prepare response
            List <DocumentDefinition> data = null;
            Extras extras = null;

            if (args.ReturnEntities ?? false)
            {
                (data, extras) = await GetByIds(ids, args, action, cancellation : default);
            }

            // Check user permissions again
            await CheckActionPermissionsAfter(actionFilter, ids, data);

            // Commit and return
            trx.Complete();
            return(data, extras);
        }