1

Using Delphi 2005, I have a TwwDBGrid (InfoPower) that allows the user to select part(s) & quantities to generate a quote. Part of the selection process is to allow the user to add a discount for each part consisting of DiscountAmount and DiscountType. DiscountAmount is the amount of the discount and DiscountType is the type of discount amount (% off, $ off or flat price). This data is held in a ClientDataSet called ClientDataSetParts.

Within the OnCalc method (ClientDataSetPartsCalcFields) I have the following code which will recalculate the TotalPrice based on the parts cost and any discounts. RangePricing is used in the case where a price is calculated based on a quantity within a range (i.e., 1-100 = $100, 101-200 = $150, etc.):

procedure TfrmCustom_Services.ClientDataSetPartsCalcFields(
  DataSet: TDataSet);
begin
  inherited;
  // if part is selected then calculate Total
  if (ClientDataSetPartsSelected.Value) then begin
    // if range pricing is defined
    if (ClientDataSetParts.FieldByName('RangePricing').AsBoolean) then begin
      ClientDataSetParts.FieldByName('TotalPrice').Value :=
            ClientDataSetParts.FieldbyName('UnitPrice').Value;
    // otherwise use regular pricing
    end else begin
      ClientDataSetParts.FieldByName('TotalPrice').Value :=
          ClientDataSetParts.FieldbyName('UnitPrice').Value *
          ClientDataSetParts.FieldByName('Quantity').Value;
    end;

  // otherwise clear the Total
  end else begin
    ClientDataSetParts.FieldByName('TotalPrice').Clear;
    if (ClientDataSetParts.FieldByName('Quantity').Value <> null) then
      ClientDataSetParts.FieldByName('Quantity').Clear;
    if (ClientDataSetParts.FieldByName('DiscountAmount').Value <> null) then
      ClientDataSetParts.FieldByName('DiscountAmount').Clear;
    if (ClientDataSetParts.FieldByName('DiscountType').Value <> null) then
      ClientDataSetParts.FieldByName('DiscountType').Clear;
  end;

  // Update totals if Discount is applied
  // Only recalculate if both discount value and type are applied
  // otherwise will constantly get errors when switching fields
  if ((ClientDataSetPartsDiscountAmount.Value > 0) and (ClientDataSetPartsDiscountType.Value <> '')) then begin
    case StringToCaseSelect((ClientDataSetPartsDiscountType.Value), ['% Disc','$ Disc','Price']) of
      0 :
      begin
        ClientDataSetParts.FieldByName('DiscountDollarAmount').Value :=
            ClientDataSetParts.FieldByName('TotalPrice').Value * (ClientDataSetParts.FieldByName('DiscountAmount').Value/100);
        ClientDataSetPartsTotalPrice.Value := ClientDataSetPartsTotalPrice.Value - (ClientDataSetPartsTotalPrice.Value * (ClientDataSetPartsDiscountAmount.Value/100));
      end;
      1 :
      begin
        ClientDataSetParts.FieldByName('DiscountDollarAmount').Value := ClientDataSetPartsDiscountAmount.Value;
        ClientDataSetPartsTotalPrice.Value := ClientDataSetPartsTotalPrice.Value - ClientDataSetPartsDiscountAmount.Value;
      end;
      2 :
      begin
        ClientDataSetParts.FieldByName('DiscountDollarAmount').Value :=
            ClientDataSetPartsTotalPrice.Value - ClientDataSetPartsDiscountAmount.Value;
        ClientDataSetPartsTotalPrice.Value := ClientDataSetPartsDiscountAmount.Value;
      end;
    end;
  end;

end;

The problem comes when I try to calculate DiscountDollarAmount (the actual dollar figure of the discount that I have to return to an outside source via API). Each time I set the ClientDataSetParts.FieldByName('DiscountDollarAmount').Value, ClientDataSetPartsCalcFields is called again causing endless calls and eventually a stack overflow.

How can I update this value without calling ClientDataSetPartsCalcFields recursively?

8
  • Have you tried setting AutoCalcFields to false? Commented Oct 17, 2012 at 13:42
  • Doesn't change anything. Commented Oct 17, 2012 at 14:00
  • Ok. But have a read on the documentation for 'AutoCalcFields', maybe it will give some insight. Commented Oct 17, 2012 at 14:02
  • Store the TotalPrice and any other calculated field used in DiscountDollarAmount to local variables when they're calculated, and use those variables in calculating DiscountDollarAmount instead of using the other field's Field.Value. Commented Oct 17, 2012 at 14:11
  • I have moved my code to OnDataChange but the problem seems to be that no matter where I try to assign 'DiscountDollarAmount', this change is detected as a change to the data set (rightfully so) and calls OnDataChange or AutoCalc which starts the recursion. How can I prevent the data set from updating itself when I set this value? Commented Oct 17, 2012 at 14:47

2 Answers 2

2

The infinite loop you're getting into is caused by the code changing the value, seeing it has changed, and re-starting the change (as you already know). To fix this, at the beginning of the procedure, set the OnCalc event to nil, then at the end, re-set it to the procedure. This way, the event monitoring will suspend during the time it actually processes, then will resume after the processing is done and it has assigned the new value.

Sign up to request clarification or add additional context in comments.

6 Comments

This would probably mask the underlying fault and lead to other bugs.
@David Heffernan - Can you elaborate? I don't understand how it would break anything, and in the past, I've used this solution as the "correct" solution according to my own sources (books, internet searches, and senior co-workers).
The explanation that makes sense is that this code sets non-calcukated fields. In which case the code is broken and this fix masks that.
@DavidHeffernan - So how would I calculate this DiscountDollarAmount correctly?
My guess is that the problem is not how to calculate it, rather that you are calculating it at all. Setting a non-calculated field will result in the OnCalcFields event firing. And that's your un-terminated recursion. I already asked you once. I'll try again. Is DiscountDollarAmount a calculated field? Are all the fields that you set in this event handler calculated fields?
|
2

This behaviour is what happens when you modify a non-calculated field. The OnCalcFields event should not make any modifications to non-calculated fields.

I know nothing about your database structure, but if my hunch is correct, you are modifying a non-calculated field somewhere in the OnCalcFields event handler. And because you modify a non-calculated field, the calculated fields need to be re-calculated. And so OnCalcFields is called. Which modifies a non-calculated field. And, well, you can see where this is going!

4 Comments

The field DiscountDollarAmount is of type fkCalculated. My OP explains what I am doing, taking a TotalPrice and applying a discount. How would I do this such that I do not encounter this recursion without using the method @Tom suggested?
OK, clearly you are doing something else wrong. Use the debugger with Debug DCUs enabled, and look at the call stack on the first recursive call to OnCalcFields. Look at the call stack for clues as to why the top level OnCalcFields provoked a recursive call to OnCalcFields. If possible, show us that call stack.
So I got everything working via @Tom suggestion. I then commented out the two lines to turn off and then turn back on the OnCalcFields method and everything worked like expected. Cannot explain why yesterday I was encountering the infinite loop and today I am not as the only change is the commenting out of those two lines. Anyways, thanks to all for your help.
If you can't reproduce the behaviour then it's a bit of a non question. Tom and I had the same diagnosis of the problem. That diagnosis is surely accurate. I strongly disagree with Tom's proposed solution though. That's just the wrong approach. It will lead to errors and fields being out of sync.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.