going from 10.2.100 to 10.2.700, most of my predecessorās Db.Validate(); calls broke. The fix was to replace with BO calls (obviously preferred) or by explicitly specifying just what was being updated with ```Db.Validate(myVarName);``
Also some BO.Update(ref blahBlah); broke until implementing the BeforeImage concept found here.
Another one has been foreach(from var thing in Db.Table blah blah select thing){ do stuff} stopped working in some, not all, cases, and I had to first do var thing = (from th in Db.Table where blah blah select th).ToList()
and then foreach the list by itself.
Iām not defending widgets in their current state (actually hate 'em) but I can see that having Epicor test and vouch for them before upgrading is pretty useful, because the above examples are mostly trial-and-error.
Whenever I need to iterate, I use a custom code block. Everywhere else I try to use widgets. Itās easier for others to maintain, as not all of IT/power users are C# developers and the flow chart design is easier for them to follow.
The only other exception where I use custom code block is when a condition block destroys data. I have to use custom code blocks in order to do a simple condition test to make sure we donāt lose serial numbers.
I have had custom code blocks break on upgrades, so I keep track of the BPMs that have those so I can easily target those for testing on upgrades.
@jdewitt6029 I am sure there are some BPMs from the uplift to Epicor 9 that have a tt table joined to an ERP table.
Me (being new to Epicor) probably just converted it to C#, checked to make sure it was working, and called it a day. Not sure if this is stated in any customization guides or anything. Would be a great addition.
Same mostly. Iāve had very minor things break but nothing near as bad as screen customizations. On a major version I might spend 1 hr fixing little BPMs here and there with custom code and another 9 on screens. I also use custom code blocks heavily and avoid widgets in most cases.
Yeah, I curled up in front of the streaming TV fireplace with Orlicky and Plosslās MRP books. I decided to start with Orlickyās first edition and get a chronological overview of the state of the art through the decades.
Amen, Jason! I would LOVE a college-type course on Epicor BPMās & customization. Something where we build on weekly, have homework & test, a final project, the whole deal. I think that would close the loop for a lot of us non-computer science folks, and give enough time to learn the why, rather than rush through an example or two like Insights Extended Ed.
Having a teaching plan where each week we get a complete understanding of why something is done a certain way before building on it would be huge (at least for me). Rather than rote memorization of ādo this, and it worksā.
Not sure if Epicor or a third-party is willing to do that, but please take my $! Iām sure there are others in the same boat that fell into coding Epicor throughout their career trajectory, and didnāt come from a CompSci background.
I will say the words ābest practiceā āthis is what was missingā and āscalableā and I think our company would sign up at least a couple of people. Even if they donāt, Iām in.
Which makes calling a Business Object from a BO sometimes tricky. Nontheless. I wonder if Epicor Functions can use the BO.Proxy or if someone who calls multiple BOās in a eFX needs to really, really know what theyre doing (havent tried yet).
The BPM and EFx behave like any other server-side code. So, as in the case of other areas, it is the ādeveloperāsā responsibility to call a service method correctly.
Moreover, (it seems to me that Iāve mentioned that earlier), even client code creates diff-grams only when tableset parameter has ref modifier. If a parameter is āby valueā (no modifier provided), then a client proxy sends the whole dataset to the server.
Potentially, Invoke BO method action can be updated to behave like a client-side proxy, but it is the breaking change. I see only one solution that can be applied - a special check-box in Invoke BO Method action configuration (something like āmimicks smart client proxyā).
Actually, Iām mostly sure that this behavior was implemented as the performance optimization - to minimize data traffic between client and server. In the case of the server-only code, such behavior creates unneeded memory allocations
Gotcha. I should be fine. In cases when I need a more Client like approach when my Facades (BPMs) are legacy and not well written (should be fixed). I use something along the lines of:
This way I get Unmodified Row and Modified Row as well as required parent rows. But in most cases It may not matterā¦ Well unless you are doing an Order dataset with 100 lines and 500 releases
//
// For show only Code should be optimized
//
var dsReceipt = bo.GetByID(vendorNum, purPoint, packSlip);
// Probably another good way to fill it instead is using GetRows
//
// Example how to Copy a few Tableset rows to New Tableset
//
var dsNew = new Erp.Tablesets.ReceiptTableset {
RcvHead = {
Epicor.Data.BufferCopy.Clone(dsReceipt.RcvHead[0])
},
RcvMisc = {
Epicor.Data.BufferCopy.Clone(dsReceipt.RcvMisc.Where(x => x.MiscSeq == seqNum).FirstOrDefault()),
Epicor.Data.BufferCopy.Clone(dsReceipt.RcvMisc.Where(x => x.MiscSeq == seqNum).FirstOrDefault())
//new Erp.Tablesets.RcvMiscRow { EnableToBuildYourOwn }
}
};
dsNew.RcvMisc[0].RowMod = "U";
//
// Example how to Copy a LINQ Row to Tableset
//
// Erp.Tablesets.ReceiptTableset
var dsNew3 = new Erp.Tablesets.ReceiptTableset {
RcvHead = {
Epicor.Data.BufferCopy.Copy<Erp.Tables.RcvHead, Erp.Tablesets.RcvHeadRow>(Db.RcvHead.Where(s => s.Company == Session.CompanyID
&& s.PackSlip == packSlip
&& s.VendorNum == vendorNum
&& s.PurPoint == purPoint).FirstOrDefault())
},
// Erp.Tablesets.RcvMiscTable
RcvMisc = {
// Erp.Tablesets.RcvMiscRow
Epicor.Data.BufferCopy.Copy<Erp.Tables.RcvMisc, Erp.Tablesets.RcvMiscRow>(Db.RcvMisc.Where(s => s.Company == Session.CompanyID
&& s.PackSlip == packSlip
&& s.VendorNum == vendorNum
&& s.PurPoint == purPoint
&& s.MiscSeq == seqNum).FirstOrDefault()),
// Erp.Tablesets.RcvMiscRow
Epicor.Data.BufferCopy.Copy<Erp.Tables.RcvMisc, Erp.Tablesets.RcvMiscRow>(Db.RcvMisc.Where(s => s.Company == Session.CompanyID
&& s.PackSlip == packSlip
&& s.VendorNum == vendorNum
&& s.PurPoint == purPoint
&& s.MiscSeq == seqNum).FirstOrDefault())
}
};
dsNew3.RcvMisc[0].RowMod = "U";
bo.OnChangeMiscDocActualAmt(vendorNum, purPoint, packSlip, seqNum, _value, ref dsNew);
bo.Update(ref dsNew);
//
// Example how to Copy a LINQ Row to a LINQ Row (Erp.Tables.RcvMisc)
//
// Epicor Clone and Copy both will create a new SysRowID when the target is a LINQ Row
var LINQtoLINQExample = Epicor.Data.BufferCopy.Clone(Db.RcvMisc.Where(s => s.Company == Session.CompanyID
&& s.PackSlip == packSlip
&& s.VendorNum == vendorNum
&& s.PurPoint == purPoint
&& s.MiscSeq == seqNum).FirstOrDefault());
// If you want to Preserve the GUID you must use Copy with 3rd param true
Erp.Tables.RcvMisc linqRowExample2 = new Erp.Tables.RcvMisc();
Epicor.Data.BufferCopy.Copy(xrow, linqRowExample2, true);
I have yet to figure out the RowMod āDā sometimes RowMod D doesnt work and you have to use SetRowState but then the Facade doesnt always get notified, because you basically deleted the row from your dataset.
// ILSpy how Epicor Uan UpdateExt handle RowMod D
// They Delete the original row but keep a 2nd version with RowMod "D"
IceRow firstRow = (IceRow)fullTs.Tables[0][0];
IceRow backupRow2 = CreateNewRow(workingTs.Tables[0]);
OnUpdateExtCopyRowForTable(UpdateExtRowAction.BackupExistingRow, firstRow, backupRow2, updateSystemColumns: true);
workingTs.Tables[0].Add(backupRow2);
backupRow2.SetRowState(IceRowState.Unchanged);
firstRow.RowMod = "D";
IceRow deletedRow = CreateNewRow(workingTs.Tables[0]);
OnUpdateExtCopyRowForTable(UpdateExtRowAction.BackupExistingRow, firstRow, deletedRow, updateSystemColumns: true);
workingTs.Tables[0].Add(deletedRow);
deletedRow.SetRowState(IceRowState.Deleted);
changedRow = deletedRow;
hasDif = true;
Perhaps I should use UpdateExt, it might be fine in this scenario and may not cause bugs because it looks like it may even be handling the Before/After Rows