Skip to main content

RPApos Gateway — Entity Mapping

How each RPApos catalog entity is transformed and loaded into FlowPOS.

Document version: 1.0 · Last updated: 2026-05


Dependency graph​

Hard prerequisites (auto-expanded in custom sync):

flowchart TD
moneda[moneda]
uom[unidad_medida]
plu_g[plu_grupo]
sku_g[sku_grupo]
prod_g[produccion_grupo]
bodega[bodega]
proveedor[proveedor]
area[area]
persona[persona]
sku[sku]
plu[plu]
usuario[usuario]
mesa[mesa]
batch[batch_maestro]

plu_option_g[plu_option_group]
plu_option_i[plu_option_item]
plu_option_a[plu_option_group_assignment]

plu_g --> plu
uom --> plu
sku_g --> sku
uom --> sku
bodega --> area
area --> mesa
plu --> batch
sku --> batch
uom --> batch
plu --> plu_option_i
plu_option_g --> plu_option_i
plu --> plu_option_a
plu_option_g --> plu_option_a

Canonical execution order is fixed in RPAPOS_CATALOG_ENTITIES (packages/global/enums/rpapos.enums.ts).


Summary table​

EntityRPApos APISinkImport type / pathPrimary FlowPOS tables
monedaMonedaRestSink—currency, business_currency
unidad_medidaUnidadMedidaRestSink—unit_of_measure, business_unit_of_measure
plu_grupoPLU/GrupoHandlerSinkcategorycategory
sku_grupoProducto/GrupoHandlerSinkcategorycategory
produccion_grupoProduccion/GrupoHandlerSinkcategorycategory
bodegaBodegaHandlerSinklocationlocation, address fields
proveedorProveedorHandlerSinksuppliersupplier domain tables
areaareaRestSink—dining_area
personaPersonaRestSink—customer
skuSKUHandlerSinkproductproduct (is_raw_material=true)
pluPLU/MaestroHandlerSinkproductproduct (is_raw_material=false)
usuarioUsuarioHandlerSinkemployeeemployee
mesaper-area Mesa/{id}RestSink—dining_table
batch_maestroBatchMaestroRestSink—product_recipe
plu_option_groupSOAP Catalogo_4 Producto_GrupoRestSinkplu (optional)product_modifier_group
plu_option_itemSOAP Producto_Grupo_Detalle / Manager API viw_Producto_Grupo_Detalle_1 (+ viw_Producto_Atributo_1 for labels)RestSinkplu_option_group, plu (optional)product_modifier
plu_option_group_assignmentSOAP Producto_Grupo_Relacion / Manager API viw_Producto_Grupo_Relacion_1RestSinkplu, plu_option_groupproduct_modifier_group_assignment
precioPLU/Precio (REST) / viw_PLU_Precio_1——Extract/archive only (no FlowPOS load yet)
plu_option_attributeviw_Producto_Atributo_1 (Manager API)——Extract/archive only (destination table TBD)

Every successful row should also write rpapos_id_map for idempotent re-sync.

Full FlowPOS table index (catalog, orders, pricing, marketplace, RPApos source names): Menus & Modifier Groups — All related tables.


RestSink details​

Moneda → currency​

  • Match by currency_code globally; link to business via business_currency.

UnidadMedida → unit_of_measure​

  • Match by name / RPApos name / abbreviation via findUomIdFirstMatch.
  • Link via business_unit_of_measure.

Area → dining_area​

Location resolution priority:

  1. Planner targetLocationId
  2. rpapos_id_map for bodega → location
  3. Oldest location created by import_type=location
  4. Oldest location for business

Upsert key: (location_id, name); updates location_id on re-sync when mapping changes.

Mesa → dining_table​

  • Resolves area_id from rpapos_id_map (entity_type=area, external id from row).
  • Fallback: first dining area at target location (or business-wide).
  • Upsert: (area_id, table_number); id map keyed by RPA mesa external id.

Persona → customer​

With Manager API credentials the gateway joins tbl_Persona, tbl_Persona_Cuenta and Persona_Data and writes phones, addresses, billing profiles and emails through the customer aggregate. A re-run fills empty core fields and adds collection elements the customer does not already have. It does not replace a stored phone, address, billing profile or email.

Match order for that write:

  1. The live customer rpapos_id_map already points at for this persona id.
  2. A live customer whose customer_code is RPAPOS-<persona id>.
  3. Create.

An older catalog import may have stored RPApos Codigo as customer_code. The id-map match keeps that code and fills the lists onto the same row. Check one staging customer_code before a fill run: a customer that has neither an id-map row nor an RPAPOS-<id> code is created again.

The planner's People / CRM choice (all, newBatch, fillImported) limits who is written. FlowPOS still reads the three tables in full on every run. A batch does not page tbl_Persona.

Without Manager API credentials the flat catalog path still upserts by customer_code and does not carry the four lists. A batch or fill mode in that case writes nobody.

Invalid Guatemala phones, invalid emails, invalid billing ids and address values moved to "other" are counted in the run log (phonesDropped, emailsDropped, billingDroppedInvalid, addressValuesMovedToOther). A completed run can still leave a person with no phone.

BatchMaestro → product_recipe​

  • Resolves parent PLU and ingredient SKU via rpapos_id_map (plu, sku).
  • Resolves UoM via id map or first business UoM.
  • Unique per (parent_product_id, ingredient_product_id).

PluOptionGroup → product_modifier_group​

Source: SOAP Catalogo_4 pTabla=Producto_Grupo. Requires registered SOAP token.

  • Upsert keyed by rpapos_id_map(entity_type=plu_option_group, external_id=<group id>).
  • Wire PK is usually Producto_Grupo (not Id) on tenant DBs; legacy Id still accepted.
  • auto_prompt always false. IdTipoGrupo stored in raw blob only.

PluOptionItem → product_modifier​

Source: SOAP Catalogo_4 pTabla=Producto_Grupo_Detalle, or Manager API viw_Producto_Grupo_Detalle_1 when credentials are present.

  • Upsert keyed by rpapos_id_map(entity_type=plu_option_item, external_id=<detail id>).
  • Wire PK is usually Producto_Grupo_Detalle; group FK is Producto_Grupo (legacy Id / Id_Grupo still accepted).
  • modifier_group_id resolved via id map (plu_option_group, Producto_Grupo value) — hard fail if id_map empty.
  • linked_product_id resolved via id map (plu, Codigo_PLU, or numeric Producto) — soft fail (NULL) with warning if unresolved.
  • Detail rows often have no label column. Manager API: orchestrator joins viw_Producto_Atributo_1 on Producto_Atributo to copy Descripcion and Codigo_PLU onto each detalle row before transform. Without that join, names fall back to linked PLU product name, then to "{group} — opción {orden}".
  • When linked_product_id is set, quantity_per_modifier is forced to 1 (check constraint).

viw_Producto_Grupo_Relacion_1 is not modifier items — it only links sellable PLUs to modifier groups (product_modifier_group_assignment). Option labels always come from detalle + attribute master (or linked PLU name).

PluOptionGroupAssignment → product_modifier_group_assignment​

Source: SOAP Catalogo_4 pTabla=Producto_Grupo_Relacion. Requires registered SOAP token. No REST equivalent.

  • product_id resolved via id map (plu, Codigo_PLU / Id_PLU) — hard fail if missing.
  • modifier_group_id resolved via id map (plu_option_group, Producto_Grupo / Id_Grupo) — hard fail if missing.
  • Unique constraint: (product_id, modifier_group_id).
  • Pre-extraction guard: if plu id_map is empty for the business, run fails with PLU_IDMAP_EMPTY.

HandlerSink details​

Uses the same handlers as CSV import (apps/backend/src/data-import/application/handlers/).

Product (PLU / SKU)​

  • PLU: sellable product; category and UoM resolved by name; optional category id map when PLU_Grupo_Nombre missing on wire.
  • SKU: raw material flag; orchestrator may pre-create categories from SKU_Grupo_Nombre before handler runs.

Category groups​

plu_grupo, sku_grupo, produccion_grupo all map to import type category with different transformer field mappings.

Bodega → location​

Creates FlowPOS locations/warehouses; id map enables later dining area placement.

Usuario → employee​

Employee import handler; may reference locations by name.

Proveedor → supplier​

Standard supplier handler.


Metadata on rows​

Transformers attach __rpapos (and related fields) for id mapping:

{
name: "Product name",
sku: "PLU-001",
__rpapos: {
externalId: "12345",
entityType: "plu"
}
}

IdMapWriterService reads this after sink success to populate rpapos_id_map.


Product images (PLU only)​

Optional post-sync step (not a catalog entity type). Matching uses rpapos_id_map where entity_type = plu and external_id equals RPApos Codigo_PLU from SOAP Producto_U_M. Images are stored on product.image_url (GCS). Does not create new products — PLU must be imported first (or use a prior id map for images-only runs).


Quick start vs full catalog​

EntityQuick startEverything
moneda✓✓
unidad_medida✓✓
plu_grupo✓✓
sku_grupo✓✓
produccion_grupo✓
bodega✓
proveedor✓✓
area✓✓
persona✓
sku✓
plu✓✓
usuario✓✓
mesa✓✓
batch_maestro✓
plu_option_group✓✓
plu_option_item✓✓
plu_option_group_assignment✓✓

Transformers (source)​

EntityTransformer file
monedamoneda.transformer.ts
unidad_medidaunidad-medida.transformer.ts
plu_grupoplu-grupo.transformer.ts
sku_gruposku-grupo.transformer.ts
produccion_grupoproduccion-grupo.transformer.ts
bodegabodega.transformer.ts
proveedorproveedor.transformer.ts
areaarea.transformer.ts
personapersona-cliente.transformer.ts
skusku.transformer.ts
pluplu.transformer.ts
usuariousuario.transformer.ts
mesamesa.transformer.ts
batch_maestrobatch-maestro.transformer.ts
plu_option_groupplu-option-group.transformer.ts
plu_option_itemplu-option-item.transformer.ts
plu_option_group_assignmentplu-option-group-assignment.transformer.ts

Payload types: apps/backend/src/rpapos/domain/types/rpapos-payloads.types.ts