客户需求
销售订单变更审核通过后同步下游单据(生产订单,成品入库单,标准出货单)的PO号,以及标准出货单的最终价
错误点
- 单据已审核,不允许OBA更新
- 审核后单据页转圈不停
错误排错过程及解决方式
问题1
- 开始时,无脑觉得本地可以,为什么客户测试环境就不可以,然后碰壁一天后,晚上发现,自己写的测试环境的代码多了一行 shipLine的自定项的赋值
foreach (ShipLine shipLine in shipLines)
{
shipLine.FinallyPriceTC = newValue;
//就是这行该死的代码
//shipLine.DescFlexField.PrivateDescSeg11 = oldValue;
endShipLines.Add(shipLine);
}
- 为什么呢?为什么多了这行就报不允许OBA更新,接下来通过反编译直接跟踪到错误出现点,Ship.cs
private void OBAValidate()
{
if (this.ActivityType == UFIDA.U9.SM.Enums.SMActivityEnum.OBAUpdate)
{
if (this.OriginalData.Status.Equals(ShipStateEnum.Approved))
{
bool allowed = true;
if (base.GetChangedAttributes().Count > 0)
{
allowed = false;
}
else if (this.ShipLines.DelLists.Count > 0)
{
allowed = false;
}
else
{
foreach (ShipLine shipline in this.ShipLines)
{
if (shipline.SysState == 4)
{
if (shipline.GetChangedAttributes().Count > 0 && !this.IsShipLineChangeable(shipline))
{
allowed = false;
}
}
else if (shipline.SysState != 1)
{
allowed = false;
}
}
}
if (!allowed)
{
throw new ShipBE10100002Exception();
}
}
}
}
- 很明显在这,当allowed属性为false是,这里抛出ShipBE10100002Exception错误,这个错误在Ship里被定义为“单据已核准,不允许OBA更新”,继续跟踪代码发现代码里的IsShipLineChangeable()方法,返回为false,所以进入下面判断的代码,赋值给allowed为false,直接报错
- 然后进入到IsShipLineChangeable方法
private bool IsShipLineChangeable(ShipLine shipline)
{
bool changeable = true;
if (!shipline.ItemInfo.ItemID.SaleInfo.IsAdjustPriceAfterShip)
{
changeable = false;
}
else
{
List<string> changedattributes = shipline.GetChangedAttributes();
foreach (string attribute in changedattributes)
{
if (!this.ChangableLineFieldsAfterApproved.Contains(attribute))
{
changeable = false;
break;
}
}
}
return changeable;
}
- 从代码不难看出,首先获取到shipLine已经被修改的属性GetChangedAttributes,然后从ChangableLineFieldsAfterApproved这个列表里去判断,如果有属性不在里面,直接返回false,下面是ChangableLineFieldsAfterApproved列表的内容
private List<string> ChangableLineFieldsAfterApproved
{
get
{
return new List<string>
{
ShipLine.EntityRes.FinallyPriceTC,
ShipLine.EntityRes.TotalNetMoneyTC,
ShipLine.EntityRes.TotalMoneyTC,
ShipLine.EntityRes.AdjustPriceDate,
ShipLine.EntityRes.AdjustPricePerson,
"ActivityType"
};
}
}
- OK,看到这,就知道允许修改的只有这几个字段,我上面代码添加了自定项所以在这不允许,直接报错,到此第一个错误解决完毕,此时已经来到了第二天下午三点
问题2
- 这时人已经飘了,这下完全OJBK,昨晚直接提前回家了,狗欢无好事,更难得错误来了
- 审核后,卡在订单变更单界面,后续的错误全都无法进行,接下来开始排错
SOLine soline = SOLine.Finder.Find(" ID= '" + somodifyline.SOLine + "'");
- 获取的销售订单行,和通过出货单单行都没问题,这时候去本地去测试,发现没问题,可以通过(此处记住这个本地测试没问题)
- 本地怎么测试都可以,但是测试就不行,无数次后找到颠覆自己认知的原因,本地我把代码加在了其他的BP,所以可以,测试加在了客户要求的销售订单变更单的BE插件,所以不可以
- 所以通过这个发现,以及发现对SV的一个赋值,发现了为什么其他BP可以通过,而销售订单变更单的插件不能通过
EntityAndFieldInfo entityfieldinfo = new EntityAndFieldInfo();
entityfieldinfo.EntityInfo = new EntityInfo();
entityfieldinfo.EntityInfo.ID = shipline.ID;
entityfieldinfo.EntityInfo.SysVer = shipline.SysVersion;
- entityfieldinfo.EntityInfo.SysVer = shipline.SysVersion;此处代码是对SV的一个赋值,这里发现赋值了实体类的版本号!这时候我去调试代码发现,查出来的soLine和shipLine全都版本号比数据库查出来的要+1!!!!!!!!!而本地能通过的原因就是从代码查出来的ShipLine跟直接从数据库查版本号保持一致
- 尝试了FIndByID从缓存取数据,尝试了DataSet从SQL取数据,尝试了从EntityQuery直接OQL取数据,全都版本号要比数据库+1,所以此处已绝望
- 这时候开始无脑乱搞,写了一个BP去改价,不行,写了一个BP再给BP做一个插件去改价,也是不行,统统查出来版本号不一致。。。此时已经来到了第三天凌晨四点,再也忍不住,睡了
- 起来后,开始尝试了用
ShipPreShipReCalculate.PerformCalculatePrice(changeinfolist, ship, true);
- 也尝试了用
if (rcv.IsPriceIncludeTax)
{
line.ArriveTotalMnyTC = rcv.AC.MoneyRound.GetRoundValue(line.FinallyPriceTC * line.ArriveQtyPU);// 价税合计(原币)
if (line.TaxRate == 0)
{
line.ArriveTotalTaxTC = 0;//税额(原本)
line.ArriveTotalNetMnyTC = rcv.AC.MoneyRound.GetRoundValue(line.ArriveTotalMnyTC - line.ArriveTotalTaxTC);// 未税金额(原币)
}
else if (line.TaxSchedule.TaxScheduleTaxs[0].Tax.TaxAmountCalMethod == UFIDA.U9.CBO.FI.Tax.TaxAmountCalMethodEnum.TaxByMoneyEmbedded)
{
line.ArriveTotalTaxTC = rcv.AC.MoneyRound.GetRoundValue(line.ArriveTotalMnyTC * line.TaxRate);//税额(原本)
line.ArriveTotalNetMnyTC = rcv.AC.MoneyRound.GetRoundValue(line.ArriveTotalMnyTC - line.ArriveTotalTaxTC);// 未税金额(原币)
}
else if (line.TaxSchedule.TaxScheduleTaxs[0].Tax.TaxAmountCalMethod == UFIDA.U9.CBO.FI.Tax.TaxAmountCalMethodEnum.TaxByMoneyAdded)
{
line.ArriveTotalNetMnyTC = rcv.AC.MoneyRound.GetRoundValue(line.ArriveTotalMnyTC / (1 + line.TaxRate));// 未税金额(原币)
line.ArriveTotalTaxTC = rcv.AC.MoneyRound.GetRoundValue(line.ArriveTotalMnyTC - line.ArriveTotalNetMnyTC);//税额(原本)
}
}
else
{
line.ArriveTotalNetMnyTC = rcv.AC.MoneyRound.GetRoundValue(line.FinallyPriceTC * line.ArriveQtyPU);//未税金额(原币)
if (line.TaxRate == 0)
{
line.ArriveTotalMnyTC = rcv.AC.MoneyRound.GetRoundValue(line.ArriveTotalNetMnyTC);//价税合计(原币)
line.ArriveTotalTaxTC = 0;//税额(原本)
}
else if (line.TaxSchedule.TaxScheduleTaxs[0].Tax.TaxAmountCalMethod == UFIDA.U9.CBO.FI.Tax.TaxAmountCalMethodEnum.TaxByMoneyEmbedded)
{
line.ArriveTotalMnyTC = rcv.AC.MoneyRound.GetRoundValue(line.ArriveTotalNetMnyTC / (1 - line.TaxRate));//价税合计(原币)
line.ArriveTotalTaxTC = rcv.AC.MoneyRound.GetRoundValue(line.ArriveTotalMnyTC - line.ArriveTotalNetMnyTC);//税额(原本)
}
else if (line.TaxSchedule.TaxScheduleTaxs[0].Tax.TaxAmountCalMethod == UFIDA.U9.CBO.FI.Tax.TaxAmountCalMethodEnum.TaxByMoneyAdded)
{
line.ArriveTotalTaxTC = rcv.AC.MoneyRound.GetRoundValue(line.ArriveTotalNetMnyTC * line.TaxRate);//税额(原本)
line.ArriveTotalMnyTC = rcv.AC.MoneyRound.GetRoundValue(line.ArriveTotalNetMnyTC + line.ArriveTotalTaxTC);//价税合计(原币)
}
}
line.TotalMnyTC = line.ArriveTotalMnyTC;
line.TotalTaxTC = line.ArriveTotalTaxTC;
line.TotalNetMnyTC = line.ArriveTotalNetMnyTC;
#endregion
- 两种方式长时候,第一种不会用,第二种核币没有算,要自己加,考虑到风险,暂时放弃
- 峰会路砖,此处划重点
当考虑到版本号的原因后,就想到代码查出来的和数据不一样,代表实体类的sysversion已经赋值,但是事务还没提交,所以接下来的要解决的问题是 “事务”
- 其实事务这个问题,一开始在做一个BP,用requiredNew的时候有考虑到这个问题,但是么有往下深究,经过和陈老师的沟通,以及陈老师的指导后开始尝试陈老师提供的两个事务的方法
using (UBFTransactionScope scope = new UBFTransactionScope(TransactionOption.RequiresNew))
{
try
{
//执行价格重算
}
catch (Exception ex)
{
scope.Rollback();
return;//返回,不执行 scope.Commit();就会回滚
}
scope.Commit();
}
- 这是直接在代码里开启一个新的事务,去做,但是做了后发现仍然不行,版本号仍然+1
下面这个方法也肯定可以,但是缺点就是抛错,前台看不到:Demo:
UFSoft.UBF.Transactions.UBFTransactionContext.Current.Committed +=new System.Transactions.TransactionCompletedEventHandler(BusinessTransactionSucess);//事务已经全部完成,数据已经成功提交到数据库中
private void BusinessTransactionSucess(object obj, System.Transactions.TransactionEventArgs e)
{
//改价逻辑
}
- OK,这个方式就可以了,直接把之前的事务提交后执行构造参数的方法,这时候再去获取sysVersion就跟数据库的一致了!
- 此时,排错结束,开发完成,待测试
总结
- 陈老师说,版本号+1的原因是因为销售订单更新时有数据回写出库单,出库单更新后也有数据回写到销售订单,所以会让版本号先+1,等事务提交后更新到数据库 普及一个只是,这时候明明数据库版本号为10,而代码查出来的是11,这个叫做脏读,脏读的解释还有其他详细看此介绍
链接: 数据库的脏读,不可重复读和幻读以及事务的隔离级别介绍 - 代码出错不可怕,可怕的是不知道错误在哪,认真检查自己代码为先,确认已检查不出来后,再去跟源码分析!