BPM ARInvoice Error - Update Tax Category

You need to pass in both the unmodified row as well as the modified row to UpdateMaster.

You can use bo GetRows or BufferCopy – I need to write-up more info on this but Epicor has helpers to clone a row.

var dsReceipt = bo.GetByID(vendorNum, purPoint, packSlip);
 
// 
// 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 GUID 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);

Example of me Updating InvcHead

foreach (var row in dsInvcHeadList.InvcHeadList)
{
Ice.Diagnostics.Log.WriteEntry($"dsInvcHeadList: {row.InvoiceNum}");

    using (var bo = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.ARInvoiceSvcContract>(this.Db))
    {
    
        var dsInvoice = bo.GetByID(row.InvoiceNum);
        var originalRow = dsInvoice.InvcHead.NewRow();
        BufferCopy.Copy(dsInvoice.InvcHead[0], originalRow);
        
        dsInvoice.InvcHead.Add(originalRow);
        Ice.Diagnostics.Log.WriteEntry($"dsInvoice: {dsInvoice.InvcHead[0].InvoiceNum}");
        string recalcAmts = callContextBpmData.ShortChar01; // asked in customization
        dsInvoice.InvcHead[0].ApplyDate = dtGroupApplyDate;
        dsInvoice.InvcHead[0].RowMod = "U";
        bo.OnChangeofApplyDateEx(row.InvoiceNum, dtGroupApplyDate, recalcAmts, ref dsInvoice);
        dsInvoice.InvcHead[0].RowMod = "U";

        /// <param name="ds"></param>
        /// <param name="cGroupID">current GroupID on Invoice</param>
        /// <param name="cTableName">Indicates the tableName that triggered the update</param>
        /// <param name="runCheckAmortSched">Indicates whether the chkAmortSchedCore method should be called</param>
        /// <param name="runChkLtrOfCredit">Indicates whether the chkLtrOfCredit method should be called</param>
        /// <param name="genAmortSched">Indicates whether Amortization schedules should be either restored or generated</param>
        /// <param name="lRASchedExists">value of current InvcDtl.RASchedExists field</param>
        /// <param name="ipInvoiceNum">current invoice num used to call chkLtrOfCredCore</param>
        /// <param name="ipInvoiceLine">current invoice line</param>
        /// <param name="cARLOCID">current ARLOCID used to call chkLtrOfCredCore</param>
        /// <param name="suppressUserPrompts">Indicates if user prompts should be suppressed</param>
        /// <param name="dTotalCharges">InvcHead.InvoiceAmt used in call to CheckLtrOfCredit</param>
        /// <param name="lGetDefaultAccount">Set the global variable to get the default account if the product code or part are dirty</param>
        /// <param name="grpTotalInvAmt">InvGrp.BaseAmount to update in UI</param>
        /// <param name="opGenMessage">possible output message from chkAmortSchedCore</param>
        /// <param name="opLtrCrdMsg">possible output message from chkLtrOfCredCore</param>
        /// <param name="lUpdateRan">Indicates that the Update process did run</param>

        decimal grpTotalInvAmt = 0;
        string opGenMessage = "";
        string opLtrCrdMsg = "";
        bool lUpdateRan = false;
        bool genAmortSched = false;
        
        bo.UpdateMaster(ref dsInvoice, dsInvoice.InvcHead[0].GroupID, "InvcHead", false, false, ref genAmortSched, false, dsInvoice.InvcHead[0].InvoiceNum, 1, "", false, dsInvoice.InvcHead[0].InvoiceAmt, false, out grpTotalInvAmt, out opGenMessage, out opLtrCrdMsg, out lUpdateRan);
        
     }
 }

Mainly focus on:

        var dsInvoice = bo.GetByID(row.InvoiceNum);
        var originalRow = dsInvoice.InvcHead.NewRow();
        BufferCopy.Copy(dsInvoice.InvcHead[0], originalRow);
        
        dsInvoice.InvcHead.Add(originalRow);

More Reading: