So, I’ve leaned that I need to sharpen my LINQ skills but, until then, can someone help me find out what’s wrong with my code? It works swimmingly when the source quote has all lines in sequence, but if the source quote has a deleted line it bombs.
My end goal is to duplicate the quantity fields from an existing quote when clicking “Actions” -> “Quote” -> “Duplicate Quote”.
So far I have created a Post-Processing BPM that sets a BPM Data Field, callContextBpmData.Number02, to Convert.ToDecimal(sourceLines.Split(’~’)[0]) and then runs the following custom code:
Again, if the original quote has all of its lines in order my code works, but when Customer Service deletes a line and clicks “Duplicate Quote” the application throws an exception.
agree with @Chris_Conn… but also it appears that you are attempting to update your current record based on the value of some other similar record in the quote (based pm Number02?)…
It seems like what you are trying to do here could be done 100% with widgets and and the “Fill table” setter widget, so that you would not need the link statement.
Below is how i reorganized the linq so i could better see it (i can’t read it if all one one)
can you explain what exactly you are copying from/to? it is hard to tell since this has things outside this code (sourceQuote, callContextBpmData.Number02 are two that I can see here).
foreach (var tt in ttQuoteDtl.Where(d => d.Added() || d.Updated()))
{
//now we get the quote dtls
var q = Db.QuoteDtl.Where(p => p.Company == tt.Company && p.QuoteNum == tt.QuoteNum && p.QuoteLine == tt.QuoteLine && p.QuoteLine <= callContextBpmData.Number02).FirstOrDefault(); //this quoteLine condition is not needed me thinks (unless you are trying to limit some details)
if (q != null) //if null, then either that dtl doesnt exist, or was excluded because of your callContext condition above
{
q.OrderQty = tt.OrderQty;
q.SellingExpectedQty = tt.SellingExpectedQty;
//q.RowMod = "U"; //i dont think this is used in a db record...
}
} //only update once all records are modified
Db.Validate();
Our CSRs have a lot of quotes that they want to be able to completely duplicate. Using the Actions -> Quote -> Duplicate Quote menu item, from Opportunity/Quote Entry, they can duplicate the lines but the quantities in the new quote are all zero. I am trying to copy the quantities from the old quote to the new quote so they don’t have to manually re-enter the line quantities.
Now, the code…the sourceLines parameter is a tilde separated list of the lines that are selected for duplication, which I am splitting into the callContextBpmData.Number02 field, and the sourceQuote is a parameter containing the original quote number.
Let me know if those are the details you were looking for or if I need to be more clear…
//first split
var lines = sourceLines.Split(’~’);
//only iterate over details that are in source lines
foreach (var tt in ttQuoteDtl.Where(d => d.Added() || d.Updated() && lines.Contains(p.QuoteLine.ToString()))
{
//now we get the related quote dtls
var q = Db.QuoteDtl.Where(p => p.Company == tt.Company && p.QuoteNum == tt.QuoteNum && p.QuoteLine == tt.QuoteLine).FirstOrDefault();
if (q != null) //if null, then either that dtl doesnt exist, or was excluded because of your callContext condition above
{
q.OrderQty = tt.OrderQty;
q.SellingExpectedQty = tt.SellingExpectedQty;
//q.RowMod = "U"; //i dont think this is used in a db record...
}
} //only update once all records are modified
Db.Validate();
Note that when you are duplicating, you should only have a quote head for the quote you are duplicating, no need to specify source quote. Also, the ttQuoteDtls are all of the lines of the duplicated quote. In the code above, we are going to iterate only details which have lines that exist within your sourcelines list
Btw - I wonder if you couldnt just handle all of this in the Pre-Process. Just modify the ttValues directly before they are saved. Then you wont have to do this lookup in the Db.
Ah yes… because I am duping from the tt values…which are 0. Boing! (That source quote is making more sense now)
Well we could move it around to fix that but I sure hate to do 2 db lookups when we could do it with one in the pre-process. Def give it a try.
in the post-process
//first split
var lines = sourceLines.Split(’~’);
//only iterate over details that are in source lines
foreach (var tt in ttQuoteDtl.Where(d => d.Added() || d.Updated() && lines.Contains(p.QuoteLine.ToString()))
{
//now we get the related quote dtls
var q = Db.QuoteDtl.Where(p => p.Company == tt.Company && p.QuoteNum == tt.QuoteNum && p.QuoteLine == tt.QuoteLine).FirstOrDefault();
var oq = Db.QuoteDtl.Where(p => p.Company == tt.Company && p.QuoteNum ==SourceQuote && p.QuoteLine == tt.QuoteLine).FirstOrDefault();
if (q != null && oq != null) //if null, then either that dtl doesnt exist
{
q.OrderQty = oq.OrderQty;
q.SellingExpectedQty = oq.SellingExpectedQty;
//q.RowMod = "U"; //i dont think this is used in a db record...
}
} //only update once all records are modified
Db.Validate();
I still have to convert one of my CopyLines BPMs… I think the source contains more than just line(s)…
See My Old ABL
// NEW TODO:
NewSourceQuoteLines = ((i == 1) ? sourceQuoteLines.Entry(i - 1, Ice.Constants.ListSeparator) : NewSourceQuoteLines + Ice.Constants.ListSeparator + sourceQuoteLines.Entry(i - 1, Ice.Constants.ListSeparator));
sourceQuote = Compatibility.Convert.ToInt32(sourceQuoteLines.Entry(i - 1, Ice.Constants.ListSeparator).Entry(1, '`'));
sourceLine = Compatibility.Convert.ToInt32(sourceQuoteLines.Entry(i - 1, Ice.Constants.ListSeparator).Entry(0, '`'));
// END TODO
DEFINE VARIABLE myArray AS CHARACTER EXTENT 30 FORMAT "X(30)".
DEFINE VARIABLE myLoopIndex AS INTEGER INIT 1 NO-UNDO.
DEFINE VARIABLE iNumEntries AS INTEGER NO-UNDO.
DEFINE VARIABLE iLoop AS INTEGER NO-UNDO.
DEF BUFFER bNewUD06 FOR UD06.
DEF VARIABLE sCurrentQuoteNum AS CHARACTER NO-UNDO.
DEF VARIABLE sCurrentQuoteLine AS CHARACTER NO-UNDO.
// Count How Many We are Copying
ASSIGN iNumEntries = NUM-ENTRIES(sourceQuoteLines, '~~').
message "sourceQuoteLines: " + sourceQuoteLines.
// Lets Determine All the Lines we have to work with, so we can find the NEWEST Lines
// since Epicors Line Numbering is not Sequential we cant just guess
// we have to count the QuoteDtl because the tt only has the newly added lines
for first ttQuoteDtl, each QuoteDtl WHERE QuoteDtl.QuoteNum = ttQuoteDtl.QuoteNum no-lock by QuoteDtl.QuoteLine.
message "- QUOTEDTL FOUND: " + string(QuoteDtl.QuoteLine).
myArray[myLoopIndex] = string(QuoteDtl.QuoteLine).
myLoopIndex = myLoopIndex + 1.
end.
// Lets Loop through our Lines to be copied
DO iLoop = 1 TO iNumEntries:
// Assign Some Vars
assign sCurrentQuoteNum = ENTRY(2, ENTRY(iLoop, sourceQuoteLines, '~~'), '`').
assign sCurrentQuoteLine = ENTRY(1, ENTRY(iLoop, sourceQuoteLines, '~~'), '`').
// Check if the Line we are copying has a UD06
FIND FIRST UD06 WHERE UD06.Key1 = sCurrentQuoteNum AND UD06.Key2 = sCurrentQuoteLine no-error.
message "Checking for UD06".
IF AVAILABLE UD06 THEN DO:
CREATE bNewUD06.
BUFFER-COPY UD06 EXCEPT sysrevid sysrowid to bNewUD06
ASSIGN bNewUD06.Key1 = string(targetQuoteNum)
bNewUD06.Key2 = string( myArray[ (myLoopIndex - iNumEntries - 1) + iLoop] ).
END.
END.