首页 > 解决方案 > Acumatica:能够覆盖 DefaultEndpointImpl 以向合同 REST API 添加高级自定义逻辑

问题描述

我想知道是否可以覆盖 DefaultEndpointImpl.cs 或在另一个文件中添加我自己的 API 逻辑?

我正在努力处理一些需要覆盖或添加此文件中的逻辑的 API 调用。例如,我可以通过 api 成功为采购订单创建采购收据,但是我无法以相同的方式添加“转让收据”采购收据。

我在 API 端点中包含了各种非标准字段,这些字段引用了原始转移、转移订单和装运,但均未成功。API 调用成功,但未添加任何行。我已经能够让行在生成后更新它们的数量,但不能添加或删除当前行。

问题看起来源于此代码,它似乎添加了正确处理添加图表的采购订单行逻辑的功能,但对其他收据类型没有做任何特殊的事情。

[FieldsProcessed(new[] { "POLineNbr", "POOrderType", "POOrderNbr" })]
protected void PurchaseReceiptDetail_Insert(PXGraph graph, EntityImpl entity, EntityImpl targetEntity) {

    var receiptEntry = (POReceiptEntry)graph;

    var lineNbr = targetEntity.Fields.SingleOrDefault(f => f.Name == "POLineNbr") as EntityValueField;
    var orderType = targetEntity.Fields.SingleOrDefault(f => f.Name == "POOrderType") as EntityValueField;
    var orderNbr = targetEntity.Fields.SingleOrDefault(f => f.Name == "POOrderNbr") as EntityValueField;

    bool insertViaAddPO = lineNbr != null && orderNbr != null && orderType != null;

    if (!insertViaAddPO && (lineNbr != null || orderType != null || orderNbr != null)) {

        throw new PXException(PO.Messages.POTypeNbrLineNbrMustBeFilled);
    }

    var detailsCache = receiptEntry.transactions.Cache;

    if (insertViaAddPO) {

        receiptEntry.filter.Cache.Remove(receiptEntry.filter.Current);
        receiptEntry.filter.Cache.Insert(new POReceiptEntry.POOrderFilter());

        var filter = receiptEntry.filter.Current; var state = receiptEntry.filter.Cache.GetStateExt(filter, "OrderType") as PXStringState;

        if (state != null && state.AllowedLabels.Contains(orderType.Value)) {
            orderType.Value = state.ValueLabelDic.Single(p => p.Value == orderType.Value).Key;

        }

        receiptEntry.filter.Cache.SetValueExt(filter, "OrderType", orderType.Value);
        receiptEntry.filter.Cache.SetValueExt(filter, "OrderNbr", orderNbr.Value);
        receiptEntry.filter.Update(filter);

        var orders = receiptEntry.poLinesSelection.Select().Select(r => r.GetItem<POReceiptEntry.POLineS>());
        var order = orders.FirstOrDefault(o => o.LineNbr == int.Parse(lineNbr.Value));

        if (order == null) {
            throw new PXException(PO.Messages.PurchaseOrderLineNotFound);
        }

        order.Selected = true;

        receiptEntry.poLinesSelection.Update(order);
        receiptEntry.Actions["AddPOOrderLine2"].Press();

    } else {
        detailsCache.Current = detailsCache.Insert();
    }
}

标签: c#restapiacumatica

解决方案


经过大量的反复试验,我设法让一切都按我的需要工作。但是我担心,由于这不是记录信息,因此在不久的将来它可能会发生变化。

DefaultEndpointImpl 类中的方法通过反射调用。为了为我的默认端点添加自定义功能,我扩展了默认类并将必要的属性添加到我的类和函数中。

我的自定义类添加了以下内容:

  1. 覆盖默认 PurchaseReceiptDetail_Insert 函数以添加转账收据功能。
  2. 添加 ReceiptDetail_Insert 方法以允许添加 IN 收据引用

第一个很简单,我对结果很满意。第二个花了很多时间和调试才能正确,很hacky,可能不适用于串行跟踪项目。花了这么长时间的原因是因为有很多未知数,我基本上一直在根据基类中的代码进行猜测。例如,我什至不确定这个函数在哪个阶段触发。它看起来函数完全覆盖了其他一些不可见的默认逻辑。

所以要么接受,要么离开!也许我会联系 Acumatica 看看是否可以获得更多信息。

using PX.Api; 
using PX.Api.ContractBased; 
using PX.Api.ContractBased.Models; 
using PX.Data;
using PX.Objects.PO;
using PX.Objects.IN;
using System;
using System.Linq;
using PX.Objects.CM;
using PX.Objects.CS;

namespace AcuStock
{
  [PXVersion("5.30.001", "AcuStock")]  
  [PXVersion("6.00.001", "AcuStock")]  
  [PXVersion("17.200.001", "AcuStock")]  
  public class AcuStockEndpointImpl : PX.Objects.DefaultEndpointImpl
  {

    [FieldsProcessed(new[] { "POLineNbr", "POOrderType", "POOrderNbr", "OrigLineNbr", "OrigRefNbr" })]
    protected new void PurchaseReceiptDetail_Insert(PXGraph graph, EntityImpl entity, EntityImpl targetEntity){

        var lineNbr = targetEntity.Fields.SingleOrDefault(f => f.Name == "OrigLineNbr") as EntityValueField;
        var refNbr = targetEntity.Fields.SingleOrDefault(f => f.Name == "OrigRefNbr") as EntityValueField;
        var receiptQty = targetEntity.Fields.SingleOrDefault(f => f.Name == "ReceiptQty") as EntityValueField;
        var location = targetEntity.Fields.SingleOrDefault(f => f.Name == "Location") as EntityValueField;
        var inventoryID = targetEntity.Fields.SingleOrDefault(f => f.Name == "InventoryID") as EntityValueField;

        bool insertViaAddTR = lineNbr != null && refNbr != null;

        var receiptEntry = (POReceiptEntry) graph;

        if (insertViaAddTR){

            receiptEntry.addReceipt.Cache.Remove(receiptEntry.addReceipt.Current);
            receiptEntry.addReceipt.Cache.Insert(new POReceiptEntry.POReceiptLineS());

            var filter = receiptEntry.addReceipt.Current;

            receiptEntry.addReceipt.Cache.SetValueExt(filter, "OrigRefNbr", refNbr.Value);
            receiptEntry.addReceipt.Cache.SetValueExt(filter, "OrigLineNbr", lineNbr.Value);
            receiptEntry.addReceipt.Cache.SetValueExt(filter, "InventoryID", inventoryID.Value);
            receiptEntry.addReceipt.Cache.SetValueExt(filter, "ReceiptQty", receiptQty.Value);
            if (location != null)
                receiptEntry.addReceipt.Cache.SetValueExt(filter, "LocationID", location.Value);
            receiptEntry.addReceipt.Update(filter);

            var lines = receiptEntry.addReceipt.Select().Select(r => r.GetItem<POReceiptEntry.POReceiptLineS>());
            var line = lines.FirstOrDefault(o => o.OrigLineNbr == int.Parse(lineNbr.Value));

            if (line == null){
                throw new PXException(PX.Objects.PO.Messages.PurchaseOrderLineNotFound);
            }

            receiptEntry.addPOReceiptLine2.Press();

        } else {
            base.PurchaseReceiptDetail_Insert(graph, entity, targetEntity);
        }

        var allocations = (targetEntity.Fields.SingleOrDefault(f => string.Equals(f.Name, "Allocations")) as EntityListField).Value ?? new EntityImpl[0];

        if (allocations.Any(a => a.Fields != null && a.Fields.Length > 0)){
            // Remove automatically added allocation
            if (receiptEntry.splits.Current != null){
                receiptEntry.splits.Delete(receiptEntry.splits.Current);
            }
    }

    }

    [FieldsProcessed(new[] { "OrigLineNbr", "OrigRefNbr", "Quantity", "Location" })]
    protected void ReceiptDetails_Insert(PXGraph graph, EntityImpl entity, EntityImpl targetEntity) {

        var lineNbr = targetEntity.Fields.SingleOrDefault(f => f.Name == "OrigLineNbr") as EntityValueField;
        var receiptQty = targetEntity.Fields.SingleOrDefault(f => f.Name == "Quantity") as EntityValueField;
        var location = targetEntity.Fields.SingleOrDefault(f => f.Name == "Location") as EntityValueField;

        var allocations = (targetEntity.Fields.SingleOrDefault(f => string.Equals(f.Name, "Allocations")) as EntityListField).Value ?? new EntityImpl[0];
        var hasAllocations = allocations.Any(a => a.Fields != null && a.Fields.Length > 0);

        var receiptEntry = (INReceiptEntry) graph;

        string transferNbr = receiptEntry.receipt.Current.TransferNbr;

        var detailsCache = receiptEntry.transactions.Cache;

        if (lineNbr == null || transferNbr == null){

            detailsCache.Current = detailsCache.Insert();
            return;
        }

        INTran newtran = null;
        decimal newtranqty = Decimal.Parse(receiptQty.Value);
        decimal newtrancost = 0m;
        receiptEntry.ParseSubItemSegKeys();

        using (new PXReadBranchRestrictedScope())
        {
            foreach (PXResult<INTransitLine, INLocationStatus2, INTransitLineLotSerialStatus, INSite, InventoryItem, INTran> res in
                PXSelectJoin<INTransitLine,
                InnerJoin<INLocationStatus2, On<INLocationStatus2.locationID, Equal<INTransitLine.costSiteID>>,
                LeftJoin<INTransitLineLotSerialStatus,
                        On<INTransitLine.transferNbr, Equal<INTransitLineLotSerialStatus.transferNbr>,
                        And<INTransitLine.transferLineNbr, Equal<INTransitLineLotSerialStatus.transferLineNbr>>>,
                InnerJoin<INSite, On<INSite.siteID, Equal<INTransitLine.toSiteID>>,
                InnerJoin<InventoryItem, On<InventoryItem.inventoryID, Equal<INLocationStatus2.inventoryID>>,
                InnerJoin<INTran,
                    On<INTran.docType, Equal<INDocType.transfer>,
                    And<INTran.refNbr, Equal<INTransitLine.transferNbr>,
                    And<INTran.lineNbr, Equal<INTransitLine.transferLineNbr>,
                    And<INTran.invtMult, Equal<shortMinus1>>>>>>>>>>,
                Where<INTransitLine.transferNbr, Equal<Required<INTransitLine.transferNbr>>, And<INTransitLine.transferLineNbr, Equal<Required<INTransitLine.transferLineNbr>>>>,
                OrderBy<Asc<INTransitLine.transferNbr, Asc<INTransitLine.transferLineNbr>>>>
                .Select(receiptEntry, transferNbr, lineNbr.Value))
            {
                INTransitLine transitline = res;
                INLocationStatus2 stat = res;
                INTransitLineLotSerialStatus lotstat = res;
                INSite site = res;
                InventoryItem item = res;
                INTran tran = res; 

                if (stat.QtyOnHand == 0m || (lotstat != null && lotstat.QtyOnHand == 0m))
                    continue;

                if (newtran == null) { 

                    if (!object.Equals(receiptEntry.receipt.Current.BranchID, site.BranchID))
                    {
                        INRegister copy = PXCache<INRegister>.CreateCopy(receiptEntry.receipt.Current);
                        copy.BranchID = site.BranchID;
                        receiptEntry.receipt.Update(copy);
                    }

                    newtran = PXCache<INTran>.CreateCopy(tran);
                    newtran.OrigBranchID = newtran.BranchID;
                    newtran.OrigTranType = newtran.TranType;
                    newtran.OrigRefNbr = transitline.TransferNbr;
                    newtran.OrigLineNbr = transitline.TransferLineNbr;
                    newtran.BranchID = site.BranchID;
                    newtran.DocType = receiptEntry.receipt.Current.DocType;
                    newtran.RefNbr = receiptEntry.receipt.Current.RefNbr;
                    newtran.LineNbr = (int)PXLineNbrAttribute.NewLineNbr<INTran.lineNbr>(receiptEntry.transactions.Cache, receiptEntry.receipt.Current);
                    newtran.InvtMult = (short)1;
                    newtran.SiteID = transitline.ToSiteID;
                    newtran.LocationID = transitline.ToLocationID;
                    newtran.ToSiteID = null;
                    newtran.ToLocationID = null;
                    newtran.BaseQty = 0m;
                    newtran.Qty = 0m;
                    newtran.UnitCost = 0m;
                    newtran.Released = false;
                    newtran.InvtAcctID = null;
                    newtran.InvtSubID = null;
                    newtran.ReasonCode = null;
                    newtran.ARDocType = null;
                    newtran.ARRefNbr = null;
                    newtran.ARLineNbr = null;
                    newtran.ProjectID = null;
                    newtran.TaskID = null;
                    newtran.CostCodeID = null;
                    newtran.TranCost = 0m;

                    receiptEntry.splits.Current = null;

                    newtran = receiptEntry.transactions.Insert(newtran);

                    receiptEntry.transactions.Current = newtran;

                    if (receiptEntry.splits.Current != null)
                    {
                        receiptEntry.splits.Delete(receiptEntry.splits.Current);
                    }
                }

                if (hasAllocations){

                    newtranqty = 0m;

                    foreach (var allocation in allocations) { 

                        var newsplitqty = allocation.Fields.SingleOrDefault(f => f.Name == "Quantity") as EntityValueField;
                        var newsplitlocation = allocation.Fields.SingleOrDefault(f => f.Name == "Location") as EntityValueField;

                        INTranSplit newsplit = this.addReceiptSplitLine(receiptEntry, stat, lotstat, transitline, newtran, item, Decimal.Parse(newsplitqty.Value), newsplitlocation.Value);

                        newtrancost += newsplit.BaseQty.Value * newsplit.UnitCost.Value;
                        newtranqty += newsplit.BaseQty.Value;
                    }

                    break;

                } else {

                    INTranSplit newsplit = this.addReceiptSplitLine(receiptEntry, stat, lotstat, transitline, tran, item, newtranqty, null);

                    newtrancost += newsplit.BaseQty.Value * newsplit.UnitCost.Value;
                    newtranqty += newsplit.BaseQty.Value;

                }

            }

            receiptEntry.UpdateTranCostQty(newtran, newtranqty, newtrancost);

        }

    }

    [FieldsProcessed(new[] { "OrigLineNbr" })]
    protected void ReceiptAllocations_Insert(PXGraph graph, EntityImpl entity, EntityImpl targetEntity) {
        // no-op
    }

    private INTranSplit addReceiptSplitLine(INReceiptEntry receiptEntry, INLocationStatus2 stat, INTransitLineLotSerialStatus lotstat, INTransitLine transitline, INTran tran, InventoryItem item, Decimal qty, string location){

        INTranSplit newsplit;
        decimal newsplitqty;
        if (lotstat.QtyOnHand == null)
        {
            newsplit = new INTranSplit();
            newsplit.InventoryID = stat.InventoryID;
            newsplit.IsStockItem = true;
            newsplit.FromSiteID = transitline.SiteID;
            newsplit.SubItemID = stat.SubItemID;
            newsplit.LotSerialNbr = null;
            newsplitqty = qty;
        }
        else
        {
            newsplit = new INTranSplit();
            newsplit.InventoryID = lotstat.InventoryID;
            newsplit.IsStockItem = true;
            newsplit.FromSiteID = lotstat.FromSiteID;
            newsplit.SubItemID = lotstat.SubItemID;
            newsplit.LotSerialNbr = lotstat.LotSerialNbr;
            newsplitqty = qty;
        }

        newsplit.DocType = receiptEntry.receipt.Current.DocType;
        newsplit.RefNbr = receiptEntry.receipt.Current.RefNbr;
        newsplit.LineNbr = tran.LineNbr;
        newsplit.SplitLineNbr = (int)PXLineNbrAttribute.NewLineNbr<INTranSplit.splitLineNbr>(receiptEntry.splits.Cache, receiptEntry.receipt.Current);

        newsplit.UnitCost = 0m;
        newsplit.InvtMult = (short)1;
        newsplit.SiteID = transitline.ToSiteID;
        newsplit.PlanID = null;
        newsplit.Released = false;
        newsplit.ProjectID = null;
        newsplit.TaskID = null;

        if (location == null) { 
            newsplit.LocationID = lotstat.ToLocationID ?? transitline.ToLocationID;
        } else { 
            receiptEntry.splits.SetValueExt<INTranSplit.locationID>(newsplit, location);
        }

        newsplit = receiptEntry.splits.Insert(newsplit);

        newsplit.MaxTransferBaseQty = newsplitqty;
        newsplit.BaseQty = newsplitqty;
        newsplit.Qty = newsplit.BaseQty.Value;

        receiptEntry.UpdateCostSubItemID(newsplit, item);

        receiptEntry.SetCostAttributes(tran, newsplit, item, tran.OrigRefNbr);
        newsplit.UnitCost = PXCurrencyAttribute.BaseRound(receiptEntry, newsplit.UnitCost);
        receiptEntry.splits.Update(newsplit);

        return newsplit;
    }

  }
}

推荐阅读