U9C 记录一次排错时长三天的错误

客户需求

销售订单变更审核通过后同步下游单据(生产订单,成品入库单,标准出货单)的PO号,以及标准出货单的最终价

错误点

  1. 单据已审核,不允许OBA更新
  2. 审核后单据页转圈不停

错误排错过程及解决方式

问题1

  1. 开始时,无脑觉得本地可以,为什么客户测试环境就不可以,然后碰壁一天后,晚上发现,自己写的测试环境的代码多了一行 shipLine的自定项的赋值
foreach (ShipLine shipLine in shipLines)
{
	 shipLine.FinallyPriceTC = newValue;
	 //就是这行该死的代码
	 //shipLine.DescFlexField.PrivateDescSeg11 = oldValue;
	 endShipLines.Add(shipLine);
}
  1. 为什么呢?为什么多了这行就报不允许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();
			}
		}
	}
}
  1. 很明显在这,当allowed属性为false是,这里抛出ShipBE10100002Exception错误,这个错误在Ship里被定义为“单据已核准,不允许OBA更新”,继续跟踪代码发现代码里的IsShipLineChangeable()方法,返回为false,所以进入下面判断的代码,赋值给allowed为false,直接报错
  2. 然后进入到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;
}
  1. 从代码不难看出,首先获取到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"
		};
	}
}
  1. OK,看到这,就知道允许修改的只有这几个字段,我上面代码添加了自定项所以在这不允许,直接报错,到此第一个错误解决完毕,此时已经来到了第二天下午三点

问题2

  1. 这时人已经飘了,这下完全OJBK,昨晚直接提前回家了,狗欢无好事,更难得错误来了
  2. 审核后,卡在订单变更单界面,后续的错误全都无法进行,接下来开始排错
SOLine soline = SOLine.Finder.Find(" ID= '" + somodifyline.SOLine + "'");

  1. 获取的销售订单行,和通过出货单单行都没问题,这时候去本地去测试,发现没问题,可以通过(此处记住这个本地测试没问题
  2. 本地怎么测试都可以,但是测试就不行,无数次后找到颠覆自己认知的原因,本地我把代码加在了其他的BP,所以可以,测试加在了客户要求的销售订单变更单的BE插件,所以不可以
  3. 所以通过这个发现,以及发现对SV的一个赋值,发现了为什么其他BP可以通过,而销售订单变更单的插件不能通过
EntityAndFieldInfo entityfieldinfo = new EntityAndFieldInfo();
entityfieldinfo.EntityInfo = new EntityInfo();
entityfieldinfo.EntityInfo.ID = shipline.ID;
entityfieldinfo.EntityInfo.SysVer = shipline.SysVersion;
  1. entityfieldinfo.EntityInfo.SysVer = shipline.SysVersion;此处代码是对SV的一个赋值,这里发现赋值了实体类的版本号!这时候我去调试代码发现,查出来的soLine和shipLine全都版本号比数据库查出来的要+1!!!!!!!!!而本地能通过的原因就是从代码查出来的ShipLine跟直接从数据库查版本号保持一致
  2. 尝试了FIndByID从缓存取数据,尝试了DataSet从SQL取数据,尝试了从EntityQuery直接OQL取数据,全都版本号要比数据库+1,所以此处已绝望
  3. 这时候开始无脑乱搞,写了一个BP去改价,不行,写了一个BP再给BP做一个插件去改价,也是不行,统统查出来版本号不一致。。。此时已经来到了第三天凌晨四点,再也忍不住,睡了
  4. 起来后,开始尝试了用
ShipPreShipReCalculate.PerformCalculatePrice(changeinfolist, ship, true);
  1. 也尝试了用
 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
  1. 两种方式长时候,第一种不会用,第二种核币没有算,要自己加,考虑到风险,暂时放弃
  2. 峰会路砖,此处划重点

当考虑到版本号的原因后,就想到代码查出来的和数据不一样,代表实体类的sysversion已经赋值,但是事务还没提交,所以接下来的要解决的问题是 “事务”

  1. 其实事务这个问题,一开始在做一个BP,用requiredNew的时候有考虑到这个问题,但是么有往下深究,经过和陈老师的沟通,以及陈老师的指导后开始尝试陈老师提供的两个事务的方法
using (UBFTransactionScope scope = new UBFTransactionScope(TransactionOption.RequiresNew))
{
     try
     {
         //执行价格重算
     }
    catch (Exception ex)
     { 
           scope.Rollback();
           return;//返回,不执行 scope.Commit();就会回滚
     } 
  scope.Commit();
   }
  1. 这是直接在代码里开启一个新的事务,去做,但是做了后发现仍然不行,版本号仍然+1
下面这个方法也肯定可以,但是缺点就是抛错,前台看不到:Demo:
UFSoft.UBF.Transactions.UBFTransactionContext.Current.Committed +=new System.Transactions.TransactionCompletedEventHandler(BusinessTransactionSucess);//事务已经全部完成,数据已经成功提交到数据库中

private void BusinessTransactionSucess(object obj, System.Transactions.TransactionEventArgs e)

{
 //改价逻辑
}
  1. OK,这个方式就可以了,直接把之前的事务提交后执行构造参数的方法,这时候再去获取sysVersion就跟数据库的一致了!
  2. 此时,排错结束,开发完成,待测试

总结

  1. 陈老师说,版本号+1的原因是因为销售订单更新时有数据回写出库单,出库单更新后也有数据回写到销售订单,所以会让版本号先+1,等事务提交后更新到数据库 普及一个只是,这时候明明数据库版本号为10,而代码查出来的是11,这个叫做脏读,脏读的解释还有其他详细看此介绍
    链接: 数据库的脏读,不可重复读和幻读以及事务的隔离级别介绍
  2. 代码出错不可怕,可怕的是不知道错误在哪,认真检查自己代码为先,确认已检查不出来后,再去跟源码分析!
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值