Create Job Adjustment in BPM C#

I’m trying to create a job adjustment in a BPM. I receive an error though as soon as it tries to assign the company value. It looks like it doesn’t recognize row 0.

I’m used to working with BOs where you populate the dataset using GetByID. That doesn’t exist for JobAdajusment

Any Ideas?

      {
        Erp.Tablesets.JobAdjustmentTableset thisDS = new Erp.Tablesets.JobAdjustmentTableset();
        thisDS.Jobs[0].Company = LaborDtlRow.Company;
        thisDS.Jobs[0].JobNum = LaborDtlRow.JobNum;
        thisDS.Jobs[0].RowMod = "U";
        JA.StartAdjustments(ref thisDS);    
        JA.ChangeLaborOprSeq(LaborDtlRow.OprSeq, ref thisDS);
        JA.ChangeLaborEmployeeNum(LaborDtlRow.EmployeeNum, ref thisDS);
        thisDS.JALaborDtl[0].LaborQty = qtyDiff;
        JA.ValidateChargeRateForTimeType(ref thisDS, out pString);
        JA.CommitLaborAdj(ref thisDS);
        
        JA.Dispose();
        callContextBpmData.Character10 = "Adjustment Fired";
  
      }

I just did something similar, doing a labor adjustment with a BPM, but I took a different approach. Even though I knew I could write it in C#, I decided to do it all with Widgets.

  1. add widget 2 Invoke BO Method… inside that define the method, and the variables. It automatically will create the needed variables for you (after you give them a name)
  2. Add widget 1 to “fill” the newly created variable for the LaborDtl record with the new adjustment you want.
    connecct the two widgets…
    Note that filling the LaborDtl requires all the necessary data. Some can be hard coded, some can come from other variables or BPMContext variables.
    image
3 Likes

Tim, Thanks for the assist. That got me going.

Here is the working code

using(var JA = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.JobAdjustmentSvcContract>(this.Db))
						  {
							Erp.Tablesets.JobAdjustmentTableset jads = new Erp.Tablesets.JobAdjustmentTableset();
							var addLbrDtl = new Erp.Tablesets.JALaborDtlRow();
							addLbrDtl.ClockInDate = clockInDate;
							addLbrDtl.LaborNote = "Pre-Receipt Adjustment: " + Convert.ToString(qtyDiff);
							addLbrDtl.AssemblySeq = LaborDtlRow.AssemblySeq;
							addLbrDtl.OprSeq = LaborDtlRow.OprSeq;
							addLbrDtl.QtyCompleted = qtyCompleted;
							//addLbrDtl.ActProdHours = LaborDtlRow.ActProdHours;
							//addLbrDtl.ActSetupHours = LaborDtlRow.ActSetupHours;
							//addLbrDtl.ActBurCost = LaborDtlRow.ActBurCost;
						   // addLbrDtl.ActLabCost = LaborDtlRow.ActLabCost;
							addLbrDtl.EmployeeNum = LaborDtlRow.EmployeeNum;
						   // addLbrDtl.EmployeeName = LaborDtlRow.EmployeeName;
							addLbrDtl.LaborQty = qtyDiff;
							addLbrDtl.LaborType = LaborDtlRow.LaborType;
							addLbrDtl.LaborHrs = 0;
							//addLbrDtl.LaborCost = LaborDtlRow.LaborCost;
							addLbrDtl.BurdenHrs = 0;
							//addLbrDtl.BurdenCost = LaborDtlRow.BurdenCost;
							addLbrDtl.Complete = LaborDtlRow.Complete;
							addLbrDtl.OpComplete = LaborDtlRow.OpComplete;
							addLbrDtl.company = LaborDtlRow.Company;
						   //addLbrDtl.LaborHedSeq = LaborDtlRow.LaborHedSeq;
						   //addLbrDtl.LaborDtlSeq = LaborDtlRow.LaborDtlSeq;
							addLbrDtl.OpCode = LaborDtlRow.OpCode;
							addLbrDtl.ResourceGrpID = LaborDtlRow.ResourceGrpID;
							//addLbrDtl.JcDept = LaborDtlRow.JcDept;
							addLbrDtl.JobNum = LaborDtlRow.JobNum;
							addLbrDtl.LaborEntryMethod = "T";
							addLbrDtl.EnableLaborQty = true;
							addLbrDtl.EnableScrapQty = true;
							addLbrDtl.EnableDiscrepQty = true;
							//addLbrDtl.LaborRoleCd = LaborDtlRow.LaborRoleCd;
							addLbrDtl.LaborDisTimeTypCd = true;
							addLbrDtl.LaborDisPrjRoleCd = true;
							//addLbrDtl.LaborTimeTypeCd = LaborDtlRow.LaborTimeTypeCd;
							addLbrDtl.RowMod = "U";
							jads.JALaborDtl.Add(addLbrDtl);
							
							JA.CommitLaborAdj(ref jads);
							JA.Dispose();
					 
						  }
9 Likes

Hi Zack

So the timing of you posting this is perfect as we were just in the middle of trying something similar. We have very large assemblies, and our supervisors, at certain points, have the need to go through multiple jobs and close operations (10-15 at a time, over multiple jobs) to ensure things are moving along in the system. We made a dashboard that focuses on JobOper that enables them to close the operation (which moves the item along in peoples work queue) but I would also like to create a “job Adjustment” at the same time so that when we look through the labor transactions we can see that the operation was closed by the supervisor.

I believe that we can use your code (which slight adjustments) but am stuck as to the best place to place the BPM to get it to fire when we want it (I have created a UD field in the labordtl table that is the “Operation Closed Override” checkbox that would be Condition trigger in the BPM…

Any guidance would be appreciated!

BIG NOTE HERE: Job Adjustments are “not quite” the same as regular job completions. I found that if you do a job adjustment to finish the final operation on a job, ALMOST everything is the same… parts backflush, job shows that a quantity is completed… all good…
BUT, if the job is Make Direct, “Normally”, when you complete the final operation, the PartAlloc table is updated showing that the job’s parts are allocated to the sales order release… but for some reason, adjustments do not do this. I have NOT tried “Auto Receive” but it might have the same issue as well.

1 Like

Hi Tim,

When you say

Note that filling the LaborDtl requires all the necessary data. Some can be hard coded, some can come from other variables or BPMContext variables.

are you referring to the Binding’s within the Setup Table Mapping window under widget 1?

Yes, your bindings can be any existing variable

Got it :slight_smile:

So all of these bindings are required when updating the LaborDtl Table? If so how would you handle the LaborHeadSeq, LaborDtlSeq, SysRowID, RowID and other system generated fields if there is NOT already a LaborDtl row cretaed? Or is this even possible?

Thank you!

You would need to make a new LaborDtl with the adapter which will handle the laborDtl Sequence, SysRowID and SysRev. You don’t need LaborHedSeq since this would be like a backflush not done by a clocked in employee.

Thanks for the notes/examples!

BL Tester and Traces were getting me close but… your code really got me “over the hump” with populating/committing the JobAdjustmentTableset. Saved a lot of time.

Using your C# I built a VERY basic UBAQ/dashboard for the paste/insert job labor adjustments.

Attached zip that includes basic files ( E10.1.6xxx ) in case anyone else is interested in adjustments.
UBAQ_JobAdj.zip (47.3 KB)

2 Likes

You actually figure out what you need brother?

1 Like

ya-- you know what I was being stupid. My code was fine as far as the Job Cost adjustment. I was referencing and looping through nothing thinking I was working through “added” rows in a UD table. I guess the datasets and temp tables work differently on the UD table methods-- I couldn’t figure out how to access the temp table of rows so I just sent the row info for 1 row from the pre directive thru bpm fields.

Using the widgets is it possible to perform a job adjsutment on more than one operation on the same Job or does this need to be coded?

No, there is no foreach loop widget. It will require code.

Thanks @aarong - I always try to use the widgets but have never succeeded on anything that requires multiple records.
Thanks for confirming.

6 posts were split to a new topic: Simple Example → Widget Loop (If you want to vomit.) :rofl:

Short answer, it can be done. Recommended?

Bored Married With Children GIF

Put the widget loop discussion here:

https://www.epiusers.help/t/simple-example-widget-loop-if-you-want-to-vomit/119254