Acumatica:能够覆盖 DefaultEndpointImpl 以向合约 REST API 添加高级自定义逻辑 [英] Acumatica: Ability to override DefaultEndpointImpl to add advanced custom logic to contract REST API

查看:15
本文介绍了Acumatica:能够覆盖 DefaultEndpointImpl 以向合约 REST API 添加高级自定义逻辑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm wondering if it possible to override DefaultEndpointImpl.cs or add my own API logic in another file?

我正在努力处理一些 API 调用,这些调用需要覆盖或添加此文件中的逻辑.例如,我能够通过 api 成功为 PO 创建购买收据,但是我无法以相同的方式添加转移收据"购买收据.

I'm struggling with a few API calls which require the logic in this file to be overridden or added to. For example I am able to create a purchase receipt for a PO via the api successfully, however I'm not able to add a "Transfer Receipt" purchase receipt in the same way.

我在 API 端点中包含了各种非标准字段,这些字段引用了原始转移、转移订单和发货,但都没有成功.API 调用成功,但未添加任何行.我已经能够让行在生成后更新其数量,但无法添加或删除当前行.

I've included various non-standard fields to the API endpoints that reference the original transfer, transfer order and shipment but have been unsuccessful. The API calls succeeds but no lines are added. I've been able to get lines to update their quantity once generated but can't add or delete current lines.

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

The problem looks to stem from this code, which seems to add functionality that correctly handles adding Purchase order lines logic of the graph but doesn't do anything special for other receipt types.

[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();
    }
}

推荐答案

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

After a lot of trial and error I have managed to get everything working as I need. However I'm concerned that since this is not documented information that it may be subject to change in the near future.

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

The methods in the class DefaultEndpointImpl are called via reflection. To add custom functionality for my default endpoint I have extended the default class and added the necessary attributes to my class and functions.

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

My custom class adds the following:

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

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

The first one was pretty straight forward and I'm happy with the outcome. The second one took a lot of time and debugging to get right, is hacky and probably doesn't work with serial tracked items. The reason it's taken so long is because there are many unknowns and I've essentially been guessing based on the code in the base class. For example I'm not even sure at which stage this function fires. It looks the functions completely override some other default logic which is invisible.

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

So take it or leave it! Maybe I will reach out to Acumatica to see if I can get more information.

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;
    }

  }
}

这篇关于Acumatica:能够覆盖 DefaultEndpointImpl 以向合约 REST API 添加高级自定义逻辑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆