CuneiTable

จาก คูนิฟ็อกซ์ วิกิ

CuneiTable is designed specifically to enable in-line data entry. Design-wise, it is more limited than CuneiForm since design flexibility is not highly expected from table-rype objects. In essence, CuneiTable is a wrapper for the accompanying CuneiForm, whether that form be the in-line type or the full expansion type.

Define

Defining a CuneiTable is essentially declaring a CuneiForm with a few additional class variables. Below is an example of a very basic CuneiTable:

class AccCodeTable(CuneiTable):
    MASTER_colsizes = [10, 20, 50, 20]
    MASTER_colnames = ["StID", "Account Code", "Account Name", "Account Group"]
    MASTER_coltypes = ["int", "str", "str", "sel"]
    MASTER_choicecols = ["accgrp"]
    MASTER_choices = [[
                         ("1", "Asset"),
                         ("2", "Liability"),
                         ("3", "Equity"),
                         ("4", "Revenue"),
                         ("5", "Expense")
                     ]]
    MASTER_firstonly = ["stid"]
    class SubForm(FlaskForm, CuneiSubForm):
        stid = StringField("StID")
        code = StringField("Code", validators=[InputRequired("Required!")])
        name = StringField("Name", validators=[InputRequired("Required!")])
        accgrp = SelectField("Group", default=1)
        def validate_stid(self, stid):
            # Validation goes here

The subclass SubForm is defined like the stripped down version of CuneiForm. Only put fields and validation logics here. All other behaviours are controlled by the MASTER variables from the CuneiTable level.

NOTE THAT neither of the Submit-delete Field Set (submit, is_del, and del_submit) is declared in the SubForm. These fields are taken care of automatically during CuneiTable initiation.

The Holy Trinity of CuneiTable

The 3 MASTER variables listed below are required for all CuneiTable classes.

  • MASTER_colnames ([str,]): The list of column headers to appear on the page.
  • MASTER_coltypes ([str,]): The list of column value types. Available types include:
    • All types listed in the main article for CuneiForm.
    • 'str': Normal string value.
    • 'check': Value represented by a checkbox.
    • 'sel': Value with multiple choices. In other words, this is equivalent to a drop-list field in forms. In the same fashion, the displayed values for this type is not exactly the same as the values stored in the database. The raw value must pass through a map first before being displayed.
    • 'rad': Similar to 'sel', but this value type is represented by radio buttons in multiple sub-columns instead.
    • 'submit', 'del_submit': Submit button. (Not handled manually, they are handled automatically during initiation.)
  • MASTER_colsizes ([float/int,]): The list of relative column sizes. CuneiFox will calculate the display sizes automatically.

NOTE #1: The member order in each of these lists follow the definition order of fields in the SubForm. DO CHECK that all of these 3 lists and the SubForm fields share the same length.

NOTE #2: On the client-side, a radio field ('rad') is displayed as multiple sub-columns (one for each choice). Hence:

  • The display headers of sub-columns are not dictated by the corresponding value in MASTER_colnames, but by the values in MASTER_radios (Detailed in a sub-section below) instead.
  • The corresponding member in the MASTER_colsizes list is NOT merely one numerical value, BUT a list of numerical values instead.

Choices and Radios

Columns with limited choices of predesignated values should utilize 'choice' or 'radio' representation; especially when the stored values must be mapped to another set of more readable or localizable display values. Ideally:

  • Columns utilizing choice representation is typed 'sel' (MASTER_coltypes[i] = 'sel'), and the corresponding field in the SubForm is a SelectField.
  • Columns utilizing radio representation is typed 'rad' (MASTER_coltypes[i] = 'rad'), and the corresponding field in the SubForm is a RadioField.
MASTER_choicecols = [str choice_col0, str choice_col1, ...]
MASTER_choices    = [
                        [ (str col0_choice0, str col0_display0),
                          (str col0_choice1, str col0_display1),
                          ... ],
                        [ (str col1_choice0, str col1_display0),
                          (str col1_choice1, str col1_display1),
                          ... ],
                        ...
                    ]

MASTER_radiocols  = [str choice_col0, str choice_col1, ...]
MASTER_radios     = [
                        [ (str col0_choice0, str col0_display0),
                          (str col0_choice1, str col0_display1),
                          ... ],
                        [ (str col1_choice0, str col1_display0),
                          (str col1_choice1, str col1_display1),
                          ... ],
                        ...
                    ]

An example can be seen in the code at the top of the article.

NOTE #1: Take care that MASTER_choicecols and MASTER_choices have the same length. Similarly for MASTER_radiocols and MASTER_radios.

NOTE #2: The SelectField and RadioField defined in the SubForm DOES NOT include the choices. (Again, see example at the top.)

Search and Fill

Since the in-line form of a table is programmatically generated, the search-fill function cannot wait until the designing step like CuneiForm's does. The search-fill function of an in-line form is defined in this step via the variable MASTER_schfill.

MASTER_schfill = {
                     str search_col0: [str search_spec0, str fill_spec0],
                     str search_col1: [str search_spec1, str fill_spec1],
                     ...
                 }

# Example from Bank Account table
MASTER_schfill = {
                     "bank_code":    ["ModalBank:code",
                                      "bank_stid:stid,bank_name:name,bank_branch:branch"],
                     "acccode_code": ["ModalAccCode:code",
                                      "acccode_name:name,acccode_stid:stid"]
                 }

NOTE: The formats of search_specX and fill_specX is similar to arguments search and fill in CuneiForm's render_blank macro.

Instant Calculation

Instant calculation function within in-line forms, or 'instacalc', allows a change in one form field to initiate a small calculation request to the server and update other affected fields based on the calculation result. It is specified in the variable MASTER_instacalc of the CuneiTable.

(Refer to the dedicated sub-section in CuneiForm article for format and example.)

Auto-numbering Function

In-line forms with auto-numbering specifications work with CuneiFox core system to serve auto running numbers in a particular format (usually for document numbers). The specifications for this feature are embedded within MASTER_autorun of the CuneiTable.

(Refer to the dedicated sub-section in CuneiForm article for format and example.)

Table with a Separate Form Modal

Sometimes, the entries have either too many columns or columns not suitable for display in the table format. In such cases, developers can use the full-form option. CuneiTable with an associated full-form is only defined with essential columns for a 'quick glance' view. The full form is shown in a pop-up modal for entry addition/edit.

A CuneiTable can be linked to a full-form via the class variable MASTER_fullform which takes the following format:

MASTER_fullform = [str module_code, str fullform_class_name,
                   str fullform_modal_header_text, str modal_size]

# Example from Product table
MASTER_fullform = ['cunei_iv', 'ProductFullForm', 'Product Information', 'xl']
  • size dictates the modal size displayed on the client-side. It utilizes BootStrap 4's 'modal-<size>' classes. Available choices are 'sm', 'md' (corresponds to default modal size), 'lg', and 'xl'.

NOTES on full-form class and macro:

  • The CuneiForm class fullform_class_name must be found under module_code in the file cuneifox/<module_code>/forms.py or cuneifox/<module_code>/forms.pyc.
  • The definition of the full-form class DOES NOT include the Submit-delete Field Set
  • The Jinja2 macro for the full-form design must also be named fullform_class_name and must be found under module_code in the file cuneifox/<module_code>/template/<module_code>/form_macro.html.
  • Refer to the main CuneiForm article for more details on form definition and design. For information specific the full-form macros, refer to a dedicated sub-section below.

In-line Form with Expansion

Some tables contain just a tab too many columns, but not enough to warrant a fully-fledged separate form. In this case, CuneiTable allows its in-line form to have some fields drawn separately in an expansion modal. This behaviour is controlled by the variables MASTER_expand, MASTER_tbexpand, and MASTER_manualdraw.

MASTER_expand = {str trigger_field0: str expand_spec0,
                 str trigger_field1: str expand_spec1,
                 ...}
MASTER_tbexpand = str expand_spec
MASTER_manualdraw = [str expand_field0, str expand_field1, ...]

# Example from the product table on the 'Product Delivery' page
MASTER_expand = {"product_code_mock":"BodyEx1:product_name"}
MASTER_tbexpand = "BodyEx1:product_name"
MASTER_manualdraw = ["product_code", "product_name", "desc", "manage_serial", "serial_nos"]
  • trigger_field: The field with an Expand button '<expand_modal_id>:<first_expand_field>' attached when the form is triggered.
  • expand_spec: The modal and field to focus on once the Expand button is presses. (Shares the format argument expand in CuneiForm's render_blank macro.)
  • expand_field: The field to be skipped during dynamic in-line form generation. Fields listed in MASTER_manualdraw must be put on the page manually.

Other Table Definition Variables

Other notable MASTER variables that can be set upon form definition include:

  • MASTER_firstonly: A list of field names that are only active (editable) for a new entry, but inactive (not editable) when editing an old entry.
  • MASTER_preserve_across: A list of field names, not presented in the database, whose values are reinjected (instead re-read from the database) into the client-side table upon successful submission. (At the time of writing, this function is handled automatically only on the shortcut Flask route single_tb_page).
  • MASTER_skipcols ([str,]): A list of field names to be skipped when committing data to database. CuneiFox naturally skip fields that are meant for client-side viewing only (fields whose names are not present as database table columns). However, if the developer wants a field to be skipped despite the database having a similarly named column, do put the field name in this list.

These 3 variables share the same format:

MASTER_firstonly = [str field_name0, str field_name1, ...]

# Example from Accounting Journal's header form
MASTER_firstonly = ["docno", "section_stid", "section_code", "section_name"]


   # - MASTER_firstonly: Transfer directly to 'MASTER_firstonly' of in-line form, or appended to 'firstonly' of fullform.
   self.preserve_across = kwargs.pop("preserve_across", getattr(type(self), "MASTER_preserve_across", []))
       self.post_submit_func = kwargs.pop("post_submit_func", getattr(type(self), "MASTER_post_submit_func", None))
       self.inject_func = kwargs.pop("inject_func", getattr(type(self), "MASTER_inject_func", None))
       self.pseudohides = kwargs.pop("pseudohides", getattr(type(self), "MASTER_pseudohides", []))
       self.del_confirm = kwargs.pop("del_confirm", getattr(type(self), "MASTER_delconfirm", None))

Initiate

Design & Pre-made Scripts

Full-form Macro

Notable Sub-routines

Useful Server-side Patterns

Shortcut Route for Single-table Page