Home / Dynamic Forms System

Dynamic Forms System

Overview

SaleFlex.PyPOS uses a database-driven dynamic form system instead of TOML files. This change makes forms more flexible and manageable. All form definitions are stored in the database, allowing for dynamic form creation and modification without code changes.

Key Changes

Database Model Updates

Form Model

New fields added to the Form model:

  • fore_color: Form foreground color
  • is_startup: Boolean flag to determine the initial form to open
  • display_mode: Which screen to display on ("MAIN", "CUSTOMER", "BOTH")

FormControl Model

New fields added to the FormControl model:

  • fk_target_form_id: UUID ForeignKey to the target form when button is clicked
  • form_transition_mode: Transition mode ("MODAL" or "REPLACE")

New Classes

DynamicFormRenderer

File: user_interface/render/dynamic_renderer.py

Reads form and control definitions from the database and converts them to the appropriate format for BaseWindow.

# Usage
renderer = DynamicFormRenderer(form_id=form_id)
# or
renderer = DynamicFormRenderer(form_name='LOGIN')

# Get settings
settings = renderer.settings
toolbar_settings = renderer.toolbar_settings
design = renderer.design

Features:

  • Loads form and control data from database
  • Returns data in the same format as TOML interpreter
  • Color parsing (hex string -> integer)
  • Startup form selection

DynamicDialog

File: user_interface/window/dynamic_dialog.py

QDialog-based modal form window. Displays forms loaded from the database as modal dialogs.

# Usage (usually through Interface)
result = interface.show_modal(form_id=target_form_id)
# or
result = interface.show_modal(form_name='CUSTOMER_FORM')

Features:

  • Modal (temporary) form display
  • Works without closing the main window
  • Supports all standard controls (button, textbox, combobox, etc.)
  • Automatic cleanup

Updated Classes

Interface

Now uses database-based form rendering:

# Old usage (DEPRECATED)
interface.draw(FormName.LOGIN)
interface.redraw(FormName.SALE)

# New usage
interface.draw(form_id=form_uuid)
interface.draw(form_name='LOGIN')
interface.redraw(form_name='SALE')
interface.show_modal(form_name='CUSTOMER_FORM') # New!

CurrentStatus

Startup form support added:

# New features
current_form_id # Current form's UUID
startup_form_id # Startup form's UUID
load_startup_form() # Loads startup form from database

Application

Loads startup form from database at startup:

# During init
self.load_startup_form() # Loads from database

# During run
if self.current_form_id:
    self.interface.draw(form_id=self.current_form_id)
else:
    self.interface.draw(form_name='LOGIN') # Fallback

Form Transition System

Button-Based Form Transition

For a button in the FormControl table:

# Database example
button = FormControl(
    name="BTN_CUSTOMER",
    type="button",
    caption1="Müşteri",
    fk_target_form_id="<customer_form_uuid>",
    form_transition_mode="MODAL", # or "REPLACE"
    ...
)

Transition Modes:

  • MODAL: Form opens as a modal dialog (temporary, on top)
  • REPLACE: Current form closes, new form opens (saves RAM)

Programmatic Form Transition

# From event handler
self._navigate_to_form(
    target_form_id="<uuid>",
    transition_mode="MODAL" # or "REPLACE"
)

# Or through Interface
self.interface.show_modal(form_id="<uuid>")
self.interface.redraw(form_id="<uuid>")

Startup Form System

Setting in Database

# Mark a form as startup form
login_form = Form.get_by_name("LOGIN")
login_form.is_startup = True
login_form.save()
Important:
  • Multiple forms can have is_startup=True (accidentally)
  • System sorts by ID and uses the first one
  • If no startup form exists, searches for form with name='LOGIN'
  • If that doesn't exist, returns None as fallback

Startup Form Priority Order

  1. Forms with is_startup=True (first by ID)
  2. Form with name='LOGIN'
  3. None (error condition)

Examples

Example 1: Creating a Customer Form

from data_layer.model import Form, FormControl

# 1. Create form
customer_form = Form(
    name="CUSTOMER_FORM",
    form_no=10,
    caption="Müşteri Tanımı",
    width=800,
    height=600,
    back_color="0xFFFFFF",
    fore_color="0x000000",
    need_login=True,
    is_startup=False,
    display_mode="MAIN"
)
customer_form.save()

# 2. Add controls
# Name textbox
name_textbox = FormControl(
    name="TXT_CUSTOMER_NAME",
    fk_form_id=customer_form.id,
    type="textbox",
    caption1="Ad",
    width=300,
    height=40,
    location_x=50,
    location_y=100,
    back_color="0xFFFFFF",
    fore_color="0x000000"
)
name_textbox.save()

Example 2: Transition from Main Form to Customer Form

# Create a button in main form
customer_button = FormControl(
    name="BTN_OPEN_CUSTOMER",
    fk_form_id=main_form.id,
    type="button",
    caption1="Müşteri",
    width=150,
    height=50,
    location_x=10,
    location_y=10,
    back_color="0x0066CC",
    fore_color="0xFFFFFF",
    fk_target_form_id=customer_form.id,
    form_transition_mode="MODAL" # Open as modal
)
customer_button.save()

Known Issues and Solutions

Issue 1: Form Not Found

Symptom: "Form not found" error

Solution:

# Check if form exists
forms = Form.filter_by(name='LOGIN', is_deleted=False)
if not forms:
    print("Form not found, creating...")
    # Create form

Issue 2: Colors Not Displaying Correctly

Symptom: Colors appear black or white

Solution:

# Color format: "0xRRGGBB" must be a string
form.back_color = "0x3268A8" # Correct
# form.back_color = 0x3268A8 # Wrong (integer)
# form.back_color = "#3268A8" # Works but not recommended

Issue 3: Modal Form Not Opening

Symptom: Modal form not displaying

Solution:

# Ensure transition_mode is written correctly
control.form_transition_mode = "MODAL" # Correct (uppercase)
# control.form_transition_mode = "modal" # Works (code does upper())

Deprecated Files

Interpreter.py

DEPRECATED: Should no longer be used. Use DynamicFormRenderer instead.

The file is preserved but shows a deprecation warning.

settings.toml

The [design_files] section has been removed. Form definitions are now in the database.