Configuration and data models
VeloxFactory is built around a small set of interconnected models. Understanding them is the key to understanding everything else, from how reports are set up, to how renderings are stored, to how print jobs and mails are dispatched, to how recurring work is scheduled.
Field Naming Conventions
VeloxFactory uses two naming styles consistently throughout the system. Database columns and Laravel model attributes are always snake_case, for example broadcast_id, report_config_id, created_by_token_id. The API and frontend use camelCase for all request and response fields, the same fields become broadcastId, reportConfigId, createdByTokenId.
This split is consistent without exception: whenever you are working with the API or the frontend, use camelCase. Whenever you are looking at raw database records, migration files, or server-side model attributes, expect snake_case. Throughout this documentation, all field names and JSON examples follow the API convention: camelCase.
The Model Hierarchy
Every piece of data in VeloxFactory fits into a clear hierarchy. At the top sits the ReportConfig, the central entity. Everything else either belongs to it, describes it, or records what happened when it was used.
ReportContext ← Organisational label for grouping reports
ReportConnectionConfig ← Optional live database connection
ReportConfig ← The report template + all its metadata
├── ReportParameter ← Input values passed at render time
├── ReportField ← Output columns from the SQL query or data payload
└── ReportResource ← Graphic file asset (image, logo)
└── (links to) CommonReportResource ← Shared asset, used by reports and mails
ReportHistoryRecord ← Record of a past rendering (optional)
├── ReportPrintTask ← A print job dispatched from a history record
│ └── (uses) Printer ← Master data of the target printer
└── ReportMailTask ← A mail dispatched from a history record
├── (uses) Mailer ← SMTP account the mail is sent through
├── (uses) MailTemplate ← Subject and body of the mail
└── (embeds) CommonReportResource ← Graphics placed in the mail body
ScheduledJob ← A recurring job: a render request, or the cleanup
├── (renders) ReportConfig ← Render jobs only
├── (runs as) User + PersonalAccessToken ← Owner and token a render job authenticates with
├── (notifies via) Mailer ← Mailer the failure mail is sent through
└── ScheduledJobRun ← Record of one execution, with trace id and result
Three of these are master data: Printer, Mailer and MailTemplate are maintained once under Configuration and then picked by name whenever a rendering is printed or mailed. They exist independently of any report.
ReportContext
A context is a visual label you assign to report configurations to group and identify them at a glance. It carries no functional logic, it is purely organisational.
| Field | Description |
|---|---|
context_name |
Display name of the context |
context_description |
Short description |
context_text_color |
Hex color for the label text |
context_badge_color |
Hex color for the badge background |
context_border_color |
Hex color for the badge border |
Every ReportConfig requires a context. A single context can be shared across any number of report configurations.
ReportConnectionConfig
A connection config represents a live database connection that VeloxFactory can use as a data source when rendering a report. When assigned to a ReportConfig, VeloxFactory executes the report's SQL query against this connection at render time and feeds the result rows into the report as field data.
| Field | Description |
|---|---|
connection_name |
Friendly name for this connection |
connection_driver |
Database driver (see table below) |
connection_host |
IP address of the database server |
connection_port |
Port (required) |
connection_database |
Database / schema name |
connection_username |
Username (stored encrypted) |
connection_password |
Password (stored encrypted) |
connection_test_query |
SQL query used to verify the connection |
connection_tested |
Whether the connection has been successfully tested |
Supported drivers:
| Driver | Database |
|---|---|
mysql |
MySQL |
mariadb |
MariaDB |
pgsql |
PostgreSQL |
sqlsrv |
Microsoft SQL Server |
Connection status is derived from connection_tested:
| Status | Meaning |
|---|---|
| approved | Connection has been tested successfully |
| unapproved | Never tested or last test failed |
Network Requirements
VeloxFactory establishes the database connection directly from the server it runs on. The target database must therefore be reachable from that host, ideally within the same network or at minimum via a secured private channel.
When is a ReportConnectionConfig needed?
A ReportConnectionConfig is optional per ReportConfig. Whether you need one depends on how your report gets its data:
| Scenario | Connection needed? |
|---|---|
| Report has no detail band (purely static layout) | No |
| Report has a detail band, data delivered via API at render time | No |
| Report has a detail band and fetches data via SQL | Yes |
ReportConfig
The ReportConfig is the core entity of VeloxFactory. It represents a single JasperReports template (the .jrxml file) together with all the metadata VeloxFactory maintains about it.
| Field | Description |
|---|---|
report_name |
Display name of the report |
report_description |
Optional description |
report_file_name |
Internal filename of the stored .jrxml |
report_width |
Page width in mm (extracted from the .jrxml on upload) |
report_height |
Page height in mm (extracted from the .jrxml on upload) |
report_query |
SQL query, defined in VeloxFactory and stored in the database |
report_has_detail_band |
Whether the template contains a detail band (extracted on upload) |
report_context_id |
FK → ReportContext |
report_connection_config_id |
FK → ReportConnectionConfig (nullable) |
report_preview_base64 |
Base64-encoded preview image |
report_thumbnail_base64 |
Base64-encoded thumbnail image |
.jrxml file. It is written and managed directly in VeloxFactory and stored in the database as part of the ReportConfig. The .jrxml only defines which fields the query result maps to.
When a .jrxml file is uploaded, VeloxFactory automatically analyses it and creates the associated ReportParameter, ReportField, and ReportResource records. You then review and complete the auto-generated data, for example setting example values or uploading resource files.
ReportParameter
Parameters are the inputs passed into a report at render time: dates, IDs, filter values, flags, and so on.
| Field | Description |
|---|---|
parameter_name |
Parameter name as defined in the .jrxml |
parameter_data_type |
Java class name (e.g. java.lang.String, java.lang.Integer) |
parameter_required |
Read from the required custom property in the .jrxml |
parameter_evaluation |
Evaluation time, extracted from the .jrxml |
parameter_example_value |
Read from the exampleValue custom property in the .jrxml |
Both parameter_required and parameter_example_value are sourced from custom properties embedded in the .jrxml parameter definition. They can also be set manually in VeloxFactory after upload.
Three parameter name prefixes carry meaning beyond the render itself:
| Prefix | Meaning |
|---|---|
P_RESOURCE_ |
The parameter holds a graphic file asset, VeloxFactory creates a ReportResource for it |
P_SCAN_ |
The parameter is filled by a barcode scan in Scan2Print |
P_STATIC_ |
The parameter is preset once per Scan2Print session and stays untouched between scans |
P_SCAN_ and P_STATIC_ are what makes a report usable in Scan2Print. Everything else about the report stays the same, the prefixes are a convention, not a separate model.
ReportField
Fields represent the data columns that populate the report's detail band, either from an SQL query result or from a data array delivered at render time.
| Field | Description |
|---|---|
field_name |
Field name as defined in the .jrxml |
field_data_type |
Java class name (e.g. java.lang.String, java.math.BigDecimal) |
field_example_value |
Read from the exampleValue custom property in the .jrxml |
field_example_value is used when rendering a preview without a live database connection.
ReportResource
Resources are graphic file assets (images and logos) embedded in the report template. They are referenced in the .jrxml via parameters following the P_RESOURCE_ naming convention.
| Field | Description |
|---|---|
parameter_name |
The P_RESOURCE_ parameter name as referenced in the .jrxml |
resource_file_name |
Filename of the directly uploaded file (nullable) |
common_report_resource_id |
FK → CommonReportResource (nullable) |
A ReportResource either holds its own uploaded file or it is linked to a CommonReportResource, never both at the same time. When linking to a common resource, the resource's own file is deleted and the common file is used in its place.
CommonReportResource
A CommonReportResource is a shared graphic asset (a company logo, a standard header image) that multiple report configurations can reference. Instead of uploading the same file to each report individually, you upload it once and link individual ReportResource records to it.
| Field | Description |
|---|---|
resource_name |
Display name, unique, and at the same time the placeholder token used in a mail body |
resource_description |
Optional description |
resource_file_name |
Internal filename of the stored file |
resource_mime_type |
Detected mime type, filled on upload |
resource_width / resource_height |
Pixel dimensions, read from the file |
resource_display_width |
Optional width the graphic is rendered with in a mail |
resource_alt_text |
Alternative text for the mail graphic |
A common resource serves two purposes. In a report it is the file behind a P_RESOURCE_ parameter. In a mail template it is an inline graphic: the body references it by [image.<resource_name>], and at send time the file is embedded into the mail itself, not linked from a server. A resource is mail capable when it is a real PNG or JPEG with readable dimensions, which is why the name has to be unique.
ReportResource is linked to a CommonReportResource, its own file is permanently deleted. Unlinking removes the reference but does not restore the file, you will need to re-upload it.
ReportHistoryRecord
A ReportHistoryRecord captures the full context of a rendering: what was requested, what was returned, and whether it succeeded. Creating a history record is optional and controlled by the createHistoryRecord flag in the render request.
| Field | Description |
|---|---|
report_config_id |
FK → ReportConfig |
trace_id |
Unique identifier for this rendering run |
output_type |
How the PDF was returned (see below) |
report_api_payload |
The exact request payload sent to the render call |
report_api_response |
The full API response, stored for traceability |
report_pdf_base64 |
Base64-encoded PDF content |
report_pdf_file_name |
Filename of the PDF on disk |
report_excel_file_name |
Filename of the xlsx export on disk, written when a mailing asked for one (nullable) |
report_thumbnail_base64 |
Base64-encoded thumbnail of the first page (generated asynchronously) |
status |
Outcome of the rendering (see below) |
Output types (output_type):
| Value | Description |
|---|---|
base64 |
PDF returned inline as a Base64 string |
url |
PDF stored as a file, a URL is returned |
preview |
Rendered for preview; file is not persisted |
none |
No PDF output (used for print-only flows) |
Status values (status):
| Value | Description |
|---|---|
| ok | Rendering succeeded, PDF received |
| render_fail | No errors reported, but no PDF received |
| error | JasperReports returned one or more errors |
| unknown | Status cannot be determined |
History records are retained for a configurable number of days, set as a retention on the Purge Job. Thumbnails are generated asynchronously after rendering completes. Deleting a history record removes its PDF and its xlsx from disk together with the record.
ReportPrintTask
A ReportPrintTask represents a print job dispatched to a physical printer. It is always linked to a ReportHistoryRecord, you always print a specific past rendering, not a report config directly.
| Field | Description |
|---|---|
report_config_id |
FK → ReportConfig |
report_history_record_id |
FK → ReportHistoryRecord |
printer_id |
FK → Printer (nullable, set when the printer was picked from the master data) |
trace_id |
Unique identifier for this print run |
broadcast_id |
WebSocket channel ID for real-time status updates (nullable) |
printer_name |
Target printer name |
copies |
Number of copies to print |
output_file_name |
Filename of the PDF sent to the printer |
output_base64_string |
Base64-encoded PDF (consumed by the print service) |
error_message |
Error detail if printing failed |
status |
Current print status (see below) |
Status values (status):
| Value | Description |
|---|---|
| pending | Created, waiting for the print service |
| printed | Successfully printed and confirmed |
| error | Printing failed |
| unknown | Status cannot be determined |
broadcastId is provided in the render request. The C# print service subscribes to that channel, picks up the task, executes the print job, and reports status back. Without a broadcastId, the task is created silently, the print service must poll for new tasks.
Printer
A Printer is master data for a physical printer. Without it, every print request had to carry the exact queue name of the print server, and everyone had to know it by heart. With it, a printer is configured once and then picked from a list.
| Field | Description |
|---|---|
printer_display_name |
The name people see, for example Warehouse Label 01. Unique, 3 to 50 characters |
printer_name |
The queue name on the print server, for example WH-LABEL-01. This is what the print service receives. Unique |
printer_type |
label-printer, a4-printer, mfc-printer or digital-printer. Descriptive, it drives icon and filter |
printer_location |
Where the machine stands, for example Hall 2, Shipping (nullable) |
printer_broadcast_id |
WebSocket channel of the print service instance that serves this printer (nullable) |
printer_description |
Free note, for example the loaded media (nullable) |
printer_is_active |
Inactive printers keep working in existing configurations but are no longer offered in pickers |
A render request may name either the display name or the queue name, VeloxFactory resolves both. A name that matches no master data record is still accepted and passed through unchanged, so integrations written before the master data existed keep working.
Mailer
A Mailer is master data for one SMTP account. It is to mailing what a ReportConnectionConfig is to data: the credentials live in one place, are tested from the UI, and are then referenced by name.
| Field | Description |
|---|---|
mailer_name |
Unique name used to reference the mailer in a request |
mailer_transport |
Transport, smtp by default |
mailer_host / mailer_port |
SMTP server and port |
mailer_scheme |
smtp, smtps or empty for the transport default |
mailer_username / mailer_password |
Credentials, stored encrypted |
mailer_from_address / mailer_from_name |
Sender of every mail sent through this mailer |
mailer_reply_to |
Optional reply-to address |
mailer_timeout / mailer_local_domain |
Optional transport details |
mailer_rate_limit_per_minute |
Max mails per minute, empty means the window is not enforced |
mailer_rate_limit_per_hour |
Max mails per hour, 500 by default |
mailer_rate_limit_per_day |
Max mails per day, empty means the window is not enforced |
mailer_is_active |
Inactive mailers are not offered and are refused by the API |
mailer_tested |
Whether a test mail has been sent successfully |
mailer_description |
Free note |
The global MAIL_* variables of Laravel are not used by the mailing pipeline. Every mail goes through a Mailer record, registered as a runtime mail connection for the duration of the send and removed again afterwards, exactly the way a ReportConnectionConfig handles its database connection.
MailTemplate
A MailTemplate holds the subject and the body of a mail, both written once and filled with placeholders at send time.
| Field | Description |
|---|---|
mail_template_name |
Unique name used to reference the template in a request |
mail_template_subject |
Subject line, placeholders allowed |
mail_template_body_html |
HTML body, written in a rich text editor, placeholders allowed |
mail_template_header_color |
Optional override of the header bar colour |
mail_template_body_color |
Optional override of the body background |
mail_template_hide_logo |
Hides the logo in the header |
mail_template_logo_resource_id |
FK → CommonReportResource, an own logo for this template (nullable) |
mail_template_footer_text |
Optional override of the global footer text, placeholders allowed |
mail_template_is_active |
Inactive templates are not offered and are refused by the API |
mail_template_description |
Free note |
Placeholders use the [token] syntax and cover the render ([traceId], [reportFileName]), the report, the history record, the user, the API token, the printer, every parameter ([parameters.<NAME>]), the data rows ([data.first.<FIELD>]), the current time ([now.date]), the recipients and the graphics ([image.<RESOURCE_NAME>]). The full catalog is available in the editor. An unknown token resolves to an empty string and is reported, it never fails a mail.
The four whitelabel fields are each a nullable override of a global default. Left empty, the mail uses the theme colours read from the application's own stylesheet, so a mail looks like the application without anything being configured twice.
ReportMailTask
A ReportMailTask represents one mail. Like a ReportPrintTask it belongs to a ReportHistoryRecord, you always mail a specific past rendering.
| Field | Description |
|---|---|
report_history_record_id |
FK → ReportHistoryRecord (nullable if no history record was created) |
mailer_id |
FK → Mailer |
mail_template_id |
FK → MailTemplate |
trace_id |
Unique identifier for this mail run |
recipients_to / recipients_cc / recipients_bcc |
Resolved address lists |
mail_subject |
The rendered subject, placeholders already resolved |
mail_body_html |
The rendered body, stored so the UI can show what was actually sent |
attachment_pdf_file_name |
The PDF on the history disk, always <trace id>.pdf |
attachment_excel_file_name |
The xlsx on the history disk, always <trace id>.xlsx |
attachment_pdf_name |
The name the PDF carries as an attachment of the mail |
attachment_excel_name |
The name the xlsx carries as an attachment of the mail |
send_async |
Whether the mail was queued instead of sent synchronously |
dispatch_after |
Set when the rate limit pushed the send into the future |
sent_at |
Timestamp of the successful send |
error_message |
Error detail if sending failed |
status |
Current mail status (see below) |
Status values (status):
| Value | Description |
|---|---|
| pending | Created, not sent yet, or waiting for a rate limit window |
| sent | Handed over to the SMTP server successfully |
| error | Sending failed |
| unknown | Status cannot be determined |
ScheduledJob
A ScheduledJob is one recurring job: either a render request that fires on a crontab expression, or the consolidated cleanup that keeps the database and the disk from growing without bound. Both live in the same table and differ in their type and their payload.
| Field | Description |
|---|---|
schedule_name |
Display name, unique |
schedule_description |
Optional description |
schedule_type |
render or purge |
schedule_cron |
Five-field crontab expression. Empty means the schedule never fires on its own and can only be run manually |
schedule_timezone |
Timezone the expression is evaluated in. Empty means the application timezone |
schedule_is_active |
An inactive schedule is never dispatched |
report_config_id |
FK → ReportConfig, render jobs only |
schedule_payload |
JSON. Render jobs: the render request body. Purge jobs: the retention map |
owner_user_id |
FK → User, the user a render job runs as |
owner_token_id |
FK → PersonalAccessToken, the token a render job authenticates with |
error_mailer_id |
FK → Mailer, the mailer the failure mail goes through (nullable) |
error_recipients_to / error_recipients_cc / error_recipients_bcc |
Address lists of the failure mail |
schedule_last_run_at |
When the schedule last fired |
schedule_next_run_at |
When it fires next. Indexed, this is what the minute tick selects on |
schedule_last_status |
Status of the most recent run |
A purge job carries its retentions in schedule_payload, one key per step: printTaskDays, mailTaskDays, historyDays, orphanedFileDays and runLogDays. A key set to null switches its step off.
ScheduledJobRun
A ScheduledJobRun is the record of one execution. It belongs to its schedule and is deleted with it.
| Field | Description |
|---|---|
scheduled_job_id |
FK → ScheduledJob, cascading delete |
trace_id |
Identifier of this run, the same id the rendering and its mail carry |
status |
Outcome of the run (see below) |
trigger |
schedule for a run the cron fired, manual for Run now |
started_at / finished_at / duration_ms |
Timing of the run |
run_as_user_id |
The user the run authenticated as, kept even after the schedule's owner changed |
report_history_record_id |
FK → ReportHistoryRecord the run produced (nullable) |
response_status |
HTTP status the internal render call answered with |
result_summary |
JSON summary: what a render returned, or how many records each purge step deleted |
error_message |
Error detail if the run failed |
error_mail_sent |
Whether a failure mail went out for this run |
error_mail_suppressed |
How many failure mails the throttle held back |
Status values (status):
| Value | Description |
|---|---|
| running | The run started and has not finished yet |
| success | Everything the run was asked to do went through |
| warning | The main work succeeded, something attached to it did not, for example the mail |
| error | The run failed |
| skipped | The run was not executed, for example because the owner is inactive or the token revoked |
report_history_record_id is nulled when the record is deleted, so the history retention stays free to clean up what a run produced. The run log itself is cleaned up by the Purge Job.
Audit Trail
Every model in VeloxFactory tracks who created and last updated a record, and which API token was used. This information is available on all records via the withAudit=true query parameter in the API.
| Field | Description |
|---|---|
created_at |
Timestamp of creation |
created_by |
User ID of the creator |
created_by_token_id |
API token ID used (if created via API) |
updated_at |
Timestamp of last update |
updated_by |
User ID of the last updater |
updated_by_token_id |
API token ID used (if updated via API) |
The creationMethod and updateMethod fields in the API response ("Frontend" vs. "API") are derived automatically, based on whether a token was present on the request.
Environment Configuration
VeloxFactory's runtime behaviour is controlled via environment variables in .env or as container environment variables.
Application
| Variable | Default | Description |
|---|---|---|
APP_SCHEME |
http |
URL scheme for generated links (http or https) |
API_RATE_LIMIT_PER_MINUTE |
10 |
Max API requests per minute per token |
PAGINATION_DEFAULT_COUNT |
25 |
Default number of results per API response |
Queue & Redis
| Variable | Default | Description |
|---|---|---|
QUEUE_CONNECTION |
database |
Queue driver (must be set to redis for Horizon) |
REDIS_CLIENT |
phpredis |
Redis client library (phpredis required) |
REDIS_HOST |
127.0.0.1 |
Redis server hostname or IP |
REDIS_PORT |
6379 |
Redis server port |
REDIS_PASSWORD |
null |
Redis password (leave null if not set) |
REDIS_QUEUE_RETRY_AFTER |
360 |
Seconds before a reserved job on the redis connection is handed to another worker. Must stay above the longest worker timeout |
REDIS_SCHEDULER_QUEUE_RETRY_AFTER |
1860 |
The same for the redis-scheduler connection, which carries the long-running scheduled jobs |
Horizon
| Variable | Default | Description |
|---|---|---|
HORIZON_PATH |
horizon |
URL path for the Horizon dashboard |
HORIZON_PREFIX |
derived from APP_NAME |
Redis key prefix for all Horizon data |
HORIZON_DOMAIN |
- | Optional custom domain for the Horizon dashboard |
Job Scheduler
| Variable | Default | Description |
|---|---|---|
SCHEDULER_CATCHUP_GRACE_MINUTES |
60 |
A due run older than this is dropped instead of fired late, for example after the scheduler process was down |
SCHEDULER_RUN_LOCK_SECONDS |
1800 |
How long one run may hold its schedule's lock before another run may start |
SCHEDULER_ERROR_MAIL_THROTTLE_MINUTES |
60 |
At most one failure mail per schedule within this window |
SCHEDULER_PREVIEW_COUNT |
5 |
How many upcoming run dates the cron preview shows |
PURGE_SCHEDULER_RUNS_DAYS |
90 |
Retention of the run log, written into the Purge Job when it is created |
Mailing
| Variable | Default | Description |
|---|---|---|
MAIL_LOGO_RESOURCE |
- | Name of a mail capable CommonReportResource, embedded inline in the mail header |
MAIL_LOGO_URL |
- | Fallback used when MAIL_LOGO_RESOURCE is empty: an absolute, publicly reachable URL. Empty falls back to the application name as text |
MAIL_FOOTER_TEXT |
Sent automatically by VeloxFactory. |
Footer of every mail, overridable per template |
MAIL_RATE_LIMIT_PER_MINUTE |
0 |
Default minute limit for a newly created mailer. 0 means not enforced. |
MAIL_RATE_LIMIT_PER_HOUR |
500 |
Default hour limit for a newly created mailer |
MAIL_RATE_LIMIT_PER_DAY |
0 |
Default day limit for a newly created mailer |
MAIL_THROTTLE_FALLBACK_TO_QUEUE |
true |
A throttled synchronous send is queued instead of answered with an error |
MAIL_MAX_ASSET_SIZE_KB |
2048 |
Maximum size of a graphic that can be embedded into a mail |
The standard Laravel MAIL_* keys stay untouched. They are the framework fallback and are not used for report mails.
Retention & Purge
The retentions themselves live on the Purge Job, where they are edited in the frontend or through the API. These variables supply the values the Purge Job is created with, which makes them the right place to preconfigure an instance before it is set up. Changing one afterwards has no effect on an existing schedule.
| Variable | Default | Description |
|---|---|---|
PURGE_HISTORY_DAYS |
30 |
Age in days after which history records are deleted. -1 creates the step switched off. |
PURGE_PRINTTASKS_DAYS |
30 |
Age in days after which print tasks are deleted. -1 creates the step switched off. |
PURGE_MAILTASKS_DAYS |
30 |
Age in days after which mail tasks are deleted. -1 creates the step switched off. |
PURGE_ORPHANED_FILES_DAYS |
30 |
Age in days after which orphaned files on disk are deleted. -1 creates the step switched off. |