Esta es la decimosexta entrega de la serie sobre utilidades para Business Central. En esta ocasión presento una función que resuelve un problema clásico en desarrollos con documentos de venta: insertar una línea nueva entre dos líneas existentes con un número de línea válido que no colisione con las ya existentes.

¡Vamos manos a la obra! ✂️


✂️ Calcular el número de línea intermedio: SplitLine

procedure SplitLine(var SalesLine: Record "Sales Line") ReturnValue: Integer
var
    AuxSalesLine: Record "Sales Line";
    LastSalesLine: Record "Sales Line";
    Step: Integer;
    CurrLineNo: Integer;
    LastLineNo: Integer;
    NewLine: Integer;
begin
    // Paso "clásico" en BC
    Step := 10000;

    // Asegura clave por rendimiento (Document Type, Document No., Line No.)
    AuxSalesLine.Reset();
    AuxSalesLine.SetCurrentKey("Document Type", "Document No.", "Line No.");
    AuxSalesLine.SetRange("Document Type", SalesLine."Document Type");
    AuxSalesLine.SetRange("Document No.", SalesLine."Document No.");

    // 1) Si no hay líneas en el documento, primera línea = 10000
    if not AuxSalesLine.FindFirst() then
        exit(Step);

    // 2) Confirma el número de la línea "actual"
    CurrLineNo := SalesLine."Line No.";
    if CurrLineNo = 0 then begin
        // Si te pasan una línea sin número (poco común), usa la última + Step
        LastSalesLine.Copy(AuxSalesLine);
        LastSalesLine.FindLast();
        exit(LastSalesLine."Line No." + Step);
    end;

    // 3) Busca la "siguiente" estrictamente mayor que la actual
    AuxSalesLine.SetFilter("Line No.", '>%1', CurrLineNo);
    if AuxSalesLine.FindFirst() then
        LastLineNo := AuxSalesLine."Line No."
    else begin
        // No hay siguiente → devuelve última + Step
        LastSalesLine.Reset();
        LastSalesLine.SetCurrentKey("Document Type", "Document No.", "Line No.");
        LastSalesLine.SetRange("Document Type", SalesLine."Document Type");
        LastSalesLine.SetRange("Document No.", SalesLine."Document No.");
        LastSalesLine.FindLast();
        exit(LastSalesLine."Line No." + Step);
    end;

    // 4) Calcula un número intermedio seguro
    NewLine := LastLineNo - CurrLineNo;
    if NewLine > 1 then begin
        // Mitad del hueco (evita repetir el actual)
        ReturnValue := CurrLineNo + (NewLine div 2);

        // Protección adicional por si el gap era 1 (ya habría salido arriba)
        if (ReturnValue = CurrLineNo) or (ReturnValue = LastLineNo) then
            ReturnValue := LastLineNo - 1;

        // Si por cualquier motivo quedó igual, cae al fallback de última + Step
        if (ReturnValue <= CurrLineNo) or (ReturnValue >= LastLineNo) then begin
            LastSalesLine.Reset();
            LastSalesLine.SetCurrentKey("Document Type", "Document No.", "Line No.");
            LastSalesLine.SetRange("Document Type", SalesLine."Document Type");
            LastSalesLine.SetRange("Document No.", SalesLine."Document No.");
            LastSalesLine.FindLast();
            ReturnValue := LastSalesLine."Line No." + Step;
        end;
        exit(ReturnValue);
    end else begin
        // Gap <= 1 → sin espacio intermedio; usa última + Step
        LastSalesLine.Reset();
        LastSalesLine.SetCurrentKey("Document Type", "Document No.", "Line No.");
        LastSalesLine.SetRange("Document Type", SalesLine."Document Type");
        LastSalesLine.SetRange("Document No.", SalesLine."Document No.");
        LastSalesLine.FindLast();
        exit(LastSalesLine."Line No." + Step);
    end;
end;

¿Qué problema resuelve?

En Business Central, los números de línea de los documentos van en saltos de 10.000 (10000, 20000, 30000...). Cuando necesitas insertar una nueva línea justo después de una línea concreta (y no al final del documento), tienes que calcular un número de línea que:

  • Sea mayor que el de la línea actual.
  • Sea menor que el de la siguiente línea existente.
  • No colisione con ningún número existente.

SplitLine resuelve exactamente esto devolviendo el número de línea adecuado.


Explicación paso a paso

1. Caso sin líneas o línea sin número

Si el documento no tiene líneas, devuelve directamente 10000. Si la línea actual tiene número 0, busca la última línea y devuelve su número + 10000.

2. Busca la siguiente línea existente

Filtra las líneas del mismo documento con un número estrictamente mayor al de la línea actual. Si no hay ninguna (la línea actual es la última), devuelve última + 10000.

3. Calcula el punto medio

Si hay espacio entre las dos líneas (diferencia > 1), calcula el punto medio del hueco:

ReturnValue := CurrLineNo + (NewLine div 2);

Incluye protecciones adicionales para evitar que el resultado coincida accidentalmente con alguno de los extremos.

4. Sin espacio disponible

Si no hay hueco entre las líneas (gap <= 1), cae al fallback: devuelve última línea + 10000. En este caso la nueva línea irá al final del documento.


Ejemplo de uso

var
    Utils: Codeunit Utils;
    SalesLine: Record "Sales Line";
    NewSalesLine: Record "Sales Line";
    NewLineNo: Integer;
begin
    // SalesLine es la línea después de la cual insertar
    NewLineNo := Utils.SplitLine(SalesLine);

    NewSalesLine.Init();
    NewSalesLine."Document Type" := SalesLine."Document Type";
    NewSalesLine."Document No." := SalesLine."Document No.";
    NewSalesLine."Line No." := NewLineNo;
    // ... rellenar resto de campos
    NewSalesLine.Insert(true);
end;

💡 Aplicaciones prácticas

  • Dividir una línea de pedido en varias líneas más pequeñas.
  • Insertar líneas de comentario o descripción entre líneas de artículo.
  • Procesos automáticos que generan líneas adicionales basadas en líneas existentes.
  • Mantenimiento del orden visual del documento sin reordenar todas las líneas.

Conclusión

En esta decimosexta entrega de la serie Utils, muestro cómo calcular correctamente el número de línea intermedio para insertar una nueva línea de venta en Business Central sin colisionar con las existentes. Una función pequeña pero con una lógica robusta que cubre todos los casos límite.

Para seguir todos los posts de esta serie, puedes encontrarlos bajo la etiqueta #UtilsBc.

Si quieres ver el código completo, está en GitHub.

¡Nos vemos en la próxima!