| Subsistema | Codeunit estándar | Uso principal |
|---|---|---|
| Motor de Workflows | Workflow Management, Workflow Setup, Workflow Event Handling | Definir eventos, respuestas y plantillas de flujo |
| Módulo de Approvals | Approvals Mgmt. | Crear solicitudes de aprobación y gestionar aprobadores |
| Respuestas de Workflow | Workflow Response Handling | Ejecutar acciones cuando el flujo avanza |
La integración gira alrededor del patrón publisher / subscriber.
Nosotros publicamos nuestros propios eventos, como “enviar Cuenta Estadística a aprobación”, y después nos suscribimos a eventos estándar de Business Central para indicarle cómo debe tratar nuestra tabla.
case RecRef.Number of
Database::"Statistical Account":
// Aquí va nuestra lógica
end;Ese case es fundamental. Business Central lanza muchos eventos de forma genérica, pero nosotros solo queremos reaccionar cuando el registro pertenece a nuestra tabla.
Los objetos principales que necesitaremos son:
| Objeto | Uso |
Enum BCS Stat. Acc. Appr. Status | Define los estados del flujo |
Table Extension BCS Statistical Account | Añade el campo de estado y reglas de bloqueo |
Codeunit BCS Stat. Acc. Approval Mgmt. | Contiene la lógica de aprobación |
Codeunit BCS Stat. Acc. Workflow Setup | Registra categoría, plantilla y relaciones |
| Page Extensions | Añaden botones, colores y control de edición |
Paso 1 - Crear el Enum de estado
Lo primero es definir los posibles estados de aprobación.
enum 60702 "BCS Stat. Acc. Appr. Status"
{
Extensible = true;
value(0; "Open") { Caption = 'Open'; }
value(1; "Pending Approval") { Caption = 'Pending Approval'; }
value(2; "Rejected") { Caption = 'Rejected'; }
value(3; "Approved") { Caption = 'Approved'; }
}Este enum actúa como la fuente de verdad del ciclo de vida del registro.
Lo dejamos como Extensible = true por si más adelante necesitamos estados adicionales (aunque es mas improbable que alguien lo extienda de lo que seria que nos den un mes más de vacaciones de gratis).
Paso 2 - Añadir el campo a la tabla
Después añadimos el campo de estado a la tabla mediante una extensión.
field(60701; "BCS Approval Status"; Enum "BCS Stat. Acc. Appr. Status")
{
Caption = 'Approval Status';
DataClassification = CustomerContent;
Editable = false; // el usuario no lo modifica manualmente
}El campo no debería ser editable directamente por el usuario. Lo moverá el flujo de aprobación.
Paso 3 - Proteger el registro
Importante que controlemos el bloqueo de modificación y borrado según estado
trigger OnBeforeDelete()
var
ApprovalsMgmt: Codeunit "Approvals Mgmt.";
begin
BCSCheckApprovalStatus();
ApprovalsMgmt.DeleteApprovalEntries(Rec.RecordId);
BCSAttachmentManagement.DeleteRelatedDocumentAttachments(Rec."No.");
end;
local procedure BCSCheckApprovalStatus()
begin
if Rec."BCS Approval Status" in
[Rec."BCS Approval Status"::Approved,
Rec."BCS Approval Status"::"Pending Approval"]
then
Error(StatusErr, Rec."BCS Approval Status");
end;
procedure ApprovalStatusAllowModify(): Boolean
begin
exit(not (Rec."BCS Approval Status" in
[Rec."BCS Approval Status"::Approved,
Rec."BCS Approval Status"::"Pending Approval"]));
end;Este bloque evita que alguien borre o modifique un registro que está pendiente o aprobado.
Además, al borrar un registro permitido, se limpian las entradas de aprobación relacionadas para evitar basura funcional.
Paso 4 - Publicar eventos propios
Vamos a necesitar crear eventos propios de aprobación
[IntegrationEvent(false, false)]
procedure OnSendStatAccForApproval(var StatisticalAccount: Record "Statistical Account")
begin
end;
[IntegrationEvent(false, false)]
procedure OnCancelStatAccApprovalRequest(var StatisticalAccount: Record "Statistical Account")
begin
end;Estos eventos serán invocados desde las acciones de la página.
La ventaja de usar eventos es que dejamos la solución abierta a extensiones futuras. Otro módulo podría suscribirse a estos eventos sin tocar nuestro código.
Paso 5 - Validar que existe un workflow activo
Antes de enviar el registro a aprobación, conviene comprobar que realmente hay un flujo configurado.
procedure CheckStatAccApprovalPossible(var StatisticalAccount: Record "Statistical Account"): Boolean
begin
if not IsStatAccApprovalWorkflowEnabled(StatisticalAccount) then
Error(NoWorkflowEnabledErr);
exit(true);
end;
procedure RunWorkflowOnSendStatAccForApprovalCode(): Code[128]
begin
exit(UpperCase('RunWorkflowOnSendStatAccForApproval'));
end;Esto evita la típica situación de “he pulsado el botón y no ha pasado nada”.
Si no hay workflow habilitado, mostramos un error claro.
Paso 6 - Conectar nuestro evento con el motor de Workflows
Este es nuestro puente entre evento custom y workflow estándar
[EventSubscriber(ObjectType::Codeunit, Codeunit::"BCS Stat. Acc. Approval Mgmt.",
'OnSendStatAccForApproval', '', true, true)]
local procedure RunWorkflowOnSendStatAccForApproval(var StatisticalAccount: Record "Statistical Account")
begin
WorkflowManagement.HandleEvent(
RunWorkflowOnSendStatAccForApprovalCode(), StatisticalAccount);
end;Este subscriber traduce nuestro evento custom a un evento que el motor estándar de Workflows entiende.
A partir de aquí, Business Central ya puede arrancar el flujo configurado.
Paso 7 - Registrar eventos en la biblioteca de workflows
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Workflow Event Handling",
'OnAddWorkflowEventsToLibrary', '', false, false)]
local procedure OnAddWorkflowEventsToLibrary()
begin
WorkflowEventHandling.AddEventToLibrary(
RunWorkflowOnSendStatAccForApprovalCode(),
Database::"Statistical Account", SendForApprovalEventDescTxt, 0, false);
end;Esto permite que nuestros eventos aparezcan en la configuración estándar de Workflows (y si no aparecen busca el boton de "Reset Microsoft Templates" en la página Workflow Templates).
Es decir, un consultor podrá seleccionarlos desde la interfaz de Business Central igual que haría con eventos estándar.
Paso 8 - Rellenar la entrada de aprobación
También debemos de poblar registros de Approval Entry para una tabla custom
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Approvals Mgmt.",
'OnPopulateApprovalEntryArgument', '', false, false)]
local procedure OnPopulateApprovalEntryArgument(
var ApprovalEntryArgument: Record "Approval Entry"; var RecRef: RecordRef;
WorkflowStepInstance: Record "Workflow Step Instance")
var
StatisticalAccount: Record "Statistical Account";
begin
case RecRef.Number of
Database::"Statistical Account":
begin
RecRef.SetTable(StatisticalAccount);
ApprovalEntryArgument."Document No." := StatisticalAccount."No.";
ApprovalEntryArgument."Table ID" := Database::"Statistical Account";
end;
end;
end;Cuando Business Central crea una solicitud de aprobación, necesita saber qué datos mostrar.
Aquí le indicamos que el número de documento debe ser el No. de la Cuenta Estadística.
Paso 9 - Actualizar el estado según avance el flujo
El campo BCS Approval Status debe sincronizarse con las acciones reales del workflow.
| Evento | Estado resultante |
| Enviar a aprobación | Pending Approval |
| Aprobar | Approved |
| Rechazar | Rejected |
| Cancelar / reabrir | Open |
El detalle importante está en la aprobación: solo deberíamos marcar como Approved cuando no queden aprobadores pendientes.
Esto permite soportar flujos con varias personas aprobando en cadena.
Paso 10 - Resolver la Card Page del registro
Ajustaremos código para redirigir a la página de ficha para una tabla custom
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Page Management",
'OnConditionalCardPageIDNotFound', '', true, true)]
local procedure OnConditionalCardPageIDNotFound(RecordRef: RecordRef; var CardPageID: Integer)
begin
case RecordRef.Number of
Database::"Statistical Account":
CardPageID := Page::"Statistical Account Card";
end;
end;Esto permite que, desde la solicitud de aprobación, el aprobador pueda abrir directamente la ficha correcta del registro.
Sin este subscriber, Business Central puede no saber qué página debe abrir para nuestra tabla custom.
Paso 11 - Registrar relaciones de tabla
Crearemos relaciones entre tabla custom y Approval Entry
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Workflow Setup",
'OnAfterInsertApprovalsTableRelations', '', true, true)]
local procedure OnAfterInsertApprovalsTableRelations()
var
ApprovalEntry: Record "Approval Entry";
StatisticalAccount: Record "Statistical Account";
WorkflowWebhookEntry: Record "Workflow Webhook Entry";
begin
WorkflowSetup.InsertTableRelation(
Database::"Statistical Account", 0,
Database::"Approval Entry", ApprovalEntry.FieldNo("Record ID to Approve"));
WorkflowSetup.InsertTableRelation(
Database::"Statistical Account", StatisticalAccount.FieldNo("SystemId"),
Database::"Workflow Webhook Entry", WorkflowWebhookEntry.FieldNo("Data ID"));
end;Este bloque suele ser uno de los más importantes y también uno de los más fáciles de olvidar.
Sin estas relaciones, Business Central puede crear aprobaciones, pero no enlazarlas correctamente con el registro original.
Paso 12 - Añadir acciones y colores en página
Crearemos las acciones de enviar/cancelar aprobación
action("BCS BCS_SendApprovalRequest")
{
Caption = 'Send Approval Request';
Enabled = not OpenApprovalEntriesExist;
trigger OnAction()
var
BCSApprovalMgmt: Codeunit "BCS Stat. Acc. Approval Mgmt.";
begin
if BCSApprovalMgmt.CheckStatAccApprovalPossible(Rec) then
BCSApprovalMgmt.OnSendStatAccForApproval(Rec);
CurrPage.Update(false);
end;
}Y para el semáforo visual:
trigger OnAfterGetRecord()
begin
OpenApprovalEntriesExist := ApprovalsMgmt.HasOpenApprovalEntries(Rec.RecordId);
CanCancelApprovalForRecord := ApprovalsMgmt.CanCancelApprovalForRecord(Rec.RecordId);
BCSSetApprovalStatusStyle();
GPageEditable := Rec.ApprovalStatusAllowModify();
end;
local procedure BCSSetApprovalStatusStyle()
begin
case Rec."BCS Approval Status" of
Rec."BCS Approval Status"::Open: BCSApprovalStatusStyleTxt := 'Standard';
Rec."BCS Approval Status"::"Pending Approval": BCSApprovalStatusStyleTxt := 'Ambiguous';
Rec."BCS Approval Status"::Approved: BCSApprovalStatusStyleTxt := 'Favorable';
Rec."BCS Approval Status"::Rejected: BCSApprovalStatusStyleTxt := 'Unfavorable';
end;
end;Este bloque mejora muchísimo la experiencia de usuario.
El usuario entiende de un vistazo si el registro está abierto, pendiente, aprobado o rechazado.
- El usuario abre una Cuenta Estadística.
- Pulsa Enviar solicitud de aprobación.
- La página valida que exista un workflow activo.
- Se publica el evento custom.
- El subscriber lo convierte en un evento del motor de Workflows.
- Business Central crea la entrada de aprobación.
- El estado cambia a Pending Approval.
- El registro queda bloqueado para edición y borrado.
- El aprobador recibe la solicitud en su bandeja estándar.
- El aprobador abre la ficha del registro.
- Si aprueba, el estado pasa a Approved.
- Si rechaza, el estado pasa a Rejected.
- Si se cancela la solicitud, el estado vuelve a Open.
Lo más importante es que no hemos creado una pantalla nueva de aprobaciones.
Estamos reutilizando la experiencia estándar de Business Central:
- Bandeja estándar de aprobaciones.
- Eventos estándar de workflow.
- Respuestas estándar.
- Integración con Power Automate.
- Comportamiento consistente con documentos estándar.
Lo que hacemos es enseñarle al motor estándar de Business Central que nuestra tabla custom existe y que debe tratarla como un documento aprobable.
El patrón que has podido ver se puede reutilizar en muchas otras tablas personalizadas.
Si quieres probar a reutilizarlo a través de Github Copilot con skills siéntete libre de probar AL Copilot Skills Collection, donde tienes esta skill y muchas más para implementar.