FrontEnd technical aspects
Languages and Frameworks:
- JavaScript: The primary scripting language used for client-side logic and interactivity.
- HTML & CSS: For structuring and styling the web application’s front-end.
Key Libraries and Tools:
- jQuery: A fast, small, and feature-rich JavaScript library. It simplifies HTML document traversal and manipulation, event handling, and Ajax interactions.
- Select2: Utilized for enhancing standard select boxes. It offers searchable select fields and is extensively used for dropdowns in the application.
- GraphQL: Used for data queries and mutations, indicating a flexible and efficient approach to fetching and updating data from an API.
Observations :
- Modular JavaScript Approach: The application’s functionality is divided into separate JS files (e.g.,
publicapi_fetch.js
,customer.js
,products.js
,salesquote.js
), each handling a specific aspect of the application. -
API Integration with Proxy Layer: The application employs
publicapi_fetch.js
to centralize all interactions with the public API.
However, instead of directly communicating with the public API, it routes requests through a proxy server.This proxy layer serves two critical purposes:
- Confidentiality: It secures sensitive information, such as the x-api-key, by preventing direct exposure to the client side, thereby enhancing security.
- CORS Policy Management: The proxy mitigates Cross-Origin Resource Sharing (CORS) issues, which are common in web applications when a client-side script makes requests to a server different from its origin.
By using a proxy, the application effectively ‘bypasses’ CORS restrictions, allowing seamless data exchange between the client and the public API.
-
Authentication and User Profile Management for Public API: The use of
authentication.js
anduserprofile_organization.js
is indicative of a system specifically designed to handle authentication for the public API.This setup ensures that:
- User Authentication: It manages user authentication processes, securing access to the public API. This is essential for restricting access to authorized users only.
- Current User Information Retrieval: It retrieves information about the current user that mirrors the user’s data when logged into Sage Active. This could include the user’s preferences, settings, and roles.
- Organization Access Control: Additionally, it facilitates fetching the list of organizations that the user is authorized to access.
This feature is crucial for ensuring that users interact only with data they are permitted to, thereby maintaining data access integrity and security within the application.
Detailed Initialization Process
- Start main.js:
- Execution starts in
main.js
. - Initial setup tasks, like setting global variables and configurations, are likely performed.
- Execution starts in
- Initialize Authentication (
authentication.js
):- Establishes user authentication for Sage Active Public API.
- Might involve retrieving tokens, setting up session states, and handling user credentials.
- Fetch User Profile (
userprofile_organization.js
):- Retrieves user-specific information such as language preferences, associated organization, and other personal settings.
- This data could be used to customize the user interface and functionality.
- Set Up API Connections (
publicapi_fetch.js
,queries_mutations.js
):- Configures connections to the backend or external APIs.
- Prepares GraphQL queries and mutations for data fetching and updates.
- Sets up endpoints and necessary headers for API communication.
- Initialize UI Components (
customer.js
,products.js
,salesquote.js
etc.):- Constructs the user interface elements, such as menus, forms, and display panels.
- Initializes event listeners and binds them to UI components for user interactions.
- Loads initial data into UI components, such as product lists, customer information, etc.
- Load Country and Tariff Data (
countries.js
,tariff_discount.js
):- Additional data fetching for countries, tariffs, and discounts might be performed here.
- This data could be crucial for features like pricing, tax calculations, and internationalization.
- Application Ready for User:
- Final checks to ensure all components are loaded and APIs are responsive.
- The application is now fully operational and waiting for user inputs or actions.
HTML Page
-
demo.html
- HTML Structure: The document starts with standard HTML5 doctype, defining language as English. It includes meta tags for character set and viewport settings.
- Title and Scripts: Sets the page title to “Sample Quotes” and includes various JavaScript files like
main.js
,publicapi_fetch.js
, and others for functionality. - Script Inclusion: Loads additional scripts related to user profile, tariffs, calculations, products, customers, sales quotes, authentication, etc.
- CSS and Icons: Links to CSS stylesheets for main and common styles, and sets a favicon.
- Body Content: Contains multiple divs for product list, organization details, product filter, customer details, and quote summary. Includes input fields for customer and product information.
- Dynamic Content: The page is designed to dynamically load content, particularly products and customer details, through included JavaScript files.
Main functions
Here is a concise summary of the main functions of each JavaScript file:
-
main.js
- Global variables: Defines
sageActiveEnvironment
, and various header configurations (headerWithoutTenant
,headerWithoutOrganization
,headerWithOrganization
), andurlAPI
. $(document).ready(function() {...})
: Initializes the application. Reads configuration fromconfig.json
, disables the interface during loading, checks for access tokens, and callsinit()
if the token is present.init()
: Sets up headers with access token and environment, reads user profile for tenant and current organization details, defines headers template, and starts the app by callingreadAllOrganizationsAndInitPage()
with the active organization ID.initPage()
: Initializes the page by reading tariffs and products, and fetching recent quotes.
- Global variables: Defines
-
authentication.js
checkAccessToken()
: Checks for an existing access token or initiates the authentication flow. It fetches environment configurations and manages the user’s authentication state.authorizationRequest()
: Initiates the OAuth2 authorization request, generating a code verifier and challenge, and redirecting the user to the authorization URL.completeAuthCodeExchange(code)
: Completes the authentication process by exchanging the authorization code for access and refresh tokens.logout()
: Logs out the user by removing the access token and redirecting to a logout URL.logout2()
: An additional logout function that handles session ending and redirects for post-logout.refreshToken()
: Refreshes the access token using the refresh token, ensuring the user remains authenticated.fetchAllowedEnvironmentsAndSBCAuthConfiguration()
: Fetches allowed Sage Active environments and configuration for SBCAuth, including clientId and URL.getAuthConfig(sageActiveEnvironment)
: Retrieves the authentication configuration for the current environment.
-
userprofile_organization.js
readUserProfile()
: Fetches and displays the user profile information, including their full name, email, Sage Active environment, and legislation code. It also manages UI elements like disabling the customer search during processing.readAllOrganizationsAndInitPage(organizationId)
: Retrieves a list of all organizations and initializes the page with these details. It populates the organization selection dropdown and sets the active organization.- Event handler for
#organizations.change
: Manages the change event for the organization selection dropdown, updating the header with the selected organization ID and reinitializing the page. ChangeEnvAndForceLogout(env)
: Changes the Sage Active environment and forces a logout for the user. This function is used for switching between different Sage Active environments.otherEnvs()
: Populates the list of other Sage Active environments that the user can switch to. This function dynamically creates options in the environment selection dropdown based on the allowed environments.- Event handler for
#otherenvs.change
and#otherenv.blur
: Manages the environment selection process, including switching to another environment or returning to the list of organizations. addNavigateOption(selectElement, name, code)
: Adds navigation options to the dropdowns, used for creating entries like “Return to the list of organizations” or “Other environment not present in the list”.
-
ask_for_import.js
-
askForImportData():
Checks if the company contains any data and prompts the user to import sample data if it does not. It uses a generic fetch to the public API to query company data and handles the response to determine if data exists. -
handleResponse(data):
Processes the data fetched byaskForImportData
. It checks for the existence of different entities such as accounting exercises, customers, accounting accounts, and products. Based on the entities’ presence or absence, it decides whether to prompt the user to import demonstration data or products, considering the user’s permissions. -
areEntitiesEmpty(entities):
Determines if all provided entities are empty or null. This function helps in assessing whether there is a need to import sample or demonstration data by checking the content of various entities. -
checkPermissionsAndAct(requiredPolicies, warningMessage, actionFunction):
Checks if the user has the required permissions to perform an action (e.g., import data) and, if so, proceeds with the action. If the permissions are lacking, it displays a warning message. -
askForImportProducts():
Asks the user if they want to import sample products. This function is called if the company lacks product data and the user has the necessary permissions to add products. -
askForImportDemo():
Prompts the user to import demonstration data if the company contains no data. It bundles multiple configurations for importing various types of data and checks for the required permissions before proceeding. -
askForImportAccountingEntries():
Specifically asks the user if they wish to import demonstration data for accounting entries. This function is structured similarly toaskForImportDemo
but focuses solely on accounting entries. -
askForImport(dialogTitle, dialogMessage, importFunction):
A standard function called byaskForImportProducts
andaskForImportDemo
to present the user with a dialog box asking if they wish to import data. It handles the user’s choice and initiates the import process if agreed. -
handleImportAction(choice, importFunction):
Handles the user’s decision from the dialog box presented byaskForImport
. If the user chooses to import, it disables the interface and calls the import function; otherwise, it cancels the dialog box.
-
-
import_fromtemplate.js
-
createObjectFromTemplate(objectConfig, objectData, total, attempt = 1):
This function creates a new object from a template. It utilizes an object configuration to construct values from the provided data and attempts to import via a generic public API.
If the import is successful on the first attempt, it increments a counter and displays a success message. In case of specific errors or server errors, it handles these cases with appropriate messages and retry attempts up to a defined maximum. -
importFromTemplate(objectConfig):
Reads all records from a given template and callscreateObjectFromTemplate
for each record, except if the user lacks the necessary permissions. It initializes the import process by displaying an informational message, processes the data in batches, and handles completion or errors with corresponding messages.
This function ensures that data from template files is imported efficiently and manages user permissions to prevent unauthorized imports. -
importFromTemplates(configs):
Manages the comprehensive import process for multiple configurations.
It disables the user interface during the import to prevent any interference, clears and displays the business message history for transparency, and utilizes a timer to monitor the process duration.
This function sequences the import tasks for various configurations, handles the overall success or failure of the import process, and ensures post-import actions, like reading product data, are performed. -
Configuration Objects:
The script sets up specific configurations for importing different data types, such as AccountingPlanMasters, accountingExercises, products, customers, suppliers, employees, and accountingEntries.
Each configuration details how to construct the import request and describes the object for logging purposes, facilitating a structured and descriptive import process.
-
-
customer.js
readCustomer(nameValue, salesQuoteId)
: Retrieves customer details based on partial name or associated sales quote ID. It constructs a dynamicwhere
clause and handles the API response.- Event handling on
#searchCustomer
input: Manages customer search functionality, featuring input handling and debouncing for efficient querying. resetCustomer()
: Resets customer information in the interface, including details, product lists, and quotes.- Event handling for displaying customer input form and managing Lead details.
- Input handling for the
#contactName
and#country
fields, including readonly mode management based on certain conditions. fillInCustomerInfoFromLead()
: Fills customer information based on Lead details.- Additional functions and event handlers for interface management, such as resetting customer details, managing product lists, and handling customer searches.
-
countries.js
handleDocumentTypesResponse(data)
: Processes received document types data, populatingdocumentTypeCodes
.handleCountriesResponse(data)
: Manages received countries data, associating document types with countries.mapDocumentTypesToCountries()
: Async function to map document types to countries.initializeCountrySelectFromMapping()
: Initializes Select2 for countries, sorted by: 1) activeLegislationCode, 2) countries with VIES code, 3) other countries.getIsoCodeFromCountryName(countryName)
: Retrieves ISO code from a country’s name.- Event Listener for
$("#autoCompleteCountry").on("change", ...)
: Adjusts country Select2 based on browser address suggestion.
-
calculation.js
fetchProductPrice(productId, customerId, documentDate, quantity, row)
: Calls the price service with product details to fetch the product price based on quantity, customer, and date, and updates the UI with the fetched price and discount.calculateNetPrice(row)
: Calculates the net price for a product line in the quote based on the quantity, current price, and discount. It also updates the total price.resetAndDisableAllQuantitiesAndPrices()
: Resets and disables quantities and prices in the product list, useful for initializing or resetting the state of the quote form.deleteAllProductListRows()
: Removes all product list rows from the UI, essentially clearing the products section.cleanFooter()
: Clears the footer section of the quote form, resetting elements like quote number, discount percentage, and total amounts.updateTotal()
: Updates the total amount of the quote considering the net prices of each product and overall discount percentage. This function also manages the state of action buttons based on quote details.
-
products.js
readProductsFromTariffList(code)
: Fetches a list of products defined in a tariff or the full list of products if no tariff code is provided.readProducts(infoLines)
: Retrieves all products or products from a template or quote, dynamically building the query based on provided information.searchProductByCodeOrName(confirmInsert)
: Searches for a product by code or name, dynamically constructing the query and handling the response for insertion or display.createNewProduct()
: Creates a new product based on user input, including code, name, category, and sales unit price. It handles the response to add the newly created product to the product list.createProductRow(product, line)
: Dynamically generates a row in the product list table, formatting data and handling deletion events.clickOnTrash(line)
: Handles the removal of a product row from the table and updates the total amount.findNextVisibleRow(allRows, index)
: Finds the next visible row in the product list table for navigation purposes.handleProductRowClick(product)
: Manages the event when a product row is clicked, updating the details section with product information.addCreateOrSearchProductInputRow()
: Adds a new row to the table to enable the addition of a new product or the search and insertion of an existing product.
-
publicapi_fetch.js
genericFetchPublicAPI(headers, query, variables, functionCallback, context = "")
: This is the central function for making generic calls to the public API. It handles various aspects such as waiting for information, refining the query, managing headers, and processing the response.- Manages error scenarios including 401 Unauthorized (triggers token refresh), 400 Bad Request, 403 Forbidden, 500 Internal Server Error, and 504 Gateway Timeout.
- Calls
refreshToken()
function to handle token expiration and reauthentication. - Uses
displayBusinessError
for showing error messages based on API responses. - Executes the provided callback function (
functionCallback
) with the response data.
hasTotalCountAndIsZero(obj)
: Utility function to determine if a query has returned zero records. This function recursively checks the response object for atotalCount
property equal to zero.
-
salesquote.js
toggleButtonsActionQuote(actionType)
: Controls the display and styling of buttons based on the action type (create, update, duplicate).setButtonsActionQuoteDisabledState(isDisabled)
: Sets the enabled/disabled state of quote action buttons.- Event Listener for ‘click’ on ‘.createQuote, .updateQuote, .duplicateQuote’: Handles actions of quote creation, update, and duplication, including displaying confirmation dialogs.
createQuoteAction(choice)
andupdateQuoteAction(choice)
: Functions to initiate the creation or updating of a quote.createOrUpdateQuoteAction(choice, action)
: Manages the process of creating or updating a sales quote.- Event Listener for
.printPreview
: Prepares and displays a PDF preview of a quote. preparePreviewPdf(choice)
: Handles the previewing of a quote as a PDF.previewPdf(base64String)
: Displays a PDF from a base64 encoded string.toggleQuotePreparationRows(action)
: Manages the visibility of quote preparation rows.createOrUpdateSalesQuote(action)
: Creates or updates a sales quote.getCustomerOrAllLastQuotes(customerId, salesQuoteId)
: Fetches recent quotes for a specific customer or all customers.displayQuoteById(id)
: Displays a quote by its ID.
-
tariff_discount.js
readTariffs()
: Fetches a list of tariffs and stores them in a dictionary in memory. This function handles the response from the public API query and populates the tariffs dictionary.populateTariffDropdown()
: Populates a dropdown menu with templates of products (tariffs where the template is true). It dynamically adds options to the dropdown based on the tariffs dictionary.- Event handler for
#templateProducts.change
: Manages the change event for the template products dropdown. It updates the interface, deletes all product list rows, resets customer quotes and quote number, and reads products from the selected tariff list. getTariffName(id, callback)
: Retrieves the name of a tariff by its ID. This function can use either a pre-fetched dictionary or dynamically fetch the tariff name using a public API query.getDiscountName(id, callback)
: Fetches the name of a discount by its ID. Similar togetTariffName
, this function can use a pre-fetched dictionary or dynamically fetch the discount name using a public API query.
-
queries_mutations.js
-
The
qm
object inqueries_mutations.js
is organized to encapsulate and manage GraphQL queries and mutations within the application.
Each property of this object represents a specific operation that can be executed against the API, such as retrieving user profiles, creating products, or checking organizational data. -
Context and Policy: Each query or mutation within
qm
is associated with acontext
andpolicy
. Thecontext
describes the operation’s purpose or the data it deals with, like “User Profile” or “Products”. Thepolicy
is related to access control, specifying which permissions are required to execute the operation. -
Body: This is the actual GraphQL query or mutation. It’s a string that contains the GraphQL operation, structured to fetch or manipulate data according to the defined schemas and types in the GraphQL server. The body may include placeholders or variables (
$values
,$id
, etc.) that are dynamically replaced with actual values during execution. -
Parameters and Operation Details: Queries and mutations are designed to be flexible and reusable. For example,
queryProducts
might include parameters for filtering, sorting, and pagination to tailor the data retrieval to specific needs. Similarly, mutations likecreateProduct
take input objects that detail the product to be created, ensuring that the operation can adapt to different product details. -
Below is the current list of queries and mutations used by the application.
queryUserProfile
: Fetches user profile information, including application language code, authentication email, and full name.queryOrganizations
: Retrieves a list of organizations, filtering out defaults and “acme,” ordered by creation date in descending order.queryUserAccessPolicyCheck
: Checks user access policies for specific actions, indicating if each action is allowed.queryCountries
: Gets a list of countries with their ISO codes, names, and VIES codes, sorted alphabetically.queryDocumentTypes
: Fetches available document types, returning their IDs and codes.queryCustomer
: Queries customer details based on criteria like name or code, including detailed address information.queryTariffProducts
: Retrieves products associated with a specific sales tariff by its ID.queryTariffById
: Fetches details of a tariff by its ID, including its name.queryDiscountById
: Gets discount information by ID, returning the discount name.queryTariffs
: Lists all sales tariffs, including their ID, code, name, enabled status, and start/end dates.queryProducts
: Queries a list of products with optional criteria, returning comprehensive details including ID, code, comments, creation date, and more.createProduct
: Mutation to create a new product, specifying required input values.queryProductPrice
: Fetches price details of a product by ID, including tax and discount information.createSalesQuote
: Mutation to create a new sales quote with specified values.updateSalesQuote
: Mutation to update an existing sales quote with new values.deleteSalesQuote
: Mutation to delete a sales quote, specifying required input values.queryQuotesOfCurrentCustomerOrAll
: Retrieves sales quotes for the current customer or all, with detailed information.queryQuoteById
: Fetches detailed information of a sales quote by ID, including document date, operational number, status, and more.queryGetPdf
: Retrieves a PDF file for a given document ID, typically for quotes or reports.createAccountingEntry
: Mutation to create an accounting entry using specific codes.createCustomer
: Mutation to create a new customer with specified values.createSupplier
: Mutation to create a new supplier with specified values.createEmployee
: Mutation to create a new employee with specified values.importAccountingPlanMaster
: Mutation to import an accounting plan master with specified values.createAccountingExercise
: Mutation to create a new accounting exercise with specified values.queryCheckOrganizationData
: Checks for the presence of demo data in the organization, querying for accounting exercises, customers, accounting accounts, and products.
-
-
common.js
waitInfo(show=true)
: Toggles the display of a waiting animation based on theshow
parameter.enableInterface()
: Enables the user interface by hiding the modal background.disableInterface(transparent)
: Disables the user interface, optionally making the modal background transparent.displayModale(id, idFocus, noDisableInterface)
: Displays a modal dialog with the specified ID and sets focus to a designated element. IfnoDisableInterface
is false, it also disables the interface.hideModale(id)
: Hides a modal dialog specified by its ID and re-enables the user interface.dlgBoxYesNo(type, title, question, action)
: Displays a Yes/No dialog box with a custom title, question, and action callback.dlgBoxYesNoCancel()
: Hides the Yes/No dialog box.padRight(str, length)
: Pads the right side of a stringstr
to a specifiedlength
.formatDateMonthDay(dateString)
: Formats a date string into a “Month Day” format.popovers(obj, listItems)
: Creates and displays a popover menu with items and associated actions.matchCustom(params, data)
: Custom matching function for select2 HTML data formatting, used for advanced searches within select dropdowns.
-
interface_actions.js
- Event listeners for ‘focus’, ‘keyup’, ‘input’ on ‘input, textarea, select, button’: Manages the display of errors and information messages by hiding them when these elements are interacted with.
- Event listeners for ‘focus’ and ‘blur’ on ‘input.quantity’: Focuses on the current row in the product list table and removes focus when it’s blurred.
- Event listener for ‘keydown’ on ‘#product-list’: Implements navigation in the product list table using the ArrowUp and ArrowDown keys for changing rows and focusing on different inputs like quantity, current price, and discount.
displayBusinessError(receivedErrorKey, context)
: Displays a business error message by returning the label of an error code or the error code itself if the label is not found.hideBusinessError()
: Hides the business error message from the display.displayBusinessInfo(info, noHideOnfirstFocus)
: Displays an informational message and can be configured not to hide on the first focus.hideBusinessInfo()
: Hides the informational message from the display, considering thenoHideOnfirstFocus
flag.