Requirement specifications¶
Document Version: 1.0 Date: 2024-11-12 Copyright: Perun Consulting Services FZ LLE System: TQPRO - Trip Offer Management Module
Table of Contents¶
- Executive Summary
- System Overview
- Functional Requirements
- Data Requirements
- API Requirements
- User Interface Requirements
- Business Logic Requirements
- Non-Functional Requirements
- Architecture Requirements
- Integration Requirements
- Security Requirements
- Testing Requirements
1. Executive Summary¶
1.1 Purpose¶
The Trip Offer Management System provides a comprehensive solution for managing travel destination pages and trip variant snippets. The system enables content administrators to create, organize, and publish travel packages through a master-detail web interface with automated HTML page generation capabilities.
1.2 Scope¶
The system encompasses: - Management of destination trip pages - Management of trip variant snippets with rich metadata - Template-based HTML page generation - Master-detail UI pattern for related entity management - RESTful API for all CRUD operations - Session-based authentication and authorization
1.3 Stakeholders¶
- Content Administrators: Primary users who manage trip pages and snippets
- Web Developers: Integration with TQPRO platform
- End Users: Consumers of generated HTML pages (indirect)
2. System Overview¶
2.1 System Context¶
The Trip Offer Management System is a module within the TQPRO (TourLinQ Professional) platform, integrated with: - NTS (Native Transaction Service) database layer - TQPRO authentication and session management - Foundation CSS frontend framework - TQPRO entity management framework
2.2 Key Features¶
- Trip Page Management: Create and manage destination pages with metadata
- Trip Snippet Management: Manage detailed trip variant offerings
- Master-Detail Interface: Coordinated management of pages and their snippets
- HTML Generation: Template-based page generation with placeholder replacement
- Pagination Support: Handle large datasets efficiently
- Active Status Filtering: Control snippet visibility
2.3 Technology Stack¶
- Backend: Java 17+, Jakarta EE, Hibernate/JPA
- Database: PostgreSQL (nts schema)
- API Layer: JAX-RS (REST)
- Frontend: HTML5, JavaScript ES6+, jQuery, Foundation CSS 6.8
- Build System: Gradle
3. Functional Requirements¶
3.1 Trip Page Management (FR-PAGE)¶
FR-PAGE-001: Create Trip Page¶
Priority: High Description: System shall allow users to create new trip destination pages.
Input Requirements:
- page_name (String, max 200 chars, required): HTML filename for the destination page
- page_desc (String, max 1000 chars, required): Verbal description of the destination
Business Rules: - Page name must be unique - Page name should follow HTML filename conventions (e.g., "dubai-packages.html") - Page description is mandatory
Output:
- Newly created page with system-generated page_id
- Success/failure notification
FR-PAGE-002: Read Trip Page¶
Priority: High Description: System shall allow retrieval of trip page details.
Input Requirements:
- page_id (Integer, required): Unique identifier of the page
Output: - Complete page record with all fields - Error if page not found
FR-PAGE-003: Update Trip Page¶
Priority: High Description: System shall allow modification of existing trip pages.
Input Requirements:
- page_id (Integer, required): Identifier of page to update
- page_name (String, max 200 chars, required)
- page_desc (String, max 1000 chars, required)
Business Rules: - Cannot update non-existent page - Page name uniqueness constraint applies
FR-PAGE-004: Delete Trip Page¶
Priority: High Description: System shall allow deletion of trip pages.
Input Requirements:
- page_id (Integer, required): Identifier of page to delete
Business Rules: - CASCADE DELETE: Deleting a page should trigger deletion of all associated snippets - User must confirm deletion (UI requirement)
Side Effects:
- All trip snippets with matching page_id are deleted
FR-PAGE-005: List Trip Pages¶
Priority: High Description: System shall provide paginated listing of all trip pages.
Input Requirements:
- page (Integer, optional): Zero-based page number (default: 0)
- pageSize (Integer, optional): Records per page (default: 10)
Output: - List of trip page records - Supports in-memory pagination
Business Rules: - Results ordered by page_id ascending - Empty list returned if no pages exist
3.2 Trip Snippet Management (FR-SNIPPET)¶
FR-SNIPPET-001: Create Trip Snippet¶
Priority: High Description: System shall allow users to create trip variant snippets.
Input Requirements (15 fields):
| Field | Type | Max Length | Required | Description |
|---|---|---|---|---|
snippet_id |
Integer | - | Auto-generated | Primary key |
page_id |
Integer | - | Yes | Foreign key to trip_page |
code |
String | 20 | Yes | Unique public package code |
title |
String | 50 | Yes | Package title |
request_desc |
String | 200 | No | Chat request description template |
destination |
String | 50 | No | Destination name |
image |
String | 200 | No | Image URL/path |
imagetitle |
String | 100 | No | Image alt text |
tagline1 |
String | 100 | No | First promotional tagline |
tagline2 |
String | 100 | No | Second promotional tagline |
price |
String | 20 | No | Price display text |
shortdesc |
String | 200 | No | Short description |
description |
String | 1000 | No | Full description |
inclusionhtml |
String | 1000 | No | HTML list of inclusions |
active |
Integer | - | No | Status flag (1=active, 0=inactive) |
Business Rules:
- code must be unique across all snippets
- page_id must reference existing trip_page
- title is mandatory
- active defaults to 1 if not specified
FR-SNIPPET-002: Read Trip Snippet¶
Priority: High Description: System shall allow retrieval of snippet details.
Input Requirements:
- snippet_id (Integer, required)
Output: - Complete snippet record with all 15 fields
FR-SNIPPET-003: Update Trip Snippet¶
Priority: High Description: System shall allow modification of existing snippets.
Input Requirements:
- All fields from FR-SNIPPET-001
- snippet_id must exist
Business Rules:
- Cannot change page_id to non-existent page
- Unique code constraint applies
FR-SNIPPET-004: Delete Trip Snippet¶
Priority: High Description: System shall allow deletion of trip snippets.
Input Requirements:
- snippet_id (Integer, required)
Business Rules: - User confirmation required (UI)
FR-SNIPPET-005: List Snippets by Page¶
Priority: High Description: System shall provide paginated listing of snippets for a specific page.
Input Requirements:
- page_id (Integer, required): Filter by this page
- page (Integer, optional): Page number
- pageSize (Integer, optional): Records per page
Output: - List of snippet records for the specified page - Ordered by snippet_id
FR-SNIPPET-006: Get Active Snippets¶
Priority: High Description: System shall retrieve only active snippets for a page.
Input Requirements:
- page_id (Integer, required)
Output:
- List of snippets where active = 1
Business Rules: - Used exclusively for HTML generation - No pagination (returns all active snippets)
3.3 Template Management (FR-TEMPLATE)¶
FR-TEMPLATE-001: List Skeleton Templates¶
Priority: High Description: System shall provide list of available HTML skeleton templates.
Output:
- List of skeleton records containing:
- skeleton_id (Integer): Unique identifier
- skeleton_name (String, 200): Display name
- skeleton_desc (String, 500): Description
- skeleton_file (String, 500): File path to skeleton HTML
Business Rules:
- Skeleton files must exist on filesystem
- Skeleton files must contain the marker: ---INSERT SNIPPETS HERE---
FR-TEMPLATE-002: List Snippet Templates¶
Priority: High Description: System shall provide list of available snippet template files.
Output:
- List of snippet file records containing:
- snippet_file_id (Integer): Unique identifier
- snippet_file_name (String, 200): Display name
- snippet_file_desc (String, 500): Description
- snippet_file (String, 500): File path to snippet template HTML
Business Rules:
- Snippet template files must exist on filesystem
- Templates use placeholder syntax: %%fieldname%%
3.4 HTML Generation (FR-GENERATE)¶
FR-GENERATE-001: Generate HTML Page¶
Priority: Critical Description: System shall generate complete HTML pages by combining skeleton templates with snippet data.
Input Requirements:
- page_id (Integer, required): Trip page to generate
- skeleton_id (Integer, required): Skeleton template to use
- snippet_file_id (Integer, required): Snippet template to use
Processing Steps:
- Validate Inputs: All three IDs must exist
- Load Page Data: Retrieve trip_page record
- Load Skeleton: Read skeleton HTML file from filesystem
- Load Snippet Template: Read snippet template file from filesystem
- Fetch Active Snippets: Get all snippets where
page_idmatches andactive = 1 - For Each Snippet:
- Clone snippet template
- Replace all placeholders with snippet data
- Append to snippet collection
- Final Assembly: Replace
---INSERT SNIPPETS HERE---marker in skeleton with all snippet HTML - Return Result: Generated HTML content and filename
Placeholder Mapping:
| Placeholder | Source Field | Notes |
|---|---|---|
%%code%% |
snippet.code | Package code |
%%title%% |
snippet.title | Package title |
%%request_desc%% |
snippet.request_desc | Chat request description |
%%destination%% |
snippet.destination | Destination name |
%%image%% |
snippet.image | Image URL |
%%imagetitle%% |
snippet.imagetitle | Image alt text |
%%tagline1%% |
snippet.tagline1 | First tagline |
%%tagline2%% |
snippet.tagline2 | Second tagline |
%%price%% |
snippet.price | Price display |
%%shortdesc%% |
snippet.shortdesc | Short description |
%%description%% |
snippet.description | Full description |
%%inclusionhtml%% |
snippet.inclusionhtml | Inclusions HTML |
Output:
Business Rules:
- NULL values are replaced with empty strings
- Only active snippets (active = 1) are included
- Snippets are concatenated with newline separators
- Original skeleton and template files are not modified
- Generated HTML is returned to client for download
Error Conditions: - Page not found - Skeleton not found - Snippet file not found - File I/O errors - No active snippets (warning, not error - generates empty page)
3.5 Master-Detail Coordination (FR-COORD)¶
FR-COORD-001: Page Selection Updates Snippets¶
Priority: High Description: When a trip page is selected in the UI, the snippet list must automatically update.
Behavior: - Clicking a page row highlights it - Snippet table immediately loads snippets for selected page - Previous snippet selection is cleared - If no snippets exist, display "No snippets found for this page"
FR-COORD-002: Page Deletion Clears Snippets¶
Priority: High Description: When selected page is deleted, snippet panel must clear.
Behavior: - If currently displayed page is deleted, clear snippet table - Reset selected page state to null - Display "Select a page to view snippets"
FR-COORD-003: Snippet Requires Page Selection¶
Priority: High Description: Users cannot create snippets without first selecting a page.
Behavior:
- "New Snippet" button disabled when no page selected
- Creating snippet auto-populates page_id from selected page
4. Data Requirements¶
4.1 Database Schema Requirements¶
4.1.1 Schema Location¶
- Schema Name:
nts - Database: PostgreSQL 12+
4.1.2 Table: nts.trip_page¶
Purpose: Stores destination page definitions
CREATE TABLE nts.trip_page (
page_id INTEGER NOT NULL,
page_name VARCHAR(200) NOT NULL,
page_desc VARCHAR(1000) NOT NULL,
CONSTRAINT trip_page_pk PRIMARY KEY(page_id)
);
Columns:
| Column | Type | Constraints | Description |
|---|---|---|---|
| page_id | INTEGER | PRIMARY KEY, NOT NULL | Unique identifier (sequence-generated) |
| page_name | VARCHAR(200) | NOT NULL | HTML filename for destination |
| page_desc | VARCHAR(1000) | NOT NULL | Description of destination |
Indexes:
- Primary key index on page_id
Sequences:
- nts.trip_page_seq: Managed by Hibernate
4.1.3 Table: nts.trip_snippet¶
Purpose: Stores trip variant snippet details
CREATE TABLE nts.trip_snippet (
snippet_id INTEGER NOT NULL,
page_id INTEGER NOT NULL,
code VARCHAR(20),
request_desc VARCHAR(200),
destination VARCHAR(50),
image VARCHAR(200),
imagetitle VARCHAR(100),
tagline1 VARCHAR(100),
tagline2 VARCHAR(100),
price VARCHAR(20),
title VARCHAR(50),
shortdesc VARCHAR(200),
description VARCHAR(1000),
inclusionhtml VARCHAR(1000),
active INTEGER,
CONSTRAINT trip_snippet_pk PRIMARY KEY(snippet_id),
CONSTRAINT trip_snippet_code_uindex UNIQUE(code),
CONSTRAINT trip_snippet_trip_page_page_id_fk
FOREIGN KEY (page_id) REFERENCES nts.trip_page(page_id)
ON DELETE NO ACTION
);
Columns: (See FR-SNIPPET-001 for detailed field specifications)
Constraints:
- UNIQUE on code column
- FOREIGN KEY to trip_page.page_id
- ON DELETE NO ACTION (application-level cascade)
Indexes:
- Primary key on snippet_id
- Unique index on code
- Foreign key index on page_id
Sequences:
- nts.trip_snippet_seq: Managed by Hibernate
4.1.4 Table: nts.trip_skeleton¶
Purpose: Stores HTML skeleton template metadata
CREATE TABLE nts.trip_skeleton (
skeleton_id INTEGER NOT NULL,
skeleton_name VARCHAR(200) NOT NULL,
skeleton_desc VARCHAR(500),
skeleton_file VARCHAR(500) NOT NULL,
CONSTRAINT trip_skeleton_pk PRIMARY KEY(skeleton_id)
);
Columns:
| Column | Type | Constraints | Description |
|---|---|---|---|
| skeleton_id | INTEGER | PRIMARY KEY | Unique identifier |
| skeleton_name | VARCHAR(200) | NOT NULL | Display name |
| skeleton_desc | VARCHAR(500) | NULL | Description |
| skeleton_file | VARCHAR(500) | NOT NULL | Filesystem path to HTML file |
Business Rules:
- skeleton_file must be absolute or relative path accessible by server
- Skeleton HTML must contain marker: ---INSERT SNIPPETS HERE---
4.1.5 Table: nts.trip_snippet_file¶
Purpose: Stores snippet template metadata
CREATE TABLE nts.trip_snippet_file (
snippet_file_id INTEGER NOT NULL,
snippet_file_name VARCHAR(200) NOT NULL,
snippet_file_desc VARCHAR(500),
snippet_file VARCHAR(500) NOT NULL,
CONSTRAINT trip_snippet_file_pk PRIMARY KEY(snippet_file_id)
);
Columns:
| Column | Type | Constraints | Description |
|---|---|---|---|
| snippet_file_id | INTEGER | PRIMARY KEY | Unique identifier |
| snippet_file_name | VARCHAR(200) | NOT NULL | Display name |
| snippet_file_desc | VARCHAR(500) | NULL | Description |
| snippet_file | VARCHAR(500) | NOT NULL | Filesystem path to template file |
Business Rules:
- Template files must use
%%placeholder%%syntax - Files must be accessible by server process
4.2 Entity Layer Requirements¶
4.2.1 Database Entity Classes (Hibernate/JPA Layer)¶
Package: com.perun.tlinq.client.nts.db.trip
Classes Required:
1. TrippageEntity - Maps to nts.trip_page
2. TripsnippetEntity - Maps to nts.trip_snippet
3. TripskeletonEntity - Maps to nts.trip_skeleton
4. TripsnippetfileEntity - Maps to nts.trip_snippet_file
Requirements:
- Must extend NTSEntity
- Use JPA annotations: @Entity, @Table, @Column, @Id
- Use @GeneratedValue with SEQUENCE strategy
- Use @SequenceGenerator for ID generation
- Use validation annotations: @NotNull, @Size
- Override getId() method from NTSEntity
Example Structure:
@Entity
@Table(name = "trip_page", schema = "nts")
public class TrippageEntity extends NTSEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "trip_page_id_gen")
@SequenceGenerator(name = "trip_page_id_gen",
sequenceName = "nts.trip_page_seq",
allocationSize = 1)
@Column(name = "page_id", nullable = false)
private Integer pageId;
// Additional fields with JPA annotations
// Getters and setters
// Override getId()
}
4.2.2 Canonical Entity Classes (Business Layer)¶
Package: com.perun.tlinq.entity.trip
Classes Required:
1. CTripPage - Business entity for trip pages
2. CTripSnippet - Business entity for trip snippets
3. CTripSkeleton - Business entity for skeletons
4. CTripSnippetFile - Business entity for snippet files
Requirements:
- Must extend TlinqEntity
- Must implement Serializable
- Platform-independent POJOs (no JPA annotations)
- All fields use Java wrapper types (Integer, not int)
- Include toString() implementation
- Field names match database entity field names for mapping
Example Structure:
public class CTripPage extends TlinqEntity implements Serializable {
private Integer pageId;
private String pageName;
private String pageDesc;
// Getters and setters
// toString() implementation
}
4.3 Entity Mapping Configuration¶
File: config/tourlinq-config.xml
Requirement: Map each canonical entity to its corresponding native entity with explicit field mappings.
Configuration Pattern:
<Entity name="TripPage"
class="com.perun.tlinq.entity.trip.CTripPage"
idField="pageId"
defaultFactory="NTSServiceFactory">
<EntityFactoryList>
<Factory name="NTSServiceFactory"
nativeEntity="com.perun.tlinq.client.nts.db.trip.TrippageEntity">
<ServiceList>
<Service name="saveTripPage" action="create"/>
<Service name="saveTripPage" action="update"/>
<Service name="readTripPage" action="read"/>
<Service name="readTripPage" action="search"/>
<Service name="deleteTripPage" action="delete"/>
</ServiceList>
<FieldMappingList>
<FieldMapping targetField="pageId" sourceField="pageId"
mapping="DirectMapping"/>
<FieldMapping targetField="pageName" sourceField="pageName"
mapping="DirectMapping"/>
<FieldMapping targetField="pageDesc" sourceField="pageDesc"
mapping="DirectMapping"/>
</FieldMappingList>
</Factory>
</EntityFactoryList>
</Entity>
Requirements:
- One <Entity> block per canonical entity
- idField must match primary key field name
- All entity fields must have corresponding <FieldMapping>
- Use DirectMapping for one-to-one field mappings
- Service names follow convention: save[EntityName], read[EntityName], delete[EntityName]
Entities to Configure: 1. TripPage (3 fields) 2. TripSnippet (15 fields) 3. TripSkeleton (4 fields) 4. TripSnippetFile (4 fields)
5. API Requirements¶
5.1 API Architecture¶
Base Path: /api/tripoffer
Protocol: HTTP/HTTPS
Method: POST (all endpoints)
Content-Type: application/json
Response Type: application/json
5.2 API Response Format¶
All API endpoints must return TlinqApiResponse format:
Success Response:
Error Response:
5.3 API Endpoints Specification¶
5.3.1 POST /api/tripoffer/page/list¶
Purpose: List all trip pages with pagination
Request Body:
Response (200 OK):
{
"success": true,
"data": [
{
"pageId": 1,
"pageName": "dubai-packages.html",
"pageDesc": "Dubai vacation packages"
}
]
}
Implementation:
- Delegate to TripOfferFacade.listPages()
- Extract session from header X-Auth-Request-Access-Token if not in body
5.3.2 POST /api/tripoffer/page/read¶
Purpose: Retrieve single trip page
Request Body:
Response:
{
"success": true,
"data": {
"pageId": 1,
"pageName": "dubai-packages.html",
"pageDesc": "Dubai vacation packages"
}
}
Error Codes:
-
MISSING_PARAMETER: pageId not provided -
ENTITY_NOT_FOUND: Page does not exist
5.3.3 POST /api/tripoffer/page/write¶
Purpose: Create or update trip page
Request Body:
{
"session": "string",
"pageId": 1, // null for create
"pageName": "dubai-packages.html",
"pageDesc": "Dubai vacation packages"
}
Response:
{
"success": true,
"data": {
"pageId": 1, // generated for new records
"pageName": "dubai-packages.html",
"pageDesc": "Dubai vacation packages"
}
}
Validation:
pageNamerequiredpageDescrequired
5.3.4 POST /api/tripoffer/page/delete¶
Purpose: Delete trip page Request Body:
Response:
Side Effects: All snippets with matching page_id are deleted
5.3.5 POST /api/tripoffer/snippet/list¶
Purpose: List snippets for a page
Request Body:
Response:
{
"success": true,
"data": [
{
"snippetId": 1,
"pageId": 1,
"code": "DXB-PKG-001",
"title": "Dubai Premium Package",
"price": "$599",
"active": 1,
// ... other 10 fields
}
]
}
5.3.6 POST /api/tripoffer/snippet/read¶
Purpose: Retrieve single snippet
Request Body:
Response: Complete snippet record with all 15 fields
5.3.7 POST /api/tripoffer/snippet/write¶
Purpose: Create or update snippet
Request Body: All 15 snippet fields
Validation:
-
coderequired and unique -
titlerequired -
pageIdmust reference existing page
5.3.8 POST /api/tripoffer/snippet/delete¶
Purpose: Delete snippet
Request Body:
5.3.9 POST /api/tripoffer/skeleton/list¶
Purpose: List skeleton templates
Request Body:
Response:
{
"success": true,
"data": [
{
"skeletonId": 1,
"skeletonName": "Standard Destination Page",
"skeletonDesc": "Standard template",
"skeletonFile": "/path/to/skeleton.html"
}
]
}
5.3.10 POST /api/tripoffer/snippetfile/list¶
Purpose: List snippet templates
Request Body:
Response: Similar to skeleton/list
5.3.11 POST /api/tripoffer/page/generate¶
Purpose: Generate HTML page with snippets
Request Body:
Response:
{
"success": true,
"data": {
"success": true,
"fileName": "dubai-packages.html",
"content": "<html>...</html>"
}
}
Processing: See FR-GENERATE-001 for detailed algorithm
5.4 API Implementation Requirements¶
Class: com.perun.tlinq.api.TripOfferApi
Pattern: Endpoint methods wrap doXXX() implementation methods
Structure:
@POST
@Path("/page/list")
public Response listPages(Map reqData, @Context HttpHeaders headers) {
try {
session = ApiUtil.gmp(reqData, "session", String.class, false);
// Extract from header if needed
ar = doListPages(reqData);
} catch (TlinqClientException e) {
ar = new TlinqApiResponse(e.getErrorCode(), e.getMessage());
}
return Response.ok(ar).build();
}
private TlinqApiResponse doListPages(Map reqData) throws TlinqClientException {
// Extract parameters
// Create facade instance
// Delegate to facade
// Return response
}
Requirements:
-
All endpoints use
@POST -
All consume and produce
application/json -
Extract session from header
X-Auth-Request-Access-Tokenas fallback -
Comprehensive logging: BEGIN/END APICALL with session
-
Exception handling for
TlinqClientExceptionand general exceptions -
All business logic delegated to
TripOfferFacade
6. User Interface Requirements¶
6.1 Page Layout Requirements¶
File: tqweb-adm/offerman.html
6.1.1 Overall Layout¶
Framework: Foundation CSS 6.8
Grid System: Foundation grid with responsive classes
Structure:
┌─────────────────────────────────────────────┐
│ Page Header │
│ "Trip Offer Management" │
├─────────────┬───────────────────────────────┤
│ Trip │ Trip Snippets │
│ Pages │ (for selected page) │
│ (1/3) │ (2/3) │
│ │ │
│ [Table] │ [Table] │
│ [Paging] │ [Paging] │
│ │ │
│ │ HTML Generation Section │
│ │ [Skeleton Dropdown] │
│ │ [Snippet File Dropdown] │
│ │ [Generate Button] │
└─────────────┴───────────────────────────────┘
Responsive Behavior:
-
Desktop: 1/3 - 2/3 split
-
Tablet: Equal columns
-
Mobile: Stacked vertically
6.1.2 Trip Pages Panel¶
Location: Left panel (1/3 width on desktop)
Components:
-
Header Section:
-
Title: "Trip Pages" with icon
-
"New Page" button (success/green)
-
-
Table Columns:
-
Page ID
-
Page Name & Description (stacked)
-
Actions (Edit, Delete buttons)
-
-
Pagination Controls:
-
Info text: "Showing X-Y"
-
Previous button
-
Next button
-
Visual Requirements:
-
Selected row highlighted with purple left border
-
Hover effect on rows
-
Action buttons: pencil icon (edit), X icon (delete)
-
Empty state: "No pages found" message
6.1.3 Trip Snippets Panel¶
Location: Right panel (2/3 width on desktop)
Components:
-
Header Section:
-
Title: "Trip Snippets" with icon
-
"New Snippet" button (success/green, disabled if no page selected)
-
-
Table Columns:
-
Snippet ID
-
Code
-
Title
-
Price
-
Active Status (badge: green for active, red for inactive)
-
Actions (Edit, Delete)
-
-
Pagination Controls: Same as pages panel
Visual Requirements:
-
Active badges: green background
-
Inactive badges: red background
-
Disabled state for "New Snippet" when no page selected
-
Empty state: "Select a page to view snippets" or "No snippets found for this page"
6.1.4 HTML Generation Section¶
Location: Below snippets table
Components:
-
Skeleton Template Selector:
-
Label: "Skeleton Template"
-
Dropdown populated from
/api/tripoffer/skeleton/list -
Display:
skeleton_name
-
-
Snippet Template Selector:
-
Label: "Snippet Template"
-
Dropdown populated from
/api/tripoffer/snippetfile/list -
Display:
snippet_file_name
-
-
Generate Button:
-
Text: "Generate & Download"
-
Primary/purple button
-
Icon: download icon
-
Disabled if no page selected
-
Behavior:
-
Clicking generates HTML and triggers browser download
-
Shows loading indicator during generation
-
Success notification on completion
6.2 Modal Dialogs¶
6.2.1 Page Dialog (page_dlg)¶
Fields:
-
Page ID (hidden/readonly for edits)
-
Page Name (text input, required)
-
Page Description (textarea, required)
Buttons:
-
Save
-
Cancel
Validation:
- Page name cannot be empty
6.2.2 Snippet Dialog (snippet_dlg)¶
Fields (15 fields matching CTripSnippet):
| Field | Input Type | Required |
|-------|------------|----------|
| Snippet ID | Hidden | Auto |
| Page ID | Hidden | Auto |
| Code | Text | Yes |
| Title | Text | Yes |
| Request Description | Textarea | No |
| Destination | Text | No |
| Image URL | Text | No |
| Image Title | Text | No |
| Tagline 1 | Text | No |
| Tagline 2 | Text | No |
| Price | Text | No |
| Short Description | Textarea | No |
| Description | Textarea | No |
| Inclusion HTML | Textarea | No |
| Active | Checkbox | No |
Layout: Two-column form for better space utilization
Validation:
-
Code required and unique
-
Title required
6.3 Design System Requirements¶
6.3.1 Color Palette¶
--primary-color: #362c5d; /* Purple for primary actions */
--primary-dark: #2a2149; /* Darker purple for hover */
--secondary-color: #FFC166; /* Orange accent */
--success-color: #3adb76; /* Green for success */
--warning-color: #ffae00; /* Orange for warnings */
--alert-color: #cc4b37; /* Red for errors/delete */
--light-bg: #f8f9fa; /* Page background */
--border-color: #e1e4e8; /* Borders */
6.3.2 Component Styling¶
Content Cards:
-
White background
-
Border radius: 8px
-
Box shadow:
0 2px 4px rgba(0,0,0,0.1) -
Header with gradient background
Tables:
-
Header: Gray gradient background
-
Row hover: Light blue background
-
Selected row: Blue background with purple left border
-
Cell padding: 15px
Buttons:
-
Border radius: 6px
-
Font weight: 600
-
Hover: Slight upward movement with shadow
-
Primary: Purple background
-
Success: Green background
-
Alert: Red background
Form Inputs:
-
Border radius: 6px
-
Padding: 12px 15px
-
Focus: Purple border with shadow
6.4 JavaScript Module Requirements¶
File: tqweb-adm/js/modules/offerman.js
Module System: ES6 modules
6.4.1 Module State¶
export let currentPagePage = 0;
export let currentSnippetPage = 0;
export let selectedPageId = null;
export let selectedPageData = null;
export const pageSize = 10;
let pages = [];
let snippets = [];
let skeletons = [];
let snippetFiles = [];
let pageRecord = {};
let snippetRecord = {};
6.4.2 Exported Functions¶
Initialization:
initialize(): Load initial data, setup bindings
Page Management:
-
loadPages(page): Fetch and render pages -
selectPage(e): Handle page row click -
newPage(): Open create dialog -
editPage(e): Open edit dialog -
savePage(): Submit page data -
deletePage(e): Delete page with confirmation
Snippet Management:
-
loadSnippets(page): Fetch and render snippets -
newSnippet(): Open create dialog -
editSnippet(e): Open edit dialog -
saveSnippet(): Submit snippet data -
deleteSnippet(e): Delete snippet with confirmation
Template Management:
-
loadSkeletons(): Populate skeleton dropdown -
loadSnippetFiles(): Populate snippet file dropdown
HTML Generation:
generatePage(): Generate and download HTML
Pagination:
-
prevPagePages(): Previous page of pages -
nextPagePages(): Next page of pages -
prevPageSnippets(): Previous page of snippets -
nextPageSnippets(): Next page of snippets
6.4.3 Data Binding¶
Requirements:
-
Use
$util.setUpEntityData()to bind entity to form fields -
Use
$util.extractEntityData()to extract form data to entity -
Form fields use
data-entity-nameanddata-entity-fieldattributes
Example:
6.4.4 AJAX Requirements¶
Configuration:
-
URL:
/api/tripoffer/* -
Method: POST
-
Content-Type:
application/json -
Data: JSON.stringify(reqData)
Error Handling:
-
Display user-friendly notifications using
$.notify() -
Log errors to console
Success Handling:
-
Update UI
-
Show success notification
-
Close dialogs
-
Refresh data
7. Business Logic Requirements¶
7.1 Facade Architecture¶
Class: com.perun.tlinq.entity.trip.TripOfferFacade
Package: com.perun.tlinq.entity.trip
Module: tqapp
Extends: EntityFacade
7.2 Facade Responsibilities¶
-
Business Logic: All validation and business rules
-
Session Management: Handle session tokens
-
Entity Operations: CRUD via EntityFacade base class
-
Factory Integration: Use
NTSServiceFactory -
Error Handling: Throw
TlinqClientExceptionwith appropriate error codes
7.3 Constructor Requirements¶
Behavior:
-
If token is null/empty, use system session
-
System session loaded from
tourlinq-config.xml
7.4 Validation Rules¶
7.4.1 Page Validation¶
Method: writePage(CTripPage page)
Rules:
-
Page entity cannot be null → Error:
MISSING_PARAMETER -
Page name cannot be null/empty → Error:
MISSING_PARAMETER -
Page description cannot be null/empty (implicitly handled by DB constraint)
7.4.2 Snippet Validation¶
Method: writeSnippet(CTripSnippet snippet)
Rules:
-
Snippet entity cannot be null → Error:
MISSING_PARAMETER -
Code cannot be null/empty → Error:
MISSING_PARAMETER -
Code must be unique → Database constraint violation
-
Title cannot be null/empty → Error:
MISSING_PARAMETER -
Page ID must reference existing page → Foreign key constraint
7.4.3 Generation Validation¶
Method: generateHtmlPage(Integer pageId, Integer skeletonId, Integer snippetFileId)
Rules:
-
All three parameters required → Error:
MISSING_PARAMETER -
Page must exist → Error:
ENTITY_NOT_FOUND -
Skeleton must exist → Error:
ENTITY_NOT_FOUND -
Snippet file must exist → Error:
ENTITY_NOT_FOUND -
Skeleton file must be readable → Error:
GENERALwith descriptive message -
Snippet file must be readable → Error:
GENERALwith descriptive message
7.5 Pagination Logic¶
Implementation: In-memory pagination
if (page != null && pageSize != null) {
int start = page * pageSize;
int end = Math.min(start + pageSize, results.size());
if (start < results.size()) {
return results.subList(start, end);
} else {
return Collections.emptyList();
}
}
Characteristics:
-
Zero-based page numbering
-
Returns empty list if start index exceeds result size
-
Does not use database-level pagination (LIMIT/OFFSET)
7.6 HTML Generation Algorithm¶
Method: generateHtmlPage()
Pseudocode:
1. VALIDATE inputs (page_id, skeleton_id, snippet_file_id)
2. READ trip_page record
3. READ skeleton record
4. READ snippet_file record
5. FETCH active snippets WHERE page_id = ? AND active = 1
6. READ skeleton HTML file from filesystem
7. READ snippet template HTML file from filesystem
8. INITIALIZE empty snippet_html string
9. FOR EACH active snippet:
a. CLONE snippet template
b. REPLACE all %%placeholder%% with snippet field values
c. APPEND to snippet_html with newline
10. REPLACE "---INSERT SNIPPETS HERE---" in skeleton with snippet_html
11. RETURN { success: true, fileName: page.pageName, content: final_html }
Placeholder Replacement:
-
Use
String.replace("%%field%%", nvl(snippet.getField())) -
Null values converted to empty strings via
nvl()helper
8. Non-Functional Requirements¶
8.1 Performance Requirements¶
NFR-PERF-001: API Response Time
-
95th percentile: < 500ms for list operations
-
95th percentile: < 200ms for read operations
-
95th percentile: < 1000ms for write operations
-
95th percentile: < 2000ms for HTML generation
NFR-PERF-002: Pagination
-
Support up to 10,000 records per entity without performance degradation
-
Page size configurable (default: 10)
NFR-PERF-003: Concurrent Users
- Support at least 20 concurrent users without performance degradation
8.2 Scalability Requirements¶
NFR-SCALE-001: Database Indexes
-
All foreign keys indexed
-
Unique constraints on code fields
-
Primary keys auto-indexed
NFR-SCALE-002: Entity Caching
- Leverage Hibernate second-level cache (if configured)
8.3 Usability Requirements¶
NFR-USE-001: Responsive Design
-
Functional on desktop (1920x1080)
-
Functional on tablet (768x1024)
-
Functional on mobile (375x667)
NFR-USE-002: User Feedback
-
All actions provide visual feedback (notifications, loading indicators)
-
Error messages are user-friendly and actionable
-
Confirmation dialogs for destructive actions
NFR-USE-003: Accessibility
-
Form labels associated with inputs
-
Keyboard navigation supported
-
Color contrast meets WCAG AA standards
8.4 Maintainability Requirements¶
NFR-MAINT-001: Code Organization
-
Clear separation of concerns (API → Facade → Entity → Database)
-
Follow established TQPRO patterns
-
Comprehensive inline documentation
NFR-MAINT-002: Logging
-
All API calls logged with session ID
-
Errors logged with stack traces
-
Use
java.util.logging.Logger
NFR-MAINT-003: Configuration
-
Entity mappings in XML (not hardcoded)
-
Database sequences managed by Hibernate
-
Template file paths configurable via database
8.5 Reliability Requirements¶
NFR-REL-001: Data Integrity
-
Referential integrity enforced via foreign keys
-
Unique constraints prevent duplicate codes
-
Transactions ensure atomicity
NFR-REL-002: Error Recovery
-
Database errors caught and returned as API errors
-
File I/O errors handled gracefully
-
No partial updates (transaction rollback on error)
9. Architecture Requirements¶
9.1 Layered Architecture Compliance¶
Requirement: System must strictly follow TQPRO's layered architecture.
Layers (top to bottom):
-
Presentation Layer: HTML + JavaScript
-
API Layer: REST endpoints (
TripOfferApi) -
Business Logic Layer: Facade (
TripOfferFacade) -
Canonical Entity Layer: Platform-independent entities (
CTripPage, etc.) -
Native Entity Layer: Database-specific entities (
TrippageEntity, etc.) -
Database Layer: PostgreSQL
Rules:
-
No layer should skip the layer directly below it
-
API layer must not contain business logic
-
Canonical entities must not have JPA annotations
-
Configuration-driven mapping between canonical and native entities
9.2 Design Patterns¶
Pattern 1: Facade Pattern
-
TripOfferFacadeprovides simplified interface to complex subsystem -
Encapsulates business logic and entity operations
Pattern 2: Data Transfer Object (DTO)
-
Canonical entities serve as DTOs between layers
-
Serializable for potential remote calls
Pattern 3: Template Method
-
HTML generation uses template method pattern
-
Skeleton provides structure, snippets fill in details
Pattern 4: Master-Detail
-
UI pattern for managing related entities
-
Page selection drives snippet display
9.3 Dependency Management¶
Module Structure:
-
tqapp: Contains facades and canonical entities -
tqapi: Contains REST API endpoints -
tqweb-adm: Contains HTML and JavaScript -
config: Contains entity mappings
Dependencies:
-
tqapidepends ontqapp -
tqappdepends ontqcommon(for EntityFacade) -
All modules depend on
config
10. Integration Requirements¶
10.1 Database Integration¶
Connection: Via Hibernate SessionFactory managed by TQPRO
Transaction Management: Container-managed (Jakarta EE)
Schema: nts schema in PostgreSQL
10.2 Session Management Integration¶
Authentication: TQPRO session tokens
Session Extraction:
-
From request body
sessionfield -
From header
X-Auth-Request-Access-Token(fallback) -
Use system session if neither present
Authorization: All API endpoints require valid session
10.3 Frontend Integration¶
Foundation CSS: Version 6.8.1 from CDN
jQuery: Required for AJAX and DOM manipulation
Utility Module: utilmod.js for data binding helpers
11. Security Requirements¶
11.1 Authentication¶
SEC-AUTH-001: All API endpoints require session token
SEC-AUTH-002: Invalid/expired sessions return error
SEC-AUTH-003: System session used only for background operations
11.2 Input Validation¶
SEC-VAL-001: All user inputs validated on server side
SEC-VAL-002: SQL injection prevented via Hibernate parameterized queries
SEC-VAL-003: XSS prevention in HTML generation (context: generated HTML is for offline use)
11.3 File System Security¶
SEC-FILE-001: Template file paths validated before access
SEC-FILE-002: File paths restricted to designated template directories
SEC-FILE-003: No user input directly used in file paths
11.4 Authorization¶
SEC-AUTHZ-001: Role-based access control (if implemented in TQPRO)
SEC-AUTHZ-002: Session validation on every API call
12. Testing Requirements¶
12.1 Unit Testing¶
Test Coverage:
-
Facade methods: All public methods
-
Validation logic: All validation rules
-
Placeholder replacement: Edge cases (null, empty, special chars)
Test Framework: JUnit 5
Mocking: Mockito for EntityFacade dependencies
12.2 Integration Testing¶
Database Tests:
-
CRUD operations for all entities
-
Foreign key constraints
-
Unique constraints
-
Cascade deletes (application-level)
API Tests:
-
All endpoints with valid inputs
-
Error scenarios (missing parameters, invalid IDs)
-
Session handling
12.3 Frontend Testing¶
Manual Testing:
-
All CRUD operations via UI
-
Pagination (forward/backward, edge cases)
-
Master-detail coordination
-
HTML generation and download
-
Responsive layout on different screen sizes
Browser Compatibility:
-
Chrome (latest)
-
Firefox (latest)
-
Safari (latest)
-
Edge (latest)
12.4 End-to-End Testing¶
Scenarios:
-
Create page → Add snippets → Generate HTML → Verify output
-
Edit page → Verify snippets retained
-
Delete page → Verify snippets deleted
-
Pagination → Verify data consistency
-
Multiple users → Verify session isolation
13. Deployment and Configuration¶
13.1 Database Deployment¶
Steps:
-
Execute
doc/trip_offer_schema.sql -
Verify tables created in
ntsschema -
Insert sample skeleton and snippet file records
-
Grant appropriate permissions to application user
13.2 Application Deployment¶
Modules to Deploy:
-
tqapp.jar: Contains facades and entities -
tqapi.war: Contains REST endpoints -
tqweb-adm: Static web resources
Configuration:
-
Update
config/tourlinq-config.xmlwith entity mappings -
Verify
NTSServiceFactoryconfiguration -
Configure system session in properties
13.3 Template File Setup¶
Skeleton Templates:
-
Place HTML files in designated directory
-
Ensure files contain marker:
---INSERT SNIPPETS HERE--- -
Insert records in
nts.trip_skeletontable
Snippet Templates:
-
Place HTML template files in designated directory
-
Use
%%placeholder%%syntax for dynamic fields -
Insert records in
nts.trip_snippet_filetable
14. Appendices¶
Appendix A: Error Codes¶
| Code | Description | HTTP Status |
|------|-------------|-------------|
| MISSING_PARAMETER | Required parameter not provided | 200 (in JSON) |
| ENTITY_NOT_FOUND | Entity does not exist | 200 (in JSON) |
| GENERAL | General error (see message) | 200 (in JSON) |
Note: All errors return HTTP 200 with error details in JSON body
Appendix B: Sample Data¶
Sample Page:
INSERT INTO nts.trip_page (page_id, page_name, page_desc)
VALUES (1, 'dubai-packages.html', 'Dubai vacation packages and tour offers');
Sample Snippet:
INSERT INTO nts.trip_snippet (
snippet_id, page_id, code, title, price, active,
description, inclusionhtml
)
VALUES (
1, 1, 'DXB-PKG-001', 'Dubai Premium Package', '$599', 1,
'Explore magnificent Dubai with our premium package',
'<ul><li>4 nights accommodation</li><li>Daily breakfast</li></ul>'
);
Appendix C: File Structure¶
tqpro/
├── config/
│ └── tourlinq-config.xml (Entity mappings)
├── doc/
│ └── trip_offer_schema.sql (Database schema)
├── tqapp/src/main/java/com/perun/tlinq/
│ ├── client/nts/db/trip/ (Hibernate entities)
│ │ ├── TrippageEntity.java
│ │ ├── TripsnippetEntity.java
│ │ ├── TripskeletonEntity.java
│ │ └── TripsnippetfileEntity.java
│ └── entity/trip/ (Canonical entities & facade)
│ ├── CTripPage.java
│ ├── CTripSnippet.java
│ ├── CTripSkeleton.java
│ ├── CTripSnippetFile.java
│ └── TripOfferFacade.java
├── tqapi/src/main/java/com/perun/tlinq/api/
│ └── TripOfferApi.java (REST API)
└── tqweb-adm/
├── offerman.html (UI)
└── js/modules/
└── offerman.js (Frontend logic)
Appendix D: Placeholder Reference¶
Complete list of supported placeholders in snippet templates:
| Placeholder | Field | Example |
|-------------|-------|---------|
| %%code%% | code | DXB-PKG-001 |
| %%title%% | title | Dubai Premium Package |
| %%request_desc%% | request_desc | I would like to inquire about... |
| %%destination%% | destination | Dubai |
| %%image%% | image | images/dubai.jpg |
| %%imagetitle%% | imagetitle | Dubai Skyline |
| %%tagline1%% | tagline1 | LIMITED OFFER |
| %%tagline2%% | tagline2 | Save 20% |
| %%price%% | price | $599 per person |
| %%shortdesc%% | shortdesc | 5 Days / 4 Nights |
| %%description%% | description | Explore magnificent Dubai... |
| %%inclusionhtml%% | inclusionhtml | <ul><li>...</li></ul> |
Appendix E: Revision History¶
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | 2024-11-12 | Development Team | Initial specification based on implementation |