Adding Attachments to PO Lines during PO Generation/Creation within New PO Suggestions

:Grumble:
I can’t even get the vendor number from the POSugg.Generate BO.

Why not? that’s there

Not sure.
I just put in a message widget to spit out the vendor number and it came up blank.

Did you put a RowMod condition on it? If so that’s your problem the record is there but it isn’t RowModed… Also you could use the SugList parameter and split it and do lookups in code

So here’s a “HACK”

I created a Pre-Processing directive on POSug.Generate method and set my CallContextBPMData.Character01 = “TEST RAINBOW”

Then in a Data Directive in POHeader condition (Added) I display that CallContextBPMData.Character01. My hope was that it would pass the call context along (and it does).
http://jcg.pictures/gdggujdsQiaIhj.png

So if you stick some clever references in the CallContext in Generate IE A GUID to tell you which POs were created at that time. Stick that GUID from the call context in a UDfield in POHeader. Then on Post Processing on Generate you can find all PO’s that have that GUID in the UD field to know which are the ones you need to frock with :wink:

2 Likes

Lol, no kidding.
They certainly didn’t make this easy, did they?

Breaking it Down

Pre-Processing Directive on Generate
Set BPMCallContext.Character01 = Guid.NewGuid();

In Tran Data Directive on POHeader

Condition Row Added
IF BPMCallContext.Character01 <> Empty
Then Set POHeader.SuggestionGuid_c = BPMCallContext.Character01;

Then Post-Procesing BPM on Generate
Find all POHeaders which contain POHeader.SuggestionGuid_c == BPMCallContext.Character01
boom you found all youe newly created POs…
Reset BPMCallContext.Character01 = " " //Be a good citizen and clean up

Then smooth sailing call BO’s to add attachments etc.

4 Likes

While you were working on that, this was my approach. Yours seems a little safer, however.

        callContextBpmData.Character01 = "";
        foreach(var r in ttSugPoDtl)
        {
           int vNum = (int)r.VendorNum;
           string jNum = r.JobNum.ToString();
           int oNum = (int)r.OrderNum;
           int oLine = (int)r.OrderLine;
           int oRel = (int)r.OrderRelNum;
           string pNum = r.PartNum.ToString();
           DateTime dDate = Convert.ToDateTime(r.DueDate);

           int poNum = (
              from ph in Db.POHeader.With(LockHint.NoLock)
              join pd in Db.PODetail.With(LockHint.NoLock) on new {ph.Company, PO = ph.PONum} equals new {pd.Company, PO = pd.PONUM}
              join pr in Db.PORel.With(LockHint.NoLock) on new {pd.Company, POn = pd.PONUM, pd.POLine} equals new {pr.Company, POn = pr.PONum, pr.POLine}
              where ph.VendorNum==vNum &&
                       pr.JobNum==jNum &&
                       pd.OrderNum==oNum &&
                       pd.OrderLine==oLine &&
                       pd.PartNum==pNum &&
                       pd.DueDate==dDate
              select ph.PONum).DefaultIfEmpty(0).FirstOrDefault();

           callContextBpmData.Character01 = poNum.ToString() + Environment.NewLine;
        }

1 Like

*** Edit - I got it. Apparently, that’s yelling at me for having nested Linq statements? Or something to that affect? I rewrote one and put the data into a list to iterate through and it seemed to work. Just gotta button up a few things and it should be good to go.

I’m having issues with threading, apparently. This is normally solved by adding a transaction scope, which I did, but it didn’t seem to help. Maybe it’s in the wrong spot?

Error:

  135563

  =====================================================================
  Trace: System.Data.EntityException: The underlying provider failed on EnlistTransaction. ---> System.Data.SqlClient.SqlException: The operation failed because the session is not single threaded.
     at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
     at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
     at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
     at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
     at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
     at System.Data.SqlClient.SqlInternalConnectionTds.PropagateTransactionCookie(Byte[] cookie)
     at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
     at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
     at System.Data.SqlClient.SqlInternalConnection.EnlistTransaction(Transaction transaction)
     at System.Data.SqlClient.SqlConnection.EnlistTransaction(Transaction transaction)
     at Epicor.Data.Provider.EpiConnection.EnlistTransaction(Transaction transaction) in C:\_Releases\ICE\3.1.500.16\Source\Framework\Epicor.System\Data\EpiProvider2\EpiConnection.cs:line 209
     at System.Data.EntityClient.EntityConnection.EnlistTransaction(Transaction transaction)
     --- End of inner exception stack trace ---
     at System.Data.EntityClient.EntityConnection.EnlistTransaction(Transaction transaction)
     at System.Data.Objects.ObjectContext.EnsureConnection()
     at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
     at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
     at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
     at System.Data.Objects.CompiledQuery.ExecuteQuery[TResult](ObjectContext context, Object[] parameterValues)
     at Epicor.Data.DBExpressionCompiler.GetResult[TContext,TQuery,TResult](Func`3 executeQuery, Cache cacheSetting, TContext dataContext, TQuery query) in C:\_Releases\ICE\3.1.500.16\Source\Framework\Epicor.System\Data\DBExpressionCompiler.cs:line 441
     at Epicor.Data.DBExpressionCompiler.InvokeSingle[TContext,TQuery,TResult](Expression expression, Cache currentCacheSetting, Boolean cacheQuery, TContext dataContext, Func`2 getDataCacheKey, Func`2 compileQuery, Func`3 executeQuery) in C:\_Releases\ICE\3.1.500.16\Source\Framework\Epicor.System\Data\DBExpressionCompiler.cs:line 302
     at Epicor.Data.DBExpressionCompiler.<>c__DisplayClass33_0`3.<Compile>b__0(TContext context, TArg1 arg1) in C:\_Releases\ICE\3.1.500.16\Source\Framework\Epicor.System\Data\DBExpressionCompiler.Generated.cs:line 1082
     at Erp.Services.BO.POSvc.Initialize() in C:\_Releases\ERP\UD10.1.500.16\Source\Server\Services\BO\PO\PO.cs:line 743
     at Epicor.Hosting.OperationBoundClass.Initialize(Operation operation, Boolean root) in C:\_Releases\ICE\3.1.500.16\Source\Framework\Epicor.System\Hosting\OperationBoundClass.cs:line 55
     at Epicor.Customization.Bpm.SvcFacadeBase`3.CoreInitialize(Operation operation, Boolean root) in c:\_Releases\ICE\3.1.500\Current\Source\Server\Internal\Lib\Epicor.Customization.Bpm\SvcFacadeBase.Generic.cs:line 93
     at Epicor.Hosting.OperationBoundClass.Initialize(Operation operation, Boolean root) in C:\_Releases\ICE\3.1.500.16\Source\Framework\Epicor.System\Hosting\OperationBoundClass.cs:line 53
     at Ice.Assemblies.ServiceRenderer.GetService[TService](IceDataContext context, Boolean ignoreFacade) in C:\_Releases\ICE\3.1.500.16\Source\Framework\Epicor.System\Assemblies\ServiceRenderer.cs:line 123
     at Epicor.Customization.Bpm.BO8CEB157C0F9F41A1A98F39A807931631.GeneratePostProcessingDirective_POSuggAttach_ADW_C9E8719FCDF445F3B8D743B74A85BCD1.A001_CustomCodeAction()
  =====================================================================

Code:

  callContextBpmData.Character01 = "";
  callContextBpmData.Character08 = "";
  const string RELATEDPART = "Part";

  foreach(var r in ttSugPoDtl)
  {
     int vNum = (int)r.VendorNum;
     string jNum = r.JobNum.ToString();
     int oNum = (int)r.OrderNum;
     int oLine = (int)r.OrderLine;
     int oRel = (int)r.OrderRelNum;
     string pNum = r.PartNum.ToString();
     DateTime dDate = Convert.ToDateTime(r.DueDate);

     foreach(var poNum in(
        from ph in Db.POHeader.With(LockHint.NoLock)
        join pd in Db.PODetail.With(LockHint.NoLock) on new {ph.Company, PO = ph.PONum} equals new {pd.Company, PO = pd.PONUM}
        join pr in Db.PORel.With(LockHint.NoLock) on new {pd.Company, POn = pd.PONUM, pd.POLine} equals new {pr.Company, POn = pr.PONum, pr.POLine}
        where ph.VendorNum==vNum &&
                 pr.JobNum==jNum &&
                 pd.OrderNum==oNum &&
                 pd.OrderLine==oLine &&
                 pd.PartNum==pNum &&
                 pd.DueDate==dDate
        select new {pd}))
     using(var scope = IceDataContext.CreateDefaultTransactionScope())
     {
        if( poNum.pd != null )
        {
           callContextBpmData.Character01 += poNum.pd.PONUM.ToString() + Environment.NewLine;
           int vPONum = (int)poNum.pd.PONUM;
           int vPOLine = (int)poNum.pd.POLine;
           int iter = 0;
           string strPONum = poNum.pd.PONUM.ToString();

           try{
              using(var poBO = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.POSvcContract>(Db))
              {
                 POTableset poTS = new POTableset();
                 poTS = poBO.GetByID(vPONum);
        
                 foreach( var rows in (
                    from xa in Db.XFileAttch.With(LockHint.NoLock)
                    join xr in Db.XFileRef.With(LockHint.NoLock) on new {xa.Company, xa.XFileRefNum} equals new {xr.Company, xr.XFileRefNum}
                    where xa.Company==Session.CompanyID && xa.RelatedToFile==RELATEDPART && xa.Key1==pNum
                    select new {xr}))
                 {
                    poBO.GetNewPODetailAttch(ref poTS, vPONum, vPOLine);
                    
                    poTS.PODetailAttch[iter].Company = Session.CompanyID;
                    poTS.PODetailAttch[iter].PONUM = vPONum;
                    poTS.PODetailAttch[iter].POLine = vPOLine;
                    poTS.PODetailAttch[iter].DrawingSeq = iter;
                    poTS.PODetailAttch[iter].XFileRefNum = 0;
                    poTS.PODetailAttch[iter].SysRevID = 0;
                    poTS.PODetailAttch[iter].SysRowID = Guid.Empty;
                    poTS.PODetailAttch[iter].ForeignSysRowID = Guid.Empty;
                    poTS.PODetailAttch[iter].DrawDesc = rows.xr.XFileDesc.ToString();
                    poTS.PODetailAttch[iter].FileName = rows.xr.XFileName.ToString();
                    poTS.PODetailAttch[iter].PDMDocID = "";
                    poTS.PODetailAttch[iter].DocTypeID = rows.xr.DocTypeID.ToString();
                    poTS.PODetailAttch[iter].RowMod = "A";
                    iter++;
                 }
                    poBO.Update(ref poTS);
              }
           }
           catch(Exception e)
           {
              callContextBpmData.Character08 = "=====================================================================" + Environment.NewLine + 
                                                               "Trace: " + e.ToString() + Environment.NewLine +
                                                               "=====================================================================" + Environment.NewLine;
           }
        }
        scope.Complete();
     }
  }

As a rule I don’t like to iterate through LINQ results. I prefer a ToList() and then iterate. LINQ does lazy execution which is cool (most of the time) but if you are iterating through DB objects it can cause issues.

Got it working.
Here’s the final product (such as it is):

      callContextBpmData.Character01 = "";
      callContextBpmData.Character08 = "";
      const string RELATEDPART = "Part";
      const string RELATEDPO = "PODetail";

      foreach(var r in ttSugPoDtl)
      {
         int vNum = (int)r.VendorNum;
         string jNum = r.JobNum.ToString();
         int oNum = (int)r.OrderNum;
         int oLine = (int)r.OrderLine;
         int oRel = (int)r.OrderRelNum;
         string pNum = r.PartNum.ToString();
         DateTime dDate = Convert.ToDateTime(r.DueDate);

         var poNumList = (
            from ph in Db.POHeader.With(LockHint.NoLock)
            join pd in Db.PODetail.With(LockHint.NoLock) on new {ph.Company, PO = ph.PONum} equals new {pd.Company, PO = pd.PONUM}
            join pr in Db.PORel.With(LockHint.NoLock) on new {pd.Company, POn = pd.PONUM, pd.POLine} equals new {pr.Company, POn = pr.PONum, pr.POLine}
            where ph.VendorNum==vNum &&
                     pr.JobNum==jNum &&
                     pd.OrderNum==oNum &&
                     pd.OrderLine==oLine &&
                     pd.PartNum==pNum &&
                     pd.DueDate==dDate
            select new {pd}).ToList();

         if( poNumList.Count > 0 )
         {
            foreach(var poNum in poNumList)
            {
               callContextBpmData.Character01 += poNum.pd.PONUM.ToString() + Environment.NewLine;
               int vPONum = (int)poNum.pd.PONUM;
               int vPOLine = (int)poNum.pd.POLine;
               int iter = 0;
               string strPONum = poNum.pd.PONUM.ToString();
         
               try{
                  using(var poBO = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.POSvcContract>(Db))
                  {
                     POTableset poTS = new POTableset();
                     poTS = poBO.GetByID(vPONum);

                     iter = (
                        from xa in Db.XFileAttch.With(LockHint.NoLock)
                        join xr in Db.XFileRef.With(LockHint.NoLock) on new {xa.Company, xa.XFileRefNum} equals new {xr.Company, xr.XFileRefNum}
                        where xa.Company==Session.CompanyID && xa.RelatedToFile==RELATEDPO && xa.Key1==strPONum
                        select xa.AttachNum).Count();
            
                     foreach( var rows in (
                        from xa in Db.XFileAttch.With(LockHint.NoLock)
                        join xr in Db.XFileRef.With(LockHint.NoLock) on new {xa.Company, xa.XFileRefNum} equals new {xr.Company, xr.XFileRefNum}
                        where xa.Company==Session.CompanyID && xa.RelatedToFile==RELATEDPART && xa.Key1==pNum
                        select new {xr}))
                     using(var scope = IceDataContext.CreateDefaultTransactionScope())
                     {
                        poBO.GetNewPODetailAttch(ref poTS, vPONum, vPOLine);
                        
                        poTS.PODetailAttch[iter].Company = Session.CompanyID;
                        poTS.PODetailAttch[iter].PONUM = vPONum;
                        poTS.PODetailAttch[iter].POLine = vPOLine;
                        poTS.PODetailAttch[iter].DrawingSeq = iter;
                        poTS.PODetailAttch[iter].XFileRefNum = 0;
                        poTS.PODetailAttch[iter].SysRevID = 0;
                        poTS.PODetailAttch[iter].SysRowID = Guid.Empty;
                        poTS.PODetailAttch[iter].ForeignSysRowID = Guid.Empty;
                        poTS.PODetailAttch[iter].DrawDesc = rows.xr.XFileDesc.ToString();
                        poTS.PODetailAttch[iter].FileName = rows.xr.XFileName.ToString();
                        poTS.PODetailAttch[iter].PDMDocID = "";
                        poTS.PODetailAttch[iter].DocTypeID = rows.xr.DocTypeID.ToString();
                        poTS.PODetailAttch[iter].RowMod = "A";
                        iter++;
                        scope.Complete();
                     }
                     poBO.Update(ref poTS);
                  }
               }
               catch(Exception e)
               {
                  callContextBpmData.Character08 = "=====================================================================" + Environment.NewLine + 
                                                                   "Trace: " + e.ToString() + Environment.NewLine +
                                                                   "=====================================================================" + Environment.NewLine;
               }
            }
         }
      }

So is this running in post proc generate?

Yup.

and your ttSugPoDtl isn’t empty at that point? #Confused

All that you know is a lie.

… Also, I have no idea. Lol. I wield my C# sword without aim.

1 Like

According to my trace your ttPOSug should be empty in post Proc… Did you click “BUY” on these suggestions? My spidy-sense says this shouldn’t be working

Yes, they were marked as “BUY” and then I clicked generate.

Well… sh*t I’m dumbfounded… oh well if it ain’t broke… however I believe this will fail if they click “BUY” and then leave the screen or clear it and come back and hit generate later. I believe generate doesn’t actually care about what you have selected on the screen it generates ANY PoSug with the Buy Checkbox whether it is in the screen or not…
Not sure if that matters in your case but try it, open po Sug, Mark Buy… clear screen , load another POSug, (don’t mark buy) hit generate. I believe the original PO sug will still generate and its information will NOT be in the ttSug table.

1 Like

Boo, you’re right. It did not work.
I’ll have to mess around with this some more tomorrow…

You may have to try that Pre DD Post hack that should work regardless of which SugCreated since you’ll be triggering on Data Directive POHeader.Added… what a pain