Actions Library - Quality Document Manager
Overview
The QDM actions library contains all the server handlers that manage the lifecycle of quality documents. These handlers are triggered by user actions in the web interface.
Covered functionalities
This library enables the following business operations:
-
Document management
- Create a new document with automatic reference calculation
- Retrieve and apply document type configuration
- Manage document versions and archiving
- Search and display parent documents
-
Training management
- Automatically create a training session for a document
- Verify that training is published before validation
- Delete obsolete training
-
Binder management (document collections)
- Add or remove documents from a binder
- Reorganize document order in a binder
- Generate consolidated PDF of all binder documents
-
Distribution management
- Send a document to a group of recipients
- Track acknowledgments of receipt
- Remind users who haven't confirmed reading
- Generate distribution vouchers
-
Export and printing
- Export attachments to ZIP archive
- Compare two document versions as PDF
- Generate Excel or PDF exports of document lists
Operating principle
Each user action (document validation, training creation, distribution sending) triggers a server handler. These handlers are organized in .ashx files (handlers) that execute the necessary operations and return the result to the user interface.
Actions structure
File locations
Action files are organized by module:
2.Avancé/Web/Custom/
├── QDM/
│ ├── Document/
│ │ └── DocumentManager.ashx # Main document manager
│ ├── Classeur/
│ │ └── BinderHandler.ashx # Binder management
│ ├── TableauxDeDetails/
│ │ └── TabManager.ashx # Detail tables management
│ └── QDMLSHandler.ashx # Document collection manager
├── EXTEND/
│ ├── ModuleBureautique/Office/
│ │ ├── Actions.ashx # Office module actions
│ │ ├── Auth.ashx # Office authentication
│ │ ├── Handler.ashx # Main Office handler
│ │ ├── OfficeTemplate.ashx # Office template management
│ │ └── PluginHandler.ashx # Office plugin handler
│ ├── ViewLink/
│ │ └── Actions.ashx # View actions
│ ├── Survey/
│ │ └── Handlers/
│ │ └── SurveyHandler.ashx # Survey management
│ ├── CompareFile/
│ │ └── PDFExport.ashx # PDF comparison export
│ ├── ExportPJs/
│ │ └── ZipHandler.ashx # Attachment ZIP export
│ └── Help/
│ └── OnlineHelp.ashx # Online help
└── Modules/Distribution/
└── DistributionActions.ashx # Distribution actions
Main QDM actions
DocumentManager.ashx
Path: Custom/QDM/Document/DocumentManager.ashx
Business functionalities: This handler centralizes all quality document lifecycle operations.
Covered business needs
1. Automatic configuration by document type
Need: Each document type (Procedure, Instruction, Form) has specific rules (classification level, validity duration, validation workflow).
Solution: The system automatically retrieves these rules and applies them to the document.
2. Automatic document referencing
Need: Each document must have a unique reference calculated according to business rules (prefix by type, sequential numbering, etc.).
Solution: The system automatically calculates the reference during creation.
3. Document-training link management
Need: Certain critical documents require mandatory training before implementation.
Solution: The system automatically creates the training session and verifies it's published before document validation.
4. Obsolete document archiving
Need: Maintain complete history of documents and their versions for audits.
Solution: The system archives the document and all its versions while preserving traceability.
Available actions
| Action | Parameters | Description | Return |
|---|---|---|---|
GetConfig | type (string) | Retrieves document type configuration | JSON: {isExist, NIVEAU} |
GetTraining | RefDoc (string) | Retrieves training associated with document | JSON: {isExist, isPublished, reference} |
CreateTraining | IdDoc, RefDoc | Automatically creates a training session | JSON: {success, trainingId} |
DeleteTraining | RefDoc | Deletes associated training | JSON: {success} |
GetDocumentParent | IdDoc | Retrieves parent document | JSON: Parent document |
CalculateReference | IdType, Niveau | Calculates reference for new document | JSON: {reference} |
GetPreviousVersion | IdDoc | Retrieves previous document version | JSON: Previous version |
ArchiveDocument | IdDoc | Archives document and its versions | JSON: {success} |
Usage examples
Example 1: Automatic application of document type configuration
Business scenario
A quality manager creates a new "Procedure" type document. The system must automatically apply the rules defined for this type (classification level "Confidential", validity duration 2 years, etc.).
// When the user selects "Procedure" type from the dropdown
var type = APSGetFieldValueByName("ApsType");
// System queries the database to retrieve rules for this type
var config = JSON.parse(
HandlerRequest("Custom/QDM/Document/DocumentManager", "GetConfig",
"&type=" + encodeURI(type))
);
// If rules exist for this type, they are automatically applied
if (!config.isError && config.data.isExist) {
APSSetFieldValueByName("Niveau", config.data.NIVEAU);
console.log("Configuration loaded for type: " + type);
}
Business benefit: The quality manager doesn't need to memorize all the rules. The system applies them automatically, ensuring compliance and avoiding input errors.
Example 2: Verify training existence
// Verify training existence
var ref = APSGetFieldValueByName("reference");
var training = JSON.parse(
HandlerRequest("Custom/QDM/Document/DocumentManager", "GetTraining",
"&RefDoc=" + ref)
);
if (!training.isError && training.data.isExist) {
console.log("Training found: " + training.data.reference);
}
Standard response format
{
"isError": false,
"message": "",
"data": {
// Action-specific data
}
}
BinderHandler.ashx
Path: Custom/QDM/Classeur/BinderHandler.ashx
Objective: Management of document binders (document collections).
Available actions
| Action | Parameters | Description | Return |
|---|---|---|---|
GetBinderDocuments | IdBinder | Retrieves list of documents in a binder | JSON: Document list |
AddDocumentToBinder | IdBinder, IdDoc | Adds document to binder | JSON: {success} |
RemoveDocumentFromBinder | IdBinder, IdDoc | Removes document from binder | JSON: {success} |
ReorderDocuments | IdBinder, order[] | Reorders binder documents | JSON: {success} |
GenerateBinderPDF | IdBinder | Generates consolidated binder PDF | JSON: {success, fileUrl} |
TabManager.ashx
Path: Custom/QDM/TableauxDeDetails/TabManager.ashx
Objective: Management of detail tables in forms.
Available actions
| Action | Parameters | Description | Return |
|---|---|---|---|
GetTableData | IdDoc, TableName | Retrieves table data | JSON: Table data |
AddRow | IdDoc, TableName, data | Adds row to table | JSON: {success, rowId} |
UpdateRow | IdDoc, TableName, rowId, data | Updates a row | JSON: {success} |
DeleteRow | IdDoc, TableName, rowId | Deletes a row | JSON: {success} |
EXTEND actions
ModuleBureautique - Actions.ashx
Path: Custom/EXTEND/ModuleBureautique/Office/Actions.ashx
Objective: Actions related to Office document editing in the browser.
Available actions
| Action | Parameters | Description | Return |
|---|---|---|---|
OpenDocument | IdDoc, FileName | Opens Office document for editing | JSON: {url, token} |
SaveDocument | IdDoc, FileName, content | Saves edited document | JSON: {success} |
CheckOut | IdDoc, FileName | Locks document for editing | JSON: {success, lockedBy} |
CheckIn | IdDoc, FileName | Unlocks document after editing | JSON: {success} |
CancelCheckOut | IdDoc, FileName | Cancels lock without saving | JSON: {success} |
GetTemplates | formularId | Retrieves available Office templates | JSON: Template list |
GenerateFromTemplate | IdDoc, templateId | Generates document from template | JSON: {success, fileUrl} |
Usage example
// Open Word document for editing
var response = JSON.parse(
HandlerRequest("Custom/EXTEND/ModuleBureautique/Office/Actions",
"OpenDocument",
"&IdDoc=" + IdDoc + "&FileName=" + encodeURI(fileName))
);
if (!response.isError) {
window.open(response.data.url, '_blank');
}
Survey - SurveyHandler.ashx
Path: Custom/EXTEND/Survey/Handlers/SurveyHandler.ashx
Objective: Management of surveys and evaluations.
Available actions
| Action | Parameters | Description | Return |
|---|---|---|---|
GetQuestions | IdSurvey | Retrieves survey questions | JSON: Question list |
SaveResponse | IdSurvey, IdUser, responses | Saves user responses | JSON: {success, score} |
GetResults | IdSurvey, IdUser | Retrieves user results | JSON: Detailed results |
CalculateScore | IdSurvey, responses | Calculates survey score | JSON: {score, isPassed} |
GenerateCertificate | IdSurvey, IdUser | Generates success certificate | JSON: {success, certificateUrl} |
ViewLink - Actions.ashx
Path: Custom/EXTEND/ViewLink/Actions.ashx
Objective: Custom actions on views (document lists).
Available actions
| Action | Parameters | Description | Return |
|---|---|---|---|
BulkValidate | IdDocs[] | Validates multiple documents in bulk | JSON: {success, count} |
BulkArchive | IdDocs[] | Archives multiple documents in bulk | JSON: {success, count} |
BulkDistribute | IdDocs[], recipients | Distributes multiple documents in bulk | JSON: {success, count} |
ExportToExcel | viewId, filters | Excel export of view with filters | Excel file |
ExportToPDF | viewId, filters | PDF export of view with filters | PDF file |
Distribution actions
DistributionActions.ashx
Path: Modules/Distribution/DistributionActions.ashx
Objective: Management of document distribution actions.
Available actions
| Action | Parameters | Description | Return |
|---|---|---|---|
SendDistribution | IdDoc, recipients[], type | Sends a distribution | JSON: {success, distributionId} |
GetDistributionStatus | distributionId | Distribution status | JSON: Detailed status |
GetAcknowledgements | IdDoc, distributionId | List of distribution acknowledgments | JSON: AR list |
RemindUsers | distributionId, userIds[] | Reminds users for acknowledgment | JSON: {success, remindedCount} |
CancelDistribution | distributionId | Cancels a distribution | JSON: {success} |
GenerateVoucher | distributionId | Generates paper distribution voucher |
Utility actions
PDFExport.ashx
Path: Custom/EXTEND/CompareFile/PDFExport.ashx
Objective: PDF document export and comparison.
Available actions
| Action | Parameters | Description | Return |
|---|---|---|---|
ComparePDF | file1, file2 | Compares two PDF versions | PDF with differences |
ExportWithWatermark | IdDoc, watermark | Exports PDF with watermark | |
MergePDFs | files[] | Merges multiple PDFs | Merged PDF |
ZipHandler.ashx
Path: Custom/EXTEND/ExportPJs/ZipHandler.ashx
Objective: Export attachments to ZIP archive.
Available actions
| Action | Parameters | Description | Return |
|---|---|---|---|
ExportAttachments | IdDoc | Exports all attachments of a document | ZIP file |
ExportMultipleDocuments | IdDocs[] | Exports multiple documents and their attachments | ZIP file |
Security and best practices
Authentication
Business challenge: Ensure only authorized users can perform actions on quality documents (creation, modification, validation, archiving). This meets audit and traceability requirements of quality standards (ISO 9001, etc.).
Control implemented: All handlers verify user identity before executing any operation.
// Systematic authentication control
if (!Context.User.Identity.IsAuthenticated)
{
// Access denied if user is not identified
return JsonResponse.Error("User not authenticated");
}
Use cases:
- User tries to validate document without being logged in → Action refused
- Logged-in user initiates distribution → Action authorized and logged with their identity
- Session expires during input → User must reconnect before saving
Parameter validation
Why it's important: Data sent from the browser can be incorrect or even malicious. Always verify it's valid before use, like checking a phone number contains only digits.
Received parameters must always be validated before use:
// Document ID validation
int idDoc;
// Try to convert parameter to integer
if (!int.TryParse(Context.Request["IdDoc"], out idDoc) || idDoc <= 0)
{
// If conversion fails or number is negative, refuse
return JsonResponse.Error("Invalid document ID");
}
What is verified:
- Does the parameter exist?
- Is it the correct type? (text, number, date, etc.)
- Is it within acceptable value range?
- Does it contain no dangerous characters?
Error handling
Why it's important: When something doesn't go as expected, clearly inform the user interface what went wrong. This allows displaying an understandable error message to the user instead of simply "crashing".
Standardized error response format used by all handlers:
{
"isError": true,
"message": "Error description",
"data": null
}
Response structure:
isError: a boolean (true/false) indicating if an error occurredmessage: explanatory text of the error (e.g., "Document not found", "Insufficient rights")data: requested data (null in case of error)
Concrete example: If a user tries to open a non-existent document, the handler returns {"isError": true, "message": "Document #12345 does not exist", "data": null} and the interface can display a clear message to the user.
Logs
Why record logs?
Logs are like an application logbook. They allow tracing who did what and when, which is essential for debugging, security, and auditing. If a problem occurs, logs help trace the sequence of events to understand what happened.
Important actions must be recorded in logs:
// Record important information
Logger.Info($"Document {idDoc} validated by user {Context.User.Identity.Name}");
When to log?
- When a document is created, modified, validated, archived
- When a user performs an important action
- When an error occurs (with error details)
- When a sensitive operation is performed (rights change, deletion, etc.)
Different log levels:
Logger.Info(): Normal operation (e.g., "Document created")Logger.Warning(): Abnormal but non-blocking situation (e.g., "Missing training")Logger.Error(): Error preventing operation (e.g., "Cannot access database")
Creating a new action
Basic handler structure
File: CustomAction.ashx
<%@ WebHandler Language="C#" Class="CustomAction" %>
using System;
using System.Web;
using Avanteam.Kernel;
using Avanteam.Documents;
public class CustomAction : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
try
{
// Authentication verification
if (!context.User.Identity.IsAuthenticated)
{
context.Response.Write(JsonResponse.Error("Not authenticated"));
return;
}
// Retrieve requested action
string action = context.Request["action"];
switch (action)
{
case "GetData":
GetData(context);
break;
case "SaveData":
SaveData(context);
break;
default:
context.Response.Write(JsonResponse.Error("Unknown action"));
break;
}
}
catch (Exception ex)
{
Logger.Error($"CustomAction error: {ex.Message}", ex);
context.Response.Write(JsonResponse.Error(ex.Message));
}
}
private void GetData(HttpContext context)
{
// GetData action implementation
int idDoc = int.Parse(context.Request["IdDoc"]);
// Data retrieval
var data = DocumentHelper.GetDocument(idDoc);
// JSON return
context.Response.Write(JsonResponse.Success(data));
}
private void SaveData(HttpContext context)
{
// SaveData action implementation
int idDoc = int.Parse(context.Request["IdDoc"]);
string value = context.Request["value"];
// Save
DocumentHelper.UpdateField(idDoc, "FieldName", value);
// JSON return
context.Response.Write(JsonResponse.Success(new { saved = true }));
}
public bool IsReusable
{
get { return false; }
}
}
JavaScript call
// GetData action call
var result = JSON.parse(
HandlerRequest("Custom/Module/CustomAction", "GetData",
"&IdDoc=" + idDoc)
);
if (!result.isError) {
console.log("Data retrieved", result.data);
} else {
console.error("Error", result.message);
}
Utility class: JsonResponse
Location: App_Code/JsonResponse.cs
Helper class to standardize JSON responses.
Available methods
public static class JsonResponse
{
// Success response
public static string Success(object data)
{
return Serialize(new { isError = false, message = "", data });
}
// Error response
public static string Error(string message)
{
return Serialize(new { isError = true, message, data = (object)null });
}
// Custom response
public static string Custom(bool isError, string message, object data)
{
return Serialize(new { isError, message, data });
}
private static string Serialize(object obj)
{
return Newtonsoft.Json.JsonConvert.SerializeObject(obj);
}
}
Usage
// Success
context.Response.Write(JsonResponse.Success(new {
id = 123,
name = "Document"
}));
// Error
context.Response.Write(JsonResponse.Error("Document not found"));
// Custom
context.Response.Write(JsonResponse.Custom(false, "Warning", warningData));
Action debugging
Developer console
Handler calls can be viewed in the console:
console.log("Handler call:", "CustomAction", "GetData", params);
var result = HandlerRequest("Custom/Module/CustomAction", "GetData", params);
console.log("Result:", result);
Server logs
Server logs are located in:
App_Data/Logs/for application logs- IIS logs for HTTP requests
Browser network tools
Use the "Network" tab of developer tools to see:
- Sent parameters
- JSON response
- Processing time
- Any HTTP errors
Performance and optimization
Cache
Use cache for frequently accessed data:
string cacheKey = $"Config_{type}";
var config = HttpContext.Current.Cache[cacheKey];
if (config == null)
{
config = LoadConfigFromDatabase(type);
HttpContext.Current.Cache.Insert(cacheKey, config, null,
DateTime.Now.AddMinutes(30), Cache.NoSlidingExpiration);
}
return config;
Asynchronous requests
For long processes, prefer asynchronous requests:
// Start long process
var jobId = StartLongProcess();
// Status polling
var interval = setInterval(function() {
var status = CheckJobStatus(jobId);
if (status.isCompleted) {
clearInterval(interval);
console.log("Process completed");
}
}, 1000);
Navigation: