Issue with delta values for reduced delta in case business rule triggered

I have an issue with the delta set that gets passed to the Update RO call

After a first ApplyUpdates which fails due to a server side business rule it looks like the fields are scrambled a bit

In my TProductDetailServerRules.BeforeProcessChange

I throw this exception raise OffDAUpdateException.Create(daerrormsg);

This gets handled on the client side via fBusinessRules.OnAfterMergeDelta

in which my code visualizes the issue in case delta’s still exist.

if Assigned (DataTable) and Assigned (DataTable.Delta) and
(DataTable.Delta.Count <> 0) then

If after this I do not reload (reset) the dataset and try ApplyUpdates again (but with one modified field so the business rule check passes)

The server throws an error which looks like the (old) delta values are totally mixed up

The 2 screenshots below is the delta info passed back to the client.

I notice a currency field prd_aankoopprijs is claimed to have old value ‘A’ (which is in fact probably just the value of another field)

Any idea what could cause this? How this can be prevented?

This is the code I use to visualize

Hi,

try to use NewValueByName[] instead of just NewValue[] as we do in BP:

          for i := 0 to (aDelta.LoggedFieldCount - 1) do begin
            remotename := aDelta.LoggedFieldNames[i];

..
            if lReducesDeltaMode and ROVariantsEqual(aChange.OldValueByName[remotename], aChange.NewValueByName[remotename]) and (lPKList.IndexOf(remotename) = -1) then Continue;
...
          end;

it may solve your issue with

Hi Evgeny,

It is not a visualization issue.

The server actually complains that the update cannot be performed.

For now due to a readonly field (but LogChanges still true) which he think has changed.

I’ll change the LogChanges property but I believe he will complain about other fields or incompatible types

FYI: After making the LogChanges adjustment no error occurs.

However I do not feel very confident that everything is OK.

I believe too many fields are set (cleared) in the DB

A change of one field results in

UPDATE Product SET “prd_DateChanged”= :prd_DateChanged, “prd_CNK”= :prd_CNK WHERE (“prd_PrimKey”=:OLD_prd_PrimKey) params: :prd_DateChanged=18/03/2026 10:56:12 :prd_CNK=0901057 :OLD_prd_PrimKey=100000643

After the bizrule trigger and correction it results in

UPDATE Product SET “prd_DateChanged”= :prd_DateChanged, “prd_UserId”= :prd_UserId, “prd_atc_primkey”= :prd_atc_primkey, “prd_atc_code”= :prd_atc_code, “prd_CNK”= :prd_CNK, “prd_Groothandelsproduct”= :prd_Groothandelsproduct,
“prd_DatumOpMarkt”= :prd_DatumOpMarkt, “prd_DatumSchrapping”= :prd_DatumSchrapping, “prd_CommercieleStatus”= :prd_CommercieleStatus, “prd_StatusWetgeving”= :prd_StatusWetgeving, “prd_CategorieAPB”= :prd_CategorieAPB,
“prd_CategorieOfficinall”= :prd_CategorieOfficinall, “prd_Gebruik”= :prd_Gebruik, “prd_Wetgeving”= :prd_Wetgeving, “prd_CodeDopingFrGem”= :prd_CodeDopingFrGem, “prd_CodeDopingVlGem”= :prd_CodeDopingVlGem,
“prd_ptv_primkey”= :prd_ptv_primkey, “prd_Bereiding”= :prd_Bereiding, “prd_Bewaartemperatuur”= :prd_Bewaartemperatuur, “prd_HeeftBarcode”= :prd_HeeftBarcode, “prd_LabelAPB”= :prd_LabelAPB, “prd_Steriel”= :prd_Steriel,
“prd_CodeVerdovingsmiddel”= :prd_CodeVerdovingsmiddel, “prd_Hospitaalverpakking”= :prd_Hospitaalverpakking, “prd_EnkelTBTInHospitaal”= :prd_EnkelTBTInHospitaal, “prd_IsVoorschriftPlichtig”= :prd_IsVoorschriftPlichtig,
“prd_IsImport”= :prd_IsImport, “prd_IsVerdoving”= :prd_IsVerdoving, “prd_IsVeterinair”= :prd_IsVeterinair, “prd_CodeUnidosis”= :prd_CodeUnidosis, “prd_Originaliteit”= :prd_Originaliteit,
“prd_HoeveelheidPerVerpakking”= :prd_HoeveelheidPerVerpakking, “prd_AantalIEInsulPerVerpakking”= :prd_AantalIEInsulPerVerpakking, “prd_GeldigheidsdatumPrijsInfo”= :prd_GeldigheidsdatumPrijsInfo,
“prd_EigenAankoopPrijs”= :prd_EigenAankoopPrijs, “prd_Publieksprijs”= :prd_Publieksprijs, “prd_VorigePublieksprijs”= :prd_VorigePublieksprijs, “prd_EigenPrijs”= :prd_EigenPrijs, “prd_Richtprijs”= :prd_Richtprijs,
“prd_BTWCode”= :prd_BTWCode, “prd_IsTBTHomeopathie”= :prd_IsTBTHomeopathie, “prd_Terugbetalingsbasis”= :prd_Terugbetalingsbasis, “prd_CodeFiscaalForfait”= :prd_CodeFiscaalForfait, “prd_IBCode”= :prd_IBCode,
“prd_MinVoorraad”= :prd_MinVoorraad, “prd_MaxVoorraad”= :prd_MaxVoorraad, “prd_AantalTeBestellen”= :prd_AantalTeBestellen, “prd_AutomatischBestellen”= :prd_AutomatischBestellen, “prd_sbs_Primkey”= :prd_sbs_Primkey,
“prd_sbs_Status”= :prd_sbs_Status, “prd_sbs_LaatstGetriggerd”= :prd_sbs_LaatstGetriggerd, “prd_lev_Primkey”= :prd_lev_Primkey, “prd_korting”= :prd_korting, “prd_lok_Primkey”= :prd_lok_Primkey,
“prd_Geldigheidsdatum”= :prd_Geldigheidsdatum, “prd_pgs_primkey”= :prd_pgs_primkey, “prd_pga_primkey”= :prd_pga_primkey, “prd_Dynaphar”= :prd_Dynaphar, “prd_IsSchriftelijkeAanvraag”= :prd_IsSchriftelijkeAanvraag,
“prd_Hernieuwing”= :prd_Hernieuwing, “prd_HernieuwingCyclisch”= :prd_HernieuwingCyclisch, “prd_HernieuwingMaanden”= :prd_HernieuwingMaanden, “prd_HernieuwingAantal”= :prd_HernieuwingAantal,
“prd_IsGeneriek”= :prd_IsGeneriek, “prd_HeeftAstmatomInfo”= :prd_HeeftAstmatomInfo, “prd_HeeftEersteAflevering”= :prd_HeeftEersteAflevering, “prd_IsJongereProduct”= :prd_IsJongereProduct, “prd_DPP”= :prd_DPP,
“prd_DDD”= :prd_DDD, “prd_ehd_DDD”= :prd_ehd_DDD, “prd_inhoud”= :prd_inhoud, “prd_ehd_Inhoud”= :prd_ehd_Inhoud, “prd_HeeftUniekeBarcode”= :prd_HeeftUniekeBarcode, “prd_RefNR”= :prd_RefNR,
“prd_HeeftVervalDatum”= :prd_HeeftVervalDatum, “prd_vervaldatum”= :prd_vervaldatum, “prd_IsGeneesmiddel”= :prd_IsGeneesmiddel, “prd_TransmissieTD”= :prd_TransmissieTD, “prd_PrintBVAC”= :prd_PrintBVAC,
“prd_IsCompendium”= :prd_IsCompendium, “prd_NietMagistraal”= :prd_NietMagistraal, “prd_ZorgTrajecten”= :prd_ZorgTrajecten, “prd_IsDelphiCare”= :prd_IsDelphiCare, “prd_IsCybele”= :prd_IsCybele,
“prd_IsBCFI”= :prd_IsBCFI, “prd_IsPharmaGuide”= :prd_IsPharmaGuide, “prd_RizivTerugbetaalbaar”= :prd_RizivTerugbetaalbaar, “prd_HeeftReferentieTBT”= :prd_HeeftReferentieTBT, “prd_BasisHono”= :prd_BasisHono,
“prd_AfFabriekPrijs”= :prd_AfFabriekPrijs, “prd_AfFabriekTBBasis”= :prd_AfFabriekTBBasis, “prd_TerugNameStatus”= :prd_TerugNameStatus, “prd_TerugNameVoorVerval”= :prd_TerugNameVoorVerval,
“prd_TerugNameNaVerval”= :prd_TerugNameNaVerval, “prd_IsLeverbaar”= :prd_IsLeverbaar, “prd_blisterrobotafvulbaar”= :prd_blisterrobotafvulbaar, “prd_DoMycarenet”= :prd_DoMycarenet, “prd_InTar31”= :prd_InTar31,
“prd_PrijsIndicator”= :prd_PrijsIndicator, “prd_NietVSVroedVrouw”= :prd_NietVSVroedVrouw, “prd_BNM”= :prd_BNM, “prd_inTar43”= :prd_inTar43, “prd_QLev_Primkey”= :prd_QLev_Primkey, “prd_QMax”= :prd_QMax,
“prd_QMin”= :prd_QMin, “prd_UseQ”= :prd_UseQ WHERE (“prd_PrimKey”=:OLD_prd_PrimKey) params: :prd_DateChanged=18/03/2026 10:51:54 :prd_UserId=8 :prd_atc_primkey=0 :prd_atc_code= :prd_CNK=0901059
:prd_Groothandelsproduct=False :prd_DatumOpMarkt=0:00:00 :prd_DatumSchrapping=0:00:00 :prd_CommercieleStatus= :prd_StatusWetgeving= :prd_CategorieAPB= :prd_CategorieOfficinall= :prd_Gebruik= :prd_Wetgeving=
:prd_CodeDopingFrGem= :prd_CodeDopingVlGem= :prd_ptv_primkey=900 :prd_Bereiding= :prd_Bewaartemperatuur= :prd_HeeftBarcode=True :prd_LabelAPB=False :prd_Steriel=False :prd_CodeVerdovingsmiddel=
:prd_Hospitaalverpakking=False :prd_EnkelTBTInHospitaal=False :prd_IsVoorschriftPlichtig=False :prd_IsImport=False :prd_IsVerdoving=False :prd_IsVeterinair=False :prd_CodeUnidosis= :prd_Originaliteit=
:prd_HoeveelheidPerVerpakking=0 :prd_AantalIEInsulPerVerpakking=0 :prd_GeldigheidsdatumPrijsInfo=0:00:00 :prd_EigenAankoopPrijs=0 :prd_Publieksprijs=0 :prd_VorigePublieksprijs=0 :prd_EigenPrijs=0
:prd_Richtprijs=False :prd_BTWCode=1 :prd_IsTBTHomeopathie=False :prd_Terugbetalingsbasis=0 :prd_CodeFiscaalForfait= :prd_IBCode= :prd_MinVoorraad=0 :prd_MaxVoorraad=0 :prd_AantalTeBestellen=0
:prd_AutomatischBestellen=False :prd_sbs_Primkey=0 :prd_sbs_Status=0 :prd_sbs_LaatstGetriggerd=0:00:00 :prd_lev_Primkey=0 :prd_korting=0 :prd_lok_Primkey=0 :prd_Geldigheidsdatum=0:00:00 :prd_pgs_primkey=0
:prd_pga_primkey=0 :prd_Dynaphar=False :prd_IsSchriftelijkeAanvraag=False :prd_Hernieuwing=False :prd_HernieuwingCyclisch=False :prd_HernieuwingMaanden=0 :prd_HernieuwingAantal=0 :prd_IsGeneriek=False
:prd_HeeftAstmatomInfo=False :prd_HeeftEersteAflevering=False :prd_IsJongereProduct=False :prd_DPP=0 :prd_DDD=0 :prd_ehd_DDD=0 :prd_inhoud=0 :prd_ehd_Inhoud=0 :prd_HeeftUniekeBarcode=False
:prd_RefNR= :prd_HeeftVervalDatum=True :prd_vervaldatum=3/10/2013 :prd_IsGeneesmiddel=False :prd_TransmissieTD=False :prd_PrintBVAC=False :prd_IsCompendium=False :prd_NietMagistraal=False
:prd_ZorgTrajecten= :prd_IsDelphiCare=False :prd_IsCybele=False :prd_IsBCFI=False :prd_IsPharmaGuide=False :prd_RizivTerugbetaalbaar=False :prd_HeeftReferentieTBT=False :prd_BasisHono=False
:prd_AfFabriekPrijs=0 :prd_AfFabriekTBBasis=0 :prd_TerugNameStatus=9 :prd_TerugNameVoorVerval=0 :prd_TerugNameNaVerval=0 :prd_IsLeverbaar=0 :prd_blisterrobotafvulbaar=False :prd_DoMycarenet=False
:prd_InTar31=False :prd_PrijsIndicator=0 :prd_NietVSVroedVrouw=False :prd_BNM=False :prd_inTar43=False :prd_QLev_Primkey=0 :prd_QMax=0 :prd_QMin=0 :prd_UseQ=False :OLD_prd_PrimKey=100000643

Hi,

for ReducedDelta = true, you have a big SQL unless those fields were changed.
Can you log actual delta change?

I don’t understand your comment or suggestion.

This is a big issue. How can we proceed? Maybe we can have a call and I show you the issue?

Hi,

Can you show client-side request that contains delta, i.e. stream that send to server for UpdateData method ?

You can drop email to support@ and attach that file for keeping privacy.

Not sure what you want me to send.

2x binary content serializeddata? First call & second call?

FYI: Client side we call via a custom RO function ‘UpdateProduct’

Our dataobject TProductDetailDATable is derived from TDAMemDataTable.

Server side: Multiple deltas get processed (see function below)

but the issue is related to the master table ProductDetail

The issue is raised inside

BizProcessor.ProcessDelta(Connection, Delta, [ctDelete, ctInsert, ctUpdate]);

function TOffServerService.UpdateProduct(const Delta: Binary): Binary;
var
  InfoDelta, ProductDelta, ProductLeverancierDelta, ProductTarificatieDelta, ProductNaamDelta, ProductStockDelta, ProductChemieLinkDelta, ProductSynoniemDelta, ProductWetenSchappelijkDelta,
    ProductEANDelta: IDADelta;
begin // FVC 02/2025: The Delta binary only contains Product DA schema - code does nothing really
  try
    try
      Connection.BeginTransaction();
      // Initializing
      DABINDataStreamer.Initialize(Delta, aiRead);
      Try
        // Unpacking…
        // WDV (9/01/2004 11:14:23) Opgelet: we moeten alle mogelijke deltas
        // WDV (9/01/2004 11:14:31) gaan benaderen, anders krijgen we problemen
        ProductDelta := ReadDeltaIfExist(DABINDataStreamer, 'ProductDetail');
        ProductLeverancierDelta := ReadDeltaIfExist(DABINDataStreamer, 'ProductLeverancier');
        ProductNaamDelta := ReadDeltaIfExist(DABINDataStreamer, 'ProductNaam');
        ProductStockDelta := ReadDeltaIfExist(DABINDataStreamer, 'ProductStock');
        InfoDelta := ReadDeltaIfExist(DABINDataStreamer, 'Info');
        ProductChemieLinkDelta := ReadDeltaIfExist(DABINDataStreamer, 'ProductLinkChemie');
        ProductSynoniemDelta := ReadDeltaIfExist(DABINDataStreamer, 'ProductSynoniem');
        ProductWetenSchappelijkDelta := ReadDeltaIfExist(DABINDataStreamer, 'ProductWetenSchappelijk');
        ProductEANDelta := ReadDeltaIfExist(DABINDataStreamer, 'ProductEAN');
        ProductTarificatieDelta := ReadDeltaIfExist(DABINDataStreamer, 'ProductTarifikatie');
      Finally
        DABINDataStreamer.Finalize;
      End;

      if (ProductChemieLinkDelta <> nil) then
        ChmLinkPrdProcessor.ProcessDelta(Connection, ProductChemieLinkDelta, [ctDelete, ctInsert, ctUpdate]);
      if (ProductSynoniemDelta <> nil) then
        ProductSynoniemProcessor.ProcessDelta(Connection, ProductSynoniemDelta, [ctDelete, ctInsert, ctUpdate]);
      if (ProductWetenSchappelijkDelta <> nil) then
        ProductWetenschappelijkProcessor.ProcessDelta(Connection, ProductWetenSchappelijkDelta, [ctDelete, ctInsert, ctUpdate]);
      if (ProductEANDelta <> nil) then
        ProductEANProcessor.ProcessDelta(Connection, ProductEANDelta, [ctDelete, ctInsert, ctUpdate]);
      if (ProductLeverancierDelta <> nil) then
        PlvProcessor.ProcessDelta(Connection, ProductLeverancierDelta, [ctDelete, ctInsert, ctUpdate]);

      CheckProcessDelta(Connection, ProductNaamProcessor, ProductNaamDelta, nil);
      if (InfoDelta <> nil) then
        InfoProcessor.ProcessDelta(Connection, InfoDelta, [ctDelete, ctInsert, ctUpdate]);
      // we doen niets met tarificatie delta

      // if (ProductTarificatieDelta <> nil) then
      // ProductTarificatieProcessor.ProcessDelta(Connection,
      // ProductTarificatieDelta,[ctDelete, ctInsert, ctUpdate]);
      if (ProductStockDelta <> nil) then
        ProductStockProcessor.ProcessDelta(Connection, ProductStockDelta, [ctDelete, ctInsert, ctUpdate]);
      CheckProcessDelta(Connection, ProductDetailProcessor, ProductDelta, Delta);

      Connection.CommitTransaction();
    except
      Connection.RollbackTransaction();
      raise;
    end;

  finally
    Result := Binary.Create();

    DABINDataStreamer.Initialize(Result, aiWrite);
    try
      if Assigned(ProductDelta) then
        DABINDataStreamer.WriteDelta(ProductDelta);
      if Assigned(ProductLeverancierDelta) then
        DABINDataStreamer.WriteDelta(ProductLeverancierDelta);
      if Assigned(ProductNaamDelta) then
        DABINDataStreamer.WriteDelta(ProductNaamDelta);
      if Assigned(ProductTarificatieDelta) then
        DABINDataStreamer.WriteDelta(ProductTarificatieDelta);
      if Assigned(InfoDelta) then
        DABINDataStreamer.WriteDelta(InfoDelta);
      if Assigned(ProductStockDelta) then
        DABINDataStreamer.WriteDelta(ProductStockDelta);
      if Assigned(ProductWetenSchappelijkDelta) then
        DABINDataStreamer.WriteDelta(ProductWetenSchappelijkDelta);
      if Assigned(ProductEANDelta) then
        DABINDataStreamer.WriteDelta(ProductEANDelta);
      if Assigned(ProductSynoniemDelta) then
        DABINDataStreamer.WriteDelta(ProductSynoniemDelta);
      if Assigned(ProductChemieLinkDelta) then
        DABINDataStreamer.WriteDelta(ProductChemieLinkDelta);
    finally
      DABINDataStreamer.Finalize();
    end;

  end;
end;

function CheckProcessDelta(Connection: IDAConnection; BizProcessor: TDABusinessProcessor; Delta: IDADelta; BinDelta: Binary): boolean;
begin
  Result := True;
  if (Delta <> nil) then
    BizProcessor.ProcessDelta(Connection, Delta, [ctDelete, ctInsert, ctUpdate]);
  if (BinDelta <> nil) then
    CheckPropagateUpdateToCloud(Delta.LogicalName, BinDelta);
end;

By the way I noticed that Delta.Size=3220 for the first call and it is 2994 for the second call

Delta of my server side

image

FYI: This is what raises the error server side. A raise Exception inside BeforeProcessChange

Hi,

lets detect firstly why you have A in currency field.
is it possible to create a very simple testcase that reproduces this case?
will whole things work if you are using standard UpdateData instead of customUpdateProduct method?

note: std UpdateData works correctly with m/d tables.

The issue also occurs when I update via legacy TDARemoteService.UpdateData

After my first ApplyUpdates. I arrive in this function with a Delta binary buffer of size 3183

When immediately calling ApplyUpdates again the Delta binary buffer is only 2958 bytes.

Can we examine the content of this buffer?

What could cause this change?

I have attached the delta buffers and the da schema

The delta is for table ‘ProductDetail’

updatedata.7z (112.5 KB)

Hi,

you can use this simple program for examinating both streams:
33093.zip (90.2 KB)

It looks like all the “old” values have disappeared

The status is also different

What could have caused this?

What am I doing wrong?

Note: The only field I change ‘prd_cnk’ has correct old and new value

Is it normal the streamdata contains status ‘failed’ when you applyupdates again?

Is it the client side that takes the serverside stream data answer and processes it?

I understand it does this for reporting but it should not persist to the dataset in the client memory, correct?

Hi,

as for me, these streams looks like:

  • updatedataDelta1.bin - client’s request
  • updatedataDelta2.bin - server’s response

because

  • status pending - delta isn’t processed yet
  • status failed - delta already processed

client-side can change this status only in reconcile dialog or manually by user
so it is possible that failed delta can be send to server again.

usually DA processes failed delta via reconcile dialogs.

check more at Reconcile Dialog and RDA.ReconcileProvider

delta changes are removed at merging only if they have csResolved status.
You always can remove unneeded change from delta

looks like you haven’t set ReducedDelta for streamer on server-side so it was returned in full …

OK,

My code shows a nice error message instead of our own custom reconcile window at client side upon return

When I show our custom reconcile window the data appears to be already messed up

Our reconcile form does nothing with delta statusses

Regarding reduced delta:

I think what we do is OK

Hi,

You can add checking for csFailed status and delete them or require to fix broken changes before sending it to server, i.e. send only changes with csPending status.

user can do it in your reconcile dialog: cancel csFailed changes or update fields and reset csPending status

The big issue to me seems the scrambled delta buffer that goes back to the client.

Old values are all gone and it looks like he has fewer new values.

The last new values are actually values for some of the first fields

This is the top of that list

It looks like LoggedFieldCount is wrong (or something to that extent)

I modified the reconcile code as you suggested (using OldValueByName)

Now the values seem to match the fieldnames

The problem of course still remains.

The update that’s done the second time updates too much

I believe this is caused by the reduced delta server side which does not write back the oldvalues

[from TDABin2DataStreamer.DoWriteDelta]