Quote Line Discount % reverts to 0 when changing Qty - how to force it to remain unless specifically over-typed?

Thanks - I have tried docdiscount too.

I’ve run a trace log

When I first enter a discount %, the discount amount does not change, see here where the discount% is set to 10 but the actual discount value remains at 0:

This occurs a long way under the ‘GetUnitPriceInfo’ method name, but it’s the closes method I can see listed before the 10% discount is displayed:

Erp.Proxy.BO.QuoteImpl GetDtlUnitPriceInfo
      <DiscountPercent>10.00</DiscountPercent>
      <Discount>0</Discount>
      <DocDiscount>0</DocDiscount>  

The actual discount value doesn’t change until much further along the trace log, under the “Update” method name:

Erp.Proxy.BO.QuoteImpl Update

[removed lots of lines inbetween….]
10.00
3.80
3.80

Long story short, I’ve tried to implement the Quotedtl.discount field to restore back to it’s original content (via the new ud field) under the update method instead, but I still get nothing.

I’ve set up an extra new ud field which will take the value of the saved discount amount, so I know I can
a) save the quotedtl.discount value and
b) set another field to take that value

I just can’t get it to work on quotedtl.discount!

If you save the line with the mismatching DiscPct and Disc, and then refresh, does it then show the proper discount?

It does when I first enter a discount %
The discount amount is 0.00 until I save
If I then change the expected selling qty, the discount % now remains in place (e.g. 10%) but the discount amount reverts to 0.00, and save/refresh does not pull in the correct amount, or any change at all, it stays at 0.00
so I have discount 10%, but discount amount 0.00

We have some similar issues with special rates, which I could only solve by forcing changes to the QuoteQty table, because at some hard-to-define points the QuoteDtl fields seem to re-set to the same as QuoteQty. May be worth exploring?

Thanks Daryl, have you any examples? even a basic overview - my sales team are ready throw Epicor out the window and use paper and wax crayons instead :wink:

Here’s the thread where I was wrestling with it.

I’ve got to get out of the office now so anything else will have to wait until tomorrow, I’m afraid. But in general terms, I found that there are some fields in QuoteDtl that, if you’re changing them programmatically, need to be changed in the associated QuoteQty rows(s) as well if you want them to stick. I assume there must be a correct approach that can handle it all in the background as the native UI does, but I haven’t found it. For me, QuoteDtl keeps reverting to QuoteQty and I can’t stop it so I have to make sure both are the same before it happens.

thanks Daryl

Something I just realized… You don’t want to save the existing Discount amount, but rather recalculate it based on the qty and Price (which could get tricky if you use price breaks).

Also … What if you try setting the DiscountPct first (before setting Discount) - since the Disc amount is based on that. Especially since the UI flows that way (you tab to the DiscPct first)

For completeness, here’s the code which is now working for us. It’s from a uBAQ rather than a BPM, but the same BO applies for both.

// Update QuoteQty with changed prices (necessary before setting QuoteDtl price, otherwise QuoteDtl price will reset to this)
            // note: if quantity breaks can be used then checks to update only the correct row will be needed
            foreach (var ttRow in ttResultsUpdate)
            {
                int qline = ttRow.QuoteDtl_QuoteLine;
                foreach (var qqty in (from row in q.QuoteQty where row.QuoteLine == qline select row))
                {
                    decimal docprice = ttRow.QuoteDtl_DocExpUnitPrice;
                    if (qqty.DocUnitPrice != docprice || qqty.PricePerCode != ttRow.QuoteDtl_ExpPricePerCode)
                    {
                        decimal price = Decimal.Round(docprice / exrate,2);
                        qqty.UnitPrice = price;
                        qqty.DocUnitPrice = docprice;
                        qqty.PricePerCode = ttRow.QuoteDtl_ExpPricePerCode;
                        qqty.RowMod = "U";
                        upd = true;
                    }
                }
            }
            if (upd)
            {
                svc.Update(ref q);
                upd = false;
            }
            
            // Update main QuoteDtl with changed prices or discounts
            foreach (var ttRow in ttResultsUpdate)
            {
                int qline = ttRow.QuoteDtl_QuoteLine;
                foreach (var qdtl in (from row in q.QuoteDtl where row.QuoteLine == qline select row))
                {
                    decimal docprice = ttRow.QuoteDtl_DocExpUnitPrice;
                    decimal disc = ttRow.QuoteDtl_DiscountPercent;
                    if (qdtl.DocDspExpUnitPrice != docprice || qdtl.ExpPricePerCode != ttRow.QuoteDtl_ExpPricePerCode)
                    {
                        qdtl.DocDspExpUnitPrice = docprice;
                        qdtl.ExpPricePerCode = ttRow.QuoteDtl_ExpPricePerCode;
                        qdtl.RowMod = "U";
                        svc.GetDtlUnitPriceInfo(true,false,ref q);
                        svc.GetDtlUnitPriceInfo_User(true,false,false,true,ref q);
                        qdtl.RowMod = "U";
                        upd = true;
                    }
                    if (qdtl.DiscountPercent != disc)
                    {
                        qdtl.DiscountPercent = disc;
                        qdtl.RowMod = "U";
                        svc.GetDtlUnitPriceInfo(true,false,ref q);
                        qdtl.RowMod = "U";
                        upd = true;
                    }
                }
            }
            if (upd)
            {
                svc.Update(ref q);
                upd = false;
            }

Obviously it’s incomplete as it’s part of a larger piece of code we’re using, but when we do this the discounts and prices do stay in place. The key is the additional method calls which do the recalculation, I think, and for us (as I said) making sure the QuoteQty doesn’t conflict.

Thank you so much for sharing.

@KMenz -

It works for me when I do the following in the post-proc

  1. Set Field 0: Set the ttQuoteDtl.DiscountPercent field to the UD field from the pre-proc
  2. Set Atguemnet/Var: Set the variable newDiscAmt to
    ttQuoteDtlRow.Rpt1Discount * ttQuoteDtlRow.SellingExpectedQty /100
  3. Set Field 1: Set the ttQuoteDtl.Discount field to the variable newDiscAmt
  4. Set Field 2: Set the ttQuoteDtl.DocDiscount field to the variable newDiscAmt

QuoteKeepDisc

EDIT

Step 2 is missing the unit price, and uses RptDiscount1 for my test when the UD field is what should be used.

1 Like

I’m sure this is the approach to take. I’d just add a couple of caveats based on the fact that I’ve been wrestling with Quotes on and off for months now:

Although calculating the fields (as per your Set Argument) mostly works fine, it can cause sporadic problems. It’s more reliable to call the methods in the Quote BO that do it for you, and I think in this case it just needs GetDtlUnitPriceInfo with the right parameters after setting DiscountPercent.

Sometimes you need an extra step to make sure the changes show up in the client. It shouldn’t the way you’ve done it, but I’m never surprised when it is needed.

1 Like

Thanks so much @dhewi and @ckrusen

Annoyingly,I can get the discount % to stick but still can’t get the discount amount to recalculate in the QuoteDtl.Discount or QuoteDtl.DocDiscount fields.

I’ve tried setting the variable to generate the new discount amount as :

(ttQuoteDtlRow.SellingExpectedQty *
ttQuoteDtlRow.DocExpUnitPrice )
*
(ttQuoteDtlRow.UDField<System.Decimal>(“SaveDiscountPct_c”) /100 )

I’ve also set a new UD field to store this value just so I can run SQL and check it works, and it does. So I can trust the build to a fair extent, but it seems that QuoteDtl.Discount and QuoteDtl.DocDiscount are protected by a force field and I can’t push a value in there.

I’ve tried on
Quote >
ChangeSellingExpQty
GetUnitPriceInfo
Update

I could set the new UD field to be our discount amount but the work then needed to amend every report and other calculation feeding from it would be insane.

Try putting it in DspDocDiscount …?

That’s assuming you aren’t calling the GetUnitPriceDtl method to do the calculation for you.

It’s going over my head now to be honest.

From within Quote.ChangeExpSellingQty/Post-Processing/[MyText]

I can invoke BO Method ‘erp.Quote.GetDtlUnitPriceInfo’ but there are 3 parameters I need to set bindings for and I’m now in cconfused.com territory.

Also can I put it into DspDocDiscount?
I can’t see that as an option as it’s not a DB field

svc.GetDtlUnitPriceInfo(true,false,ref q);

This is what we use in the code I posted above, and I got it from doing a trace to see what was going on when I did the same thing in the client screen. q in this case is the QuoteDataSet.

It’s in the field list in the BPM, because those fields are based on the dataset rather than the db. I got it wrong from memory, though, it’s DocDspDiscount.

One thing that could be throwing you off is if you use price lists, and/or there are price breaks.

We don’t use price breaks or price lists, so GetDtlUnitPriceInfo doesn’t do anything in our setup. Those are some of the extra conditions that @dhewi mentioned that my simple solution didn’t account for.

Did anyone ever get to the bottom of this? I am trying to do something similar in the Configurator. I have had success manipulating the Discount % and then executing the method associated with it on Orders… but when I take a similar approach in the QuoteDtl, I can’t get the % to change from whatever it thinks it should be based on Price List/Customer. 99% of the time that’s perfect. But we have one exception where we want to apply an incentive to bundle a certain part with a kit and in doing so, will apply additional % discount from what it would normally be.

I’ve tried setting the % field, executing the GetDtlUnitPriceInfo method, and then Update method. It’s as though I’ve done nothing at all. Just leaves it to whatever the default is based on ProdCode. Any advice at this point?