E10 duplicating quotes, why the field ParentQuoteNum is not filled?

I have noticed that some quotes that were created from duplicating an original copy, do not have the field ParentQuoteNum with the original quote number. Some quote do have it filled, but I am not sure how…

Question: When does it fill or not fill ? what is the logic regarding this field?

My goal: I was asked to generate an exact copy of an original quote (copying reference, CRMcalls quantities etc… ) and I thought of using the ParentQuoteNum to go read the missing info after the copy is created… but found out the ParentQuoteNum is 0…

Am I missing something? is this a bug? How can I make sure this field contains the parent quote number?

Thanks

Pierre

The field does not mean the quote from which this one was created… explanation below.

Best
…Monty.

Parent Quote
Identifies the number of a quote that is directly above the current quote within the parent-child hierarchy. This field is available if you use the Customer Relationship Management (CRM) module. To search for a parent quote, click the Parent Quote button and then use the Quote Search program.

OK, so you are telling me to create a UD field which will contain the parent quote used to generate the new quote.

I am ok with that…I was working on that but I am not sure how to catch the original quote ans save it to the new one… I noticed there is a DuplicateQuote method directive available, but really not sure how to use it, in order to catch the original, and later save it in the copy…

What would be best way?

Thanks

Pierre

OK, I got the sourcequote info in PostProcessing of the DuplicateQuote directive.

My original question was about : If duplicating a quote is not filling the ParentQuoteNum field of the new quote. When is such field filled ? Searching for a parent would not work if this field has no info…

Could it be a viable idea to fill this field with the parent quote via BPM, during duplication?

Thanks

Pierre

I have yet to convert mine to E10. But In order to accomplish the following:

I relied on ParentQuoteNum and had some BPMs in Place:

The ParentQuoteNum had to belong to the same Customer in my case (for Revision sake)

The E9 vs E10 Logic might have changed a bit. I have a TODO note to test :slight_smile:

You might have to do what I did despite having access to ttQuoteHed during the Duplication, you might add your logic in the POST of DuplicateQuote and then get the New Quote’s actual Db.QuoteHed record and make change to that instead, since changing anything in ttQuoteHed yielded no results, neither in the PRE or the POST – Thats E9; might have changed in E10.

MAYBE E10 Logic you will have to do this (Sorry just typing without having a env infront of me now) I know there was 1 BPM I had to do it this way, It could have been Erp.Quote.CopyLines – but anyways just a heads-up!

Quote.DuplicateQuote.PRE Method Directive: (Might not need this!)

callContextBpmData["Character01"] = Convert.ToString(sourceQuote);

I think, the sourceQuote in E10 get’s wiped (again might not be the case on this BPM) in the In-Transaction if no good, if it does get wiped - you can access the stored sourceQuote using:

callContextBpmData["Character01"]

Quote.DuplicateQuote.POST Method Directive:

// Main Query
var quotes =
	from    tqh in ttQuoteHed
	join 	qh in Db.QuoteHed.With(LockHint.UpdLock) on tqh.QuoteNum equals qh.QuoteNum
	select  qh;

foreach (var quoteRow in quotes)
{
	//quoteRow["ShortChar01"] = "Revision";
	quoteRow.ParentQuoteNum = sourceQuote; // worst case if sourceQuote isnt populated in POST then use callContextBpmData["Character01"]

	Db.Validate(quoteRow);
}

Thank you for your input, but this is what I ended up doing to test with in PostProcessing

Add the following Custom code (sourceQuote is available in Post…)


if(ttQuoteHed != null)
{
		foreach (var hed in ttQuoteDtl)
		{
				var OriHedqt = Db.QuoteHed.Where(p => p.Company == hed.Company && p.QuoteNum == sourceQuote );
				var CopyHedqt =  Db.QuoteHed.Where(p => p.Company == hed.Company && p.QuoteNum == hed.QuoteNum );
		
				CopyHedqt.Reference = OriHedqt.Reference;
				if( OriHedqt.ExpirationDate != null) 
						CopyHedqt.ExpirationDate =  OriHedqt.ExpirationDate;
				if( CopyHedqt.FollowUpDate != null )
						CopyHedqt.FollowUpDate =  OriHedqt.FollowUpDate;
				
				Db.Validate();
		}
}

if(ttQuoteDtl != null)
{
		
		foreach (var tt in ttQuoteDtl)
		{
			
				var ParentLine = 	Db.QuoteDtl.Where(p => p.Company == tt.Company && p.QuoteNum == sourceQuote && p.QuoteLine == tt.QuoteLine);
				
				var NewLine = Db.QuoteDtl.Where(p => p.Company == tt.Company && p.QuoteNum == tt.QuoteNum && p.QuoteLine == ParentLine.QuoteLine);
				NewLine.OrderQty = ParentLine.OrderQty;
				NewLine.QuoteComment = ParentLine.QuoteComment;
				NewLine.BestCsPct = ParentLine.BestCsPct;
				NewLine.ConfidencePct = ParentLine.ConfidencePct;
				
		}
		Db.Validate();	
}

The result is has wanted.

thanks

Now I need to know how to create CRMCall records from the source to the new quote… Unsure of the correct syntax…

Pierre

Typically it’s just you Query it based on Quote Key Fields, just check the DB for a QuoteNum related one.

Here is an example of me creating a NEW Quote Audit Record (Just to demo the concept of creating)

using (var txScope = IceDataContext.CreateDefaultTransactionScope())
{
	Ice.Diagnostics.Log.WriteEntry(String.Format("[ {0} ] Starting Transaction...", bpmName));

	// Create New
	Erp.Tables.QuoteAdt QuoteAdt = new Erp.Tables.QuoteAdt();
	Db.QuoteAdt.Insert(QuoteAdt);

	QuoteAdt.Company = Session.CompanyID;
	QuoteAdt.QuoteNum = quoteRow.tqh.QuoteNum;
	QuoteAdt.ChangeDate = quoteRow.tqh.ChangeDate;
	QuoteAdt.ChangeTime = System.Convert.ToInt32(DateTime.Now.TimeOfDay.TotalSeconds);
	QuoteAdt.ChangedBy = Session.UserID;
	QuoteAdt.ChangeDescription = "[CHANGES] \n \n" + changeList;

	// Do I have to call - Validate does it
	//Db.QuoteAdt.Update(QuoteAdt)

	// Commit to Database
	Db.Validate(QuoteAdt);

	// Complete Transaction
	txScope.Complete();

	Ice.Diagnostics.Log.WriteEntry(String.Format("[ {0} ] Ending Transaction...", bpmName));
}

What you can do is you can use BufferCopy once you Query the original Call Record(s) and Duplicate it:
Example 1 (Jose)

BufferCopy.CopyExceptFor(curRev, newRev, "SysRowID","SysRevID","RevisionNum", "EffectiveDate", "RowMod");

Example 2 (Me Duplicating my UD06 - i do something similar with other UD Tables I use):
MOST PEOPLE MISS THIS! If you have any UD Tables, DUPLICATE will not magically bring them over.

// Main Query
var quotes =
	from    tqd in ttQuoteDtl
	select  tqd;

foreach (var quoteRow in quotes)
{
	// Get Existing UD06
	Ice.Tables.UD06 UD06 = (
		from ud06 in Db.UD06
		where ud06.Key1 == Convert.ToString(sourceQuote) && ud06.Key2 == Convert.ToString(quoteRow.QuoteLine)
		select ud06
	).FirstOrDefault();

	if (UD06 != null)
	{
		using (var txScope = IceDataContext.CreateDefaultTransactionScope())
		{
             // Create New
			Ice.Tables.UD06 NewUD06 = null;
			Db.UD06.Insert(NewUD06);
			BufferCopy.CopyExceptFor(UD06, NewUD06, UD06.ColumnNames.SysRevID, UD06.ColumnNames.SysRowID); // MAGIC
			NewUD06.Key1 = Convert.ToString(quoteRow.QuoteNum);
			NewUD06.Key2 = Convert.ToString(quoteRow.QuoteLine);

			// Commit to Database
			Db.Validate(NewUD06);

			// Complete Transaction
			txScope.Complete();
		}
	}
}

I assume you would do something similar, Query existing Call Record, then start creating your own, but using a foreach() loop and doing BufferCopy’s just, excluding SysRevID, SysRowID, QuoteNum etc… what u don’t want to duplicate. Then before you call Db.Validate() you just change what you want changed:

// After BufferCopy
YourNewCallRecord.Key1 = Convert.ToString(quoteRow.QuoteNum); // populate the new QuoteNum
// Db.Validate

There’s a little section on BufferCopy in this Guide: Sign In

You just have to figure out the RelatedToFile and Keys.
Example of someone Creating a CRMCall

CRMCall = new Erp.Tables.CRMCall();
Db.CRMCall.Insert(CRMCall);
CRMCall.Company = Session.CompanyID;
CRMCall.OrigDate = DateTime.Now.Date;
CRMCall.OrigTime = SecondsSinceMidnight(DateTime.Now.Date);
CRMCall.OrigDcdUserID = Session.UserID;
CRMCall.CallSeqNum = keyBlockNextCallSeqNum;
CRMCall.RelatedToFile = "Customer"; // You will prob need Quote or QuoteHed not sure check BAQ
CRMCall.Key1 = Convert.ToString(MktgLstDtl.CustNum); // QuoteNum (string)
CRMCall.Key2 = "";
CRMCall.Key3 = "";
CRMCall.CallCustNum = MktgLstDtl.CustNum;
CRMCall.CallShipToNum = MktgLstDtl.ShipToNum;
CRMCall.CallConNum = MktgLstDtl.ConNum;
CRMCall.CallContactType = "Customer";
CRMCall.CallTypeCode = ttMktgLstExpParam.CallType;
CRMCall.CallDesc = CallDescription;
CRMCall.CallText = Session.UserID + " / " + Convert.ToString(DateTime.Now.Date) + " / " + Convert.ToString(DateTime.Now.Hour) + ":" + Convert.ToString(DateTime.Now.Minute) + " / " + bMktgListResult.MktgListID + " / " + bMktgListResult.ListDescription;
Db.Validate(CRMCall);

Again - you will not create each one from Scratch if you use BufferCopy =) this is just to demo an example.

Make Adjustments to my examples to fit QuoteHed and Db.CRMCall

Also in the UI, I have stuff on AfterFieldChange especially the Dates to prompt for a Call Log (if you are changing a date, you obviously must have spoken to a Manager OR the Customer/Client) ** Just a share, may come in handy for you:

/* Due Date is Changed */
case "DueDate":
	DialogResult dialogResult = EpiMessageBox.Show("Changing the Due Date should only be done if authorized by the Customer. Would you like to Log a Call to Contact the Customer?", "Warning", MessageBoxButtons.YesNo);

	if (dialogResult == DialogResult.Yes)
	{
		// Launch Log a Call
		Erp.UI.App.CRMCallEntry.CRMCallArgs crmObject = new CRMCallArgs(RelatedToFiles.QuoteHed, args.Row["QuoteNum"].ToString(), "", "", "", "", "", false);
		ProcessCaller.LaunchForm(this.oTrans, "Erp.UI.CRMCallEntry", crmObject, false);
	}
	break;

Thanks Haso,

I want, when the copy is made, to also copy the CRMCalls or the Parent quote into the copy quote. What would be the valid syntax to perform this? I thought of using this code into the same BPM…

thanks

Pierre