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
| Entity | RPApos API | Sink | Import type / path | Primary FlowPOS tables |
|---|---|---|---|---|
moneda | Moneda | RestSink | — | currency, business_currency |
unidad_medida | UnidadMedida | RestSink | — | unit_of_measure, business_unit_of_measure |
plu_grupo | PLU/Grupo | HandlerSink | category | category |
sku_grupo | Producto/Grupo | HandlerSink | category | category |
produccion_grupo | Produccion/Grupo | HandlerSink | category | category |
bodega | Bodega | HandlerSink | location | location, address fields |
proveedor | Proveedor | HandlerSink | supplier | supplier domain tables |
area | area | RestSink | — | dining_area |
persona | Persona | RestSink | — | customer |
sku | SKU | HandlerSink | product | product (is_raw_material=true) |
plu | PLU/Maestro | HandlerSink | product | product (is_raw_material=false) |
usuario | Usuario | HandlerSink | employee | employee |
mesa | per-area Mesa/{id} | RestSink | — | dining_table |
batch_maestro | BatchMaestro | RestSink | — | product_recipe |
plu_option_group | SOAP Catalogo_4 Producto_Grupo | RestSink | plu (optional) | product_modifier_group |
plu_option_item | SOAP Producto_Grupo_Detalle / Manager API viw_Producto_Grupo_Detalle_1 (+ viw_Producto_Atributo_1 for labels) | RestSink | plu_option_group, plu (optional) | product_modifier |
plu_option_group_assignment | SOAP Producto_Grupo_Relacion / Manager API viw_Producto_Grupo_Relacion_1 | RestSink | plu, plu_option_group | product_modifier_group_assignment |
precio | PLU/Precio (REST) / viw_PLU_Precio_1 | — | — | Extract/archive only (no FlowPOS load yet) |
plu_option_attribute | viw_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_codeglobally; link to business viabusiness_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:
- Planner
targetLocationId rpapos_id_mapforbodega→location- Oldest location created by
import_type=location - Oldest location for business
Upsert key: (location_id, name); updates location_id on re-sync when mapping changes.
Mesa → dining_table
- Resolves
area_idfromrpapos_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:
- The live customer
rpapos_id_mapalready points at for this persona id. - A live customer whose
customer_codeisRPAPOS-<persona id>. - 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(notId) on tenant DBs; legacyIdstill accepted. auto_promptalwaysfalse.IdTipoGrupostored 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 isProducto_Grupo(legacyId/Id_Grupostill accepted). modifier_group_idresolved via id map (plu_option_group,Producto_Grupovalue) — hard fail if id_map empty.linked_product_idresolved via id map (plu,Codigo_PLU, or numericProducto) — soft fail (NULL) with warning if unresolved.- Detail rows often have no label column. Manager API: orchestrator joins
viw_Producto_Atributo_1onProducto_Atributoto copyDescripcionandCodigo_PLUonto 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_idis set,quantity_per_modifieris forced to1(check constraint).
viw_Producto_Grupo_Relacion_1is 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_idresolved via id map (plu,Codigo_PLU/Id_PLU) — hard fail if missing.modifier_group_idresolved 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
pluid_map is empty for the business, run fails withPLU_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_Nombremissing on wire. - SKU: raw material flag; orchestrator may pre-create categories from
SKU_Grupo_Nombrebefore 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
| Entity | Quick start | Everything |
|---|---|---|
| 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)
| Entity | Transformer file |
|---|---|
| moneda | moneda.transformer.ts |
| unidad_medida | unidad-medida.transformer.ts |
| plu_grupo | plu-grupo.transformer.ts |
| sku_grupo | sku-grupo.transformer.ts |
| produccion_grupo | produccion-grupo.transformer.ts |
| bodega | bodega.transformer.ts |
| proveedor | proveedor.transformer.ts |
| area | area.transformer.ts |
| persona | persona-cliente.transformer.ts |
| sku | sku.transformer.ts |
| plu | plu.transformer.ts |
| usuario | usuario.transformer.ts |
| mesa | mesa.transformer.ts |
| batch_maestro | batch-maestro.transformer.ts |
| plu_option_group | plu-option-group.transformer.ts |
| plu_option_item | plu-option-item.transformer.ts |
| plu_option_group_assignment | plu-option-group-assignment.transformer.ts |
Payload types: apps/backend/src/rpapos/domain/types/rpapos-payloads.types.ts