Skip to main content
The certificados_academicos flow manages the purchase of academic certificates for students and graduates. It presents a structured form, resolves pricing from a level-by-format matrix, validates catalog entries, and stores all request metadata on the WooCommerce order.

Flow overview

PropertyValue
Flow IDcertificados_academicos
ClassUTB\ProductBuilder\Flows\CertificadosFlow
Shortcodes[utb_cert_form], [utb_cert_catalog_modal]
Icondashicons-awards
AJAX endpointsutb_get_certs, utb_cert_price

Shortcodes

ShortcodeRenders
[utb_cert_form]The certificate request form (includes program selector, certificate selector, quantity, and price display).
[utb_cert_catalog_modal]A modal overlay showing the full certificate catalog.

AJAX endpoints

utb_get_certs

Returns a filtered list of certificates based on applicant type and academic level. Used by the frontend to repopulate the certificate selector when the user changes utb_tipo_cert or utb_nivel. POST parameters:
ParameterTypeDescription
tipostringApplicant type: estudiantes or egresados
nivelstringAcademic level: pregrado or posgrado
Response:
{
  "success": true,
  "data": {
    "certs": [
      {
        "id": 5,
        "nombre": "Certificado de Notas",
        "tipo_usuario": "Estudiante",
        "tipo_norm": "estudiantes",
        "descripcion": "Certificado oficial de calificaciones",
        "tiempo_expedicion": "3 días hábiles",
        "qty_enabled": "1",
        "levels": ["pregrado", "posgrado"]
      }
    ]
  }
}

utb_cert_price

Calculates the total price for a certificate selection. Requires nonce default. POST parameters:
ParameterTypeDescription
noncestringWordPress nonce (default)
cert_idintCertificate ID
formatostringdigital or fisico
nivelstringpregrado or posgrado
qtyintQuantity (minimum 1)
Response:
{
  "success": true,
  "data": {
    "price": 25000,
    "price_unit": 25000,
    "price_total": 50000,
    "formatted": "<span class=\"woocommerce-Price-amount\">$50.000</span>"
  }
}

Certificate catalog

The catalog is stored in wp_utb_certificates and managed by CertificatesRepository. Table columns:
ColumnDescription
idPrimary key
slugURL-safe identifier
nombreDisplay name
tipo_usuarioEstudiante, Egresado, or Ambos
descripcionOptional description shown in the form
skuBilling SKU reference
tiempo_expedicionEstimated delivery time shown to the applicant
qty_enabledWhether multiple copies can be ordered (boolean)
form_config_jsonOptional per-certificate form field overrides (JSON)
activoSoft-delete flag

Filtering by type and level

CertificatesRepository::get_by_tipo_and_nivel() applies a two-stage filter:
  1. SQL stage: Filters by tipo_usuario with flexible matching (egresado/egresados/ambos).
  2. PHP stage: For each certificate returned, calls CertificatePricesRepository::get_available_levels(). A certificate passes the level filter if:
    • Its price table has no explicit levels defined (agnostic/universal price), OR
    • Its price table includes an entry matching the requested level.
Do not assign a level when creating a price entry for certificates that apply to all students regardless of level. Leaving the level blank allows the certificate to appear for both Pregrado and Posgrado applicants.

Quantity support

allows_quantity() determines whether a certificate supports ordering multiple copies. By default the plugin uses by_table mode: the qty_enabled column on the certificate record controls this. The legacy by_slug_patterns mode (matching slug patterns like contenidos-programaticos or certificado-de-notas) is available as a fallback configured via utb_pb_table_config['rules']['cert_qty_enabled_mode'].

Matrix pricing

Prices live in wp_utb_certificate_prices and are managed by CertificatePricesRepository. Pricing dimensions:
DimensionValues
certificate_idForeign key to wp_utb_certificates
formatodigital or fisico
nivel_codepregrado, posgrado, general, or blank
price_copPrice in Colombian pesos
Price resolution order in get_price():
  1. Fetches all active price rows for the requested certificate_id and formato, ordering exact format matches first.
  2. If no level was requested, returns the first row.
  3. Iterates rows looking for a nivel_code of general (matches any level) or an exact normalized level match.
  4. Falls back to a general or empty nivel_code row, then to the first row.
Nivel normalization mapping:
Raw valueNormalized to
postgrado, pos-gradoposgrado
pre-gradopregrado
profesional, tecnico, tecnologia, tyt, tecnica, tecnologicapregrado
especializacion, maestria, doctoradoposgrado
general or blankapplies to any level

Programs catalog

Academic programs for the certificate form (distinct from CEP programs) are stored in wp_utb_programs and managed by ProgramsRepository. Programs can be filtered by nivel (pregrado or posgrado). The system_program_selector field type in the form config renders a <select> populated from this repository via DataSourceManager.

Managing the catalog from admin

Certificates, prices, and programs are managed through UTB Builder → Tablas Locales UTB in the WordPress admin. The admin interface exposes CRUD operations for:
  • wp_utb_certificates — certificate records with type, description, delivery time, and quantity flag.
  • wp_utb_certificate_prices — price matrix rows (certificate × format × level).
  • wp_utb_programs — academic programs linked to certificates.
After modifying certificate or price records, test the pricing by selecting the certificate in the frontend form with both Digital and Physical formats and both Pregrado and Posgrado levels to verify the matrix resolves correctly.

Default form configuration

CertificadosFlow::get_default_config() defines the canonical field schema:
[
  { "id": "section_applicant", "type": "heading", "label": "Datos del Solicitante" },
  { "id": "utb_nombre",     "name": "utb_nombre",     "type": "text",  "required": true },
  { "id": "utb_apellido",   "name": "utb_apellido",   "type": "text",  "required": true },
  { "id": "utb_tipo_doc",   "name": "utb_tipo_doc",   "type": "select", "required": true,
    "options": { "cc": "Cédula de Ciudadanía", "ce": "Cédula de Extranjería", "ti": "Tarjeta de Identidad", "pasaporte": "Pasaporte" }
  },
  { "id": "utb_documento",  "name": "utb_documento",  "type": "text",  "required": true },
  { "id": "utb_correo",     "name": "utb_correo",     "type": "email", "required": true },
  { "id": "utb_telefono",   "name": "utb_telefono",   "type": "tel",   "required": true },
  { "id": "utb_id_est",     "name": "utb_id_est",     "type": "text",  "required": true, "placeholder": "T000" },
  { "id": "section_academic", "type": "heading", "label": "Datos Académicos" },
  { "id": "utb_modalidad",   "name": "utb_modalidad", "type": "select", "required": true,
    "options": { "virtual": "Virtual", "presencial": "Presencial" }
  },
  { "id": "utb_nivel",       "name": "utb_nivel",     "type": "select", "required": true,
    "options": { "pregrado": "Pregrado", "posgrado": "Posgrado" }
  },
  { "id": "utb_programa_id", "name": "utb_programa_id", "type": "system_program_selector", "required": true },
  { "id": "section_cert_details", "type": "heading", "label": "Detalles del Certificado" },
  { "id": "utb_tipo_cert",  "name": "utb_tipo_cert", "type": "select", "required": true,
    "options": { "egresados": "Egresado", "estudiantes": "Estudiante" }
  },
  { "id": "utb_formato",    "name": "utb_formato",   "type": "select", "required": true,
    "options": { "digital": "Digital", "fisico": "Físico" }
  },
  { "id": "utb_cert_id",    "name": "utb_cert_id",   "type": "system_certificate_selector", "required": true },
  { "id": "utb_qty",        "name": "utb_qty",       "type": "number",
    "logic_binding": "cert_qty", "max_qty": 10, "start_hidden": true
  },
  { "id": "utb_monto",      "name": "utb_monto",     "type": "hidden", "show_breakdown": true },
  { "id": "utb_policies",   "name": "utb_policies",  "type": "checkbox", "required": true }
]
Configuration is loaded by FormConfigManager using the same priority chain as the CEP flow: product meta → linked certificate’s form_config_json → flow default.

Cart metadata fields

The following order item meta keys are written by prepare_cart_metadata():
Meta keySource
_utb_cert_nombreutb_nombre (form field)
_utb_cert_apellidoutb_apellido
_utb_cert_tipo_docutb_tipo_doc
_utb_cert_documentoutb_documento
_utb_cert_correoutb_correo
_utb_cert_telefonoutb_telefono
_utb_cert_id_estutb_id_est
_utb_cert_modalidadutb_modalidad
_utb_cert_idCertificate ID from wp_utb_certificates
_utb_cert_nombre_certCertificate name
_utb_cert_tipo_certutb_tipo_cert (egresados or estudiantes)
_utb_cert_formatoutb_formato (digital or fisico)
_utb_cert_nivelutb_nivel (pregrado or posgrado)
_utb_cert_qtyQuantity (minimum 1)
_utb_cert_programa_idProgram ID from wp_utb_programs
_utb_cert_programa_nombreProgram name
_utb_cert_price_unitUnit price in COP
_utb_cert_price_totalTotal price (unit × qty) in COP
_utb_cert_form_jsonFull POST data as JSON for audit purposes

Server-side validation

validate_cart_data() runs these checks before adding to the cart:
  1. All required fields are present.
  2. Email format is valid.
  3. The selected program exists in wp_utb_programs.
  4. The selected certificate exists in wp_utb_certificates.
  5. The certificate’s available price levels include the selected utb_nivel (unless the certificate has no level restrictions).
  6. The certificate’s tipo_usuario matches the applicant type (egresados/estudiantes/ambos).
  7. If quantity > 1, the certificate’s qty_enabled flag is set (or force_qty_enable is set in the field config).
  8. Quantity does not exceed max_qty (from field config or global cert_qty_max_default option, default 10).
  9. If a field has validate_role: true, the document is verified against Banner and the user’s role must match the certificate’s tipo_usuario.
  10. Policies checkbox is checked.
  11. The resolved price is greater than zero.
  12. Any RulesEngine rules defined in the form config pass.

Build docs developers (and LLMs) love