Configurator Misc Charges

I have somehow managed to write 2 successful configurators and looking to enhance the first one further by including additional things to control pricing.

Does anyone know how to write a line misc charge from a configurator?

I already know how i am going to calculate the total price of the misc line but basically there will be 2 fields from the configurator that will be written, the ‘code’ for the misc charge and the price, i can write additional if required but i need to know if its possible in the first instance.

You should be able to write that logic into a UD method. If you’re familiar with how to trace the business logic that fires while using the application, it will tell you what methods you will need to refer and which fields to change.

EDIT: I’ve never tried it. Just speaking in hypothetical. I have written other order lines from within the configurator… so I assume it would be like that.

1 Like

I’ve done this before for adding freight directly from the configurator, since everything else for the line was in the configurator anyway. I had a combo box to select shipping (“Ground, Second Day, Next Day Air”) with a default to ground, which was free at the company.

There are two ways you could do this. One would be to call a Server-side UDMethod in the DocRules:

UDMethods.AddShipMiscLine( string Inputs.ShippingOption.Value, decimal Inputs.ShippingCharge.Value );

then in the UD Method:

    using ( var svc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.SalesOrderSvcContract>(Db) )
    {
        var ts = new Erp.Tablesets.SalesOrderTableset();
        svc.GetNewOrderMsc( ref ts, Context.OrderNumber, Context.OrderLineNumber );
        
        var row = ts.OrderMsc.First();
        row.MiscCode = Inputs.ShippingOption.Value;
        svc.ChangeMiscCode( ref ts );
        row.MiscAmt = Inputs.ShippingCharge.Value;
        svc.ChangeMiscAmount( ref ts );
        svc.Update( ref ts );
    }

The other option would be to set a Data Directive to call a function to do the same thing. So in the doc Rule

OrderDtl.ShipOption_c = Inputs.ShippingOption.Value;

In a Data Directive:

// Condition: If ShipOption_c has changed from Any to Another =>
// Invoke Function Widget passing in ttOrderDtl.ShipOption_c

Then an Epicor Function with pretty much the same code as the UD Method.

3 Likes

Thanks once again Kevin!

I will give this a go tomorrow when i am back in the office!

1 Like

You’re welcome! That code is quick and dirty so you may want to add some validation and make sure all the method calls work. I adapted it from this one that I use to add additional order lines from Configurator:

1 Like

Gotta love Epicor, i have tried for a good few hours today trying to get this code to work, the assemblies no matter what i did would not add or work so i gave up.

Tonight at home whilst settling the little one i decided to give it another go and low and behold the assembly added without issue, the code writing in line by line has passed validation no issue (with the exception of writing the values, need to also pass the tablename which i have done), now need to test the code and see if it does what its ment to!!

Will post updates when i get them and as i always do, will post a quick guide on how i did it so others can learn too!

1 Like

This works with a slight tweak to it!

I am going to be adding some extra code to it as you suggested to make it more robust bit once i have the finished article, i will post the result here but yes it works! :grin::grin::grin:

1 Like

EDIT::: The code below i found was actually a cache issue! :oncoming_fist:

i did have to change on the quote side row.MiscAmt to row.DocDspMiscAmt but other than that it works, i do need to work on the code some more, i have upto 9 misc charges to add so i need to work on that next and i want it to delete and replace the lines as required rather than keep adding new ones each time a configuration happens but other than that as a base start it works!

big thanks to @kve for your help!!


working on this today, sales order the logic works fine without issue, for a quote however, i can not get the price to write to the misc line, it always remains at zero.

code is as follows:

if(!Inputs.CCRequired_chk.Value) return;

int iQuoteNum = Context.QuoteNumber;
int iQuoteLine = Context.QuoteLineNumber;
int iOrderNum = Context.OrderNumber;
int iOrderLine = Context.OrderLineNumber;

if(iQuoteNum > 0)
{
    using ( var svc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.QuoteSvcContract>(Db) )
    {
        var ts = new Erp.Tablesets.QuoteTableset();svc.GetNewQuoteMsc( ref ts, iQuoteNum, iQuoteLine, 0 );

        var row = ts.QuoteMsc.First();
        row.MiscCode = "CC";
        row.Description = "Description";
        row.FreqCode = "F";
        row.MiscAmt = Inputs.Aperture600Total_dec.Value;

        svc.Update( ref ts );
    }
}
else if (iOrderNum > 0)
{
    using ( var svc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.SalesOrderSvcContract>(Db) )
    {
        var ts = new Erp.Tablesets.SalesOrderTableset();
        svc.GetNewOrderMsc( ref ts, Context.OrderNumber, Context.OrderLineNumber);

        var row = ts.OrderMsc.First();
        row.MiscCode = "CC";
        svc.ChangeMiscCode( ref ts, "OrderMsc" );
        row.DocDspMiscAmt = Inputs.Aperture600Total_dec.Value;
        svc.ChangeMiscAmount( ref ts, "OrderMsc" );

        svc.Update( ref ts );
    }
}
else
{
    //Display message saying no quote or order number found
}

on the quoteline misc charges field help, it is saying to use MiscChrg.MiscAmt as a defualt, but i am unsure how i should do this???

The Solution!!

to give some context to this code, in our configurator we have added some fields as in the image below:

the code below will then only fire if the checkbox is ticked, otherwise it stops there

if it is ticked the code will execute and in a breif summary do the following:

  • Look at the quote / order and retreive any misc line charges
  • delete any exitsing misc charges that have the code (in our case) “CC”
  • then look at all the fields in the configurator and see which ones have a value greater than zero
  • write one misc line per charge along with the description
  • sets the prices and frequency
if(!Inputs.CCRequired_chk.Value) return;

int iQuoteNum = Context.QuoteNumber;
int iQuoteLine = Context.QuoteLineNumber;
int iOrderNum = Context.OrderNumber;
int iOrderLine = Context.OrderLineNumber;

if (iQuoteNum > 0)
{
    using (var svc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.QuoteSvcContract>(Db))
    {
        // 1) Define charges (amount + description inputs)
        var charges = new[]
        {
            new { amount = Inputs.Aperture600Total_dec.Value,  desc = Inputs.Aperture600des_txt.Value },
            new { =====ADD MORE INPUTS AS REQUIRED },
        }

        // 2) Delete all existing lines where code is equal to "cc"
        var tsExisting = svc.GetByID(iQuoteNum);
        var ccLines = tsExisting.QuoteMsc
            .Where(m => m.QuoteNum == iQuoteNum && m.QuoteLine == iQuoteLine && m.MiscCode == "CC")
            .ToList();

        if (ccLines.Count > 0)
        {
            foreach (var m in ccLines) m.RowMod = "D"; // set rows to delete
            svc.Update(ref tsExisting); // commit deletions
        }

        // 3) Add back only non-zero charges
        foreach (var c in charges)
        {
            if (c.amount <= 0m) continue; // ignore where there are no charges

            var ts = new Erp.Tablesets.QuoteTableset();
            svc.GetNewQuoteMsc(ref ts, iQuoteNum, iQuoteLine, 0);

            var row = ts.QuoteMsc.First();
            row.MiscCode = "CC"; // Sets to the CC misc code
            row.Description = string.IsNullOrWhiteSpace(c.desc) ? "CC Charge" : c.desc.Trim(); // if the description field is blank, sets it to CC Charge, otherwise uses the description and removes white space
            row.FreqCode = "F"; // sets the frequency to first can also use E for every or L for Last
            row.Type = "A"; // tells database its a new row being added
            row.CurrencySwitch = false; // does not switch currency
            row.DocDspMiscAmt = c.amount; // sets the price

            svc.ChangeMiscAmt(ref ts, "QuoteMsc"); // writes the price
            svc.Update(ref ts); // saves and commits changes
        }
    }
}
else if (iOrderNum > 0)
{
    using (var svc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.SalesOrderSvcContract>(Db))
    {
        // 1) Define charges (amount + description inputs)
        var charges = new[]
        {
            new { amount = Inputs.Aperture600Total_dec.Value,   desc = Inputs.Aperture600Desc_txt.Value },
            new { amount = Inputs.Aperture601Total_dec.Value,   desc = Inputs.Aperture601Desc_txt.Value },
            new { amount = Inputs.Aperture2Total_dec.Value,     desc = Inputs.Aperture2Dec_txt.Value },
            new { amount = Inputs.Doorway1Total_dec.Value,      desc = Inputs.Doorway1Des_txt.Value },
            new { amount = Inputs.Doorway2Total_dec.Value,      desc = Inputs.Doorway2Desc_txt.Value },
            new { amount = Inputs.Penetration100Total_dec.Value,desc = Inputs.Penetration100Des_txt.Value },
            new { amount = Inputs.MitreWidthTotal_dec.Value,    desc = Inputs.MitreWidthDes_txt.Value },
            new { amount = Inputs.ApertureWonkyTotal_dec.Value, desc = Inputs.ApertureWonkyDes_txt.Value },
            new { amount = Inputs.NotchTotal_dec.Value,         desc = Inputs.NotchDesc_txt.Value },
            new { amount = Inputs.ProSawTotal_dec.Value,        desc = Inputs.ProsawCutDesc_txt.Value }
        };

        // 2) Hard delete all existing CC lines on this order line
        var tsExisting = svc.GetByID(iOrderNum);
        var ccLines = tsExisting.OrderMsc
            .Where(m => m.OrderNum == iOrderNum && m.OrderLine == iOrderLine && m.MiscCode == "CC")
            .ToList();

        if(ccLines.Count > 0)
        {
            foreach(var m in ccLines) m.RowMod = "D"; // set rows to delete
            svc.Update(ref tsExisting); //commit delete
        }

        // 3) Add back only non-zero charges
        foreach(var c in charges)
        {
            if(c.amount <=0m) continue; // ignore where there are no charges
            
            var tsNew = new Erp.Tablesets.SalesOrderTableset();
            svc.GetNewOrderMsc(ref tsNew, iOrderNum, iOrderLine);

            var row = tsNew.OrderMsc.First();
            row.MiscCode = "CC"; // Sets to the CC misc code
            svc.ChangeMiscCode(ref tsNew, "OrderMsc"); // sets defaults that are set on the misc code

            row.Description = string.IsNullOrWhiteSpace(c.desc) ? "CC Charge" : c.desc.Trim(); // if the description field is blank, sets it to CC Charge, otherwise uses the description and removes white space
            row.FreqCode = "F"; // sets the frequency to first can also use E for every or L for Last
            row.DocDspMiscAmt = c.amount; // sets the price from the total fields in the configurator
            svc.ChangeMiscAmount(ref tsNew, "OrderMsc"); // writes the price

            svc.Update(ref tsNew); // saves and commits the changes
        }
    }
}

final output looks like the following:

you can then go into the configurator, changes quantities etc and the comments if required and it will remove ALL existing lines with the CC code, then repace with new ones.

2 Likes

This looks great! Great job with the validation & removal of old charges on reconfiguration.

Only thing I see that I want to make sure you’ve tested is this one:
row.Description = string.IsNullOrWhiteSpace(c.desc) ? "CC Charge" : c.desc.Trim();

I would usually use string.IsNullOrEmpty(c.desc) instead of IsNullOrWhiteSpace(c.desc). If you’ve tested it and it works, no worries. I’m not sure if this would or wouldn’t catch correctly if your input value is an empty string, though (if c.desc == "").

Hi Kevin, thanks for the feedback!

Went back and tested it again. It works as i expect it to…

If the description field is blank it automatically fills in “CC Charge” as per the code.

If someone has deleted it but accidently put a space or multiple spaces in the description so it looks empty by eye but actually isnt then again it writes “CC Charge” as expected - without the whitespace part the field is then not empty and would not write any default in there at all which i wanted to avoid as a ‘just incase’ (or that is my understanding of how it works)

I figured doing it this way will help to capture those mistakes!

2 Likes

Glad to hear it’s working! Just wanted to make sure it wasn’t going to bring empty strings back on you. Great job!

1 Like

Thanks again Kevin!

1 Like

EDIT 2: And this is why i hate Epicor…..nothing has changed other than the weekend coming and going…….and its working again :thinking: no explanation at all!

EDIT: works on quotes but not on orders after unapproving and re-approving the configurator…at least thats 50% of the problem solved!

this has broken in 2025.2.5 :sleepy_face: let the troubleshooting begin…..We are due to be upgraded this weekend so will have to hold out until after the big upgrade but curious as to why this would fall over when it worked in 2025.2.3……..

2 Likes