Solved

Additional row added when selecting from Selector

  • 1 May 2023
  • 9 replies
  • 128 views

Userlevel 3
Badge

Hi Team,

We have an issue with additional row getting added in a grid after selecting a value from selector.

Issue

  1. Selecting an inventory Id from a Selector
  1. After the selecting from the selector an additional row is added

    Any thoughts why there is an additional row populating?

icon

Best answer by vibindas 20 June 2023, 23:00

View original

9 replies

Userlevel 7
Badge +17

HI @vibindas  Do you have any customizations in the SO301000 screen?

 

Userlevel 3
Badge

@Naveen Boga , yes and also upgraded recently to 2022R1 from 2019R1. It used to work in 2019.

Userlevel 7
Badge +4

@vibindas, Below are few option you can try to fix the issue,

  • Check if the issue is reproducible without the customization
  • If the issue is reproducible with the customization only, you can debug the customization to narrow down to the cause of the issue
Userlevel 7
Badge +17

@vibindas  Possible to share the source code here for our review?

Userlevel 3
Badge

sharing only the skeleton of the events related to CROpportunityProducts. Hope this helps...


namespace PX.Objects.CR
{
    public class MAKLQuoteMaintExt : PXGraphExtension<QuoteMaint>
    {
        [PXDBInt(IsKey = true)]
        [PXLineNbr(typeof(CRQuote.productCntr))]
        [PXUIField(DisplayName = "Line", Enabled = false)]
        protected virtual void CROpportunityProducts_LineNbr_CacheAttached(PXCache e) { }

        [PXMergeAttributes]
        [PXRemoveBaseAttribute(typeof(InventoryAttribute))]
        [Inventory(typeof(Search5<InventoryItem.inventoryID,
                LeftJoin<INItemClass, On<INItemClass.itemClassID, Equal<InventoryItem.itemClassID>>,
                LeftJoin<PMTask, On<PMTask.taskID, Equal<MAKLXBINItemClassExt.usrMAKLCommonTaskID>>,
                LeftJoin<MAKLEdition, On<MAKLEdition.editionID, Equal<MAKLXBInventoryItemExt.usrMAKLEditionID>>,
                LeftJoin<MAKLEdition2, On<MAKLEdition2.editionID, Equal<Current<MAKLCRQuoteExt.usrMAKLEditionID>>>>>>>,
                Where2<Match<Current<AccessInfo.userName>>,
                    And<InventoryItem.itemStatus, NotEqual<InventoryItemStatus.inactive>,
                    And<InventoryItem.itemStatus, NotEqual<InventoryItemStatus.markedForDeletion>,
                    And2<Where<MAKLXBInventoryItemExt.usrMAKLIsInternal, Equal<False>,
                        Or<Exists<Select<Users,
                            Where<Users.pKID, Equal<Current<AccessInfo.userID>>,
                                And<Users.guest, Equal<False>>>>>>>,
                    And2<
                        Where<MAKLXBInventoryItemExt.usrMAKLEditionID, Equal<Current<MAKLCRQuoteExt.usrMAKLEditionID>>,
                            Or<MAKLXBInventoryItemExt.usrMAKLEditionID, IsNull,
                            Or<Current<MAKLCRQuoteExt.usrMAKLEditionID>, IsNull,
                            Or<MAKLEdition.isExclusive, Equal<False>,
                            Or<MAKLEdition2.isExclusive, Equal<False>>>>>>,
                        And<PMTask.taskCD, Equal<Current<CRQuote.opportunityClassID>>>>>>
                    >>,
                Aggregate<GroupBy<InventoryItem.inventoryID>>>),
            typeof(InventoryItem.inventoryCD), typeof(InventoryItem.descr), Filterable = true)]
        protected virtual void CROpportunityProducts_InventoryID_CacheAttached(PXCache e) { }

        [PXDBInt(IsKey = true)]
        [PXLineNbr(typeof(MAKLCRQuoteExt.usrMAKLHistoryCntr))]
        protected virtual void MAKLOpportunityHistory_LineNbr_CacheAttached(PXCache e) { }

       

       [PXViewName(Messages.QuoteProducts)]
        [PXFilterable]
        [PXImport(typeof(CRQuote))]
        public PXSelect<CROpportunityProducts,
            Where<CROpportunityProducts.quoteID, Equal<Current<CRQuote.quoteID>>>,
            OrderBy<Asc<MAKLCROpportunityProductsExt.usrMAKLSortOrder>>>
            Products;

        public override void Initialize()
        {
            base.Initialize();            
        }

       
          public virtual void CROpportunityProducts_RowSelected(PXCache sender, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
        {
            if (InvokeBaseHandler != null) InvokeBaseHandler(sender, e);
            CROpportunityProducts row = (CROpportunityProducts)e.Row;
            if (row?.InventoryID == null) return;
            MAKLCROpportunityProductsExt rowE = PXCache<CROpportunityProducts>.GetExtension<MAKLCROpportunityProductsExt>(row);

            bool downgradeItem = rowE.UsrMAKLContrSourceLineNbr != null;
            bool sourceItem = false;
            switch (rowE.UsrMAKLSourceLineType)
            {
                case MAKLOpportunitySourceLineTypes.Bundle:
                case MAKLOpportunitySourceLineTypes.Margin:
                case MAKLOpportunitySourceLineTypes.Unit:
                    sourceItem = true;
                    break;
            }
            if (downgradeItem || sourceItem)
            {
                if (e.Row != null)
                    PXUIFieldAttribute.SetEnabled(sender, row, false);
            }
            else
            {
                
                InventoryItem item = PXSelect<InventoryItem, Where<InventoryItem.inventoryID, Equal<Required<InventoryItem.inventoryID>>>>
              .Select(Base, row.InventoryID);
                MAKLXBInventoryItemExt itemE = PXCache<InventoryItem>.GetExtension<MAKLXBInventoryItemExt>(item);
                if (e.Row == null) return;

                PXUIFieldAttribute.SetEnabled<CROpportunityProducts.curyUnitPrice>(sender, row,
                    (itemE.UsrMAKLIsAdhoc == true || itemE.UsrMAKLIsOverridable == true) && row?.IsFree != true);

                PXUIFieldAttribute.SetEnabled<CROpportunityProducts.manualDisc>(sender, row, row.IsFree != true);

                bool autoFreeItem = row.ManualDisc != true && row.IsFree == true;

                if (autoFreeItem)
                {
                    PXUIFieldAttribute.SetEnabled(sender, row, false);
                    PXUIFieldAttribute.SetEnabled<CROpportunityProducts.siteID>(sender, row);
                }
                PXUIFieldAttribute.SetEnabled<CROpportunityProducts.quantity>(sender, row, !autoFreeItem);
                PXUIFieldAttribute.SetEnabled<CROpportunityProducts.isFree>(sender, row, !autoFreeItem && row.InventoryID != null);

                
            }

            if (rowE.UsrMAKLSourceLineType == MAKLOpportunitySourceLineTypes.Bundle)
            {
                PXUIFieldAttribute.SetEnabled<CROpportunityProducts.discPct>(sender, row, true);
                PXUIFieldAttribute.SetEnabled<CROpportunityProducts.discountID>(sender, row, true);
                PXUIFieldAttribute.SetEnabled<MAKLCROpportunityProductsExt.usrMAKLDiscountDuration>(sender, row, true);
            }

            if (rowE.UsrMAKLContrSourceLineNbr != null)
            {
                CROpportunityProducts nextDowngrade = PXSelect<CROpportunityProducts,
                    Where<CROpportunityProducts.quoteID, Equal<Required<CROpportunityProducts.quoteID>>,
                        And<CROpportunityProducts.inventoryID, Equal<Required<CROpportunityProducts.inventoryID>>,
                        And<CROpportunityProducts.lineNbr, Greater<Required<CROpportunityProducts.lineNbr>>,
                        And<CROpportunityProducts.quantity, Less<decimal0>>>>>>
                    .Select(Base, row.QuoteID, row.InventoryID, row.LineNbr);
                PXUIFieldAttribute.SetEnabled<CROpportunityProducts.quantity>(sender, row, nextDowngrade == null && !sourceItem);
            }

            CROpportunityProducts child = PXSelect<CROpportunityProducts,
                Where<CROpportunityProducts.quoteID, Equal<Required<CROpportunityProducts.quoteID>>,
                    And2<Where<MAKLCROpportunityProductsExt.usrMAKLSourceLineType, Equal<MAKLOpportunitySourceLineTypes.unit>,
                        Or<MAKLCROpportunityProductsExt.usrMAKLSourceLineType, Equal<MAKLOpportunitySourceLineTypes.parent>>>,
                    And<MAKLCROpportunityProductsExt.usrMAKLSourceLineNbr, Equal<Required<MAKLCROpportunityProductsExt.usrMAKLSourceLineNbr>>>>>>
                .Select(Base, row.QuoteID, row.LineNbr);
            PXUIFieldAttribute.SetEnabled<CROpportunityProducts.inventoryID>(sender, row, child == null && !sourceItem && !downgradeItem);

        }

      
        public virtual void CROpportunityProducts_RowInserted(PXCache sender, PXRowInsertedEventArgs e, PXRowInserted InvokeBaseHandler)
        {
            if (e.Row == null) return;
            CROpportunityProducts row = (CROpportunityProducts)e.Row;
            ResetManualFlags();
            MAKLCROpportunityProductsExt rowE = PXCache<CROpportunityProducts>.GetExtension<MAKLCROpportunityProductsExt>(row);

            UpdateQuoteEdition(row);

            MAKLCRQuoteExt quoteE = Quote.Cache.GetExtension<MAKLCRQuoteExt>(Quote.Current);
            CROpportunityClass opClass = (CROpportunityClass)PXSelectorAttribute.Select<CRQuote.opportunityClassID>(Quote.Cache, Quote.Current);
            MAKLCROpportunityClassExt opClassE = PXCache<CROpportunityClass>.GetExtension<MAKLCROpportunityClassExt>(opClass);
            bool diffQty = opClassE.UsrMAKLUpsellMethod == MAKLUpsellMethod.Pricelist && quoteE.UsrMAKLType != MAKLOpportunityType.NewSale;

            if (row.Quantity < 0)
            {
                DoDowngrade(row, 0, e.ExternalCall);
            }
            else
            {
                UpdateUpsellDetail(row, row.Quantity);
            }

            AddAutoexplodeLines(row);
            AddUnitLines(row, diffQty);
            DoBundleTree(row, null, MAKLBundleType.Child, diffQty);
            DoBundleTree(row, null, MAKLBundleType.Dependency, diffQty);
            AddMarginLines(row, diffQty);

            if (rowE.UsrMAKLContrSourceLineNbr == null)
            {
                UpdateOtherQuoteLinesPrice(row, Math.Max(row.CuryUnitPrice ?? 0, rowE.UsrMAKLCuryClippedUnitPrice ?? 0));
            }

            if (e.ExternalCall == true)
            {
                Products.View.RequestRefresh();
                UndoRemoveCredit(Quote.Current);
                RemoveCredit(Quote.Current);
            }

            if (InvokeBaseHandler != null) InvokeBaseHandler(sender, new PXRowInsertedEventArgs(Products.Locate(row), e.ExternalCall));
        }


        public virtual void CROpportunityProducts_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e, PXRowUpdated InvokeBaseHandler)
        {
            if (e.Row == null) return;
            CROpportunityProducts row = (CROpportunityProducts)e.Row;
            CROpportunityProducts oldrow = (CROpportunityProducts)e.OldRow;

            MAKLCROpportunityProductsExt rowE = PXCache<CROpportunityProducts>.GetExtension<MAKLCROpportunityProductsExt>(row);
            MAKLCROpportunityProductsExt oldrowE = PXCache<CROpportunityProducts>.GetExtension<MAKLCROpportunityProductsExt>(oldrow);
            MAKLCRQuoteExt quoteE = Quote.Cache.GetExtension<MAKLCRQuoteExt>(Quote.Current);

            CROpportunityClass opClass = (CROpportunityClass)PXSelectorAttribute.Select<CRQuote.opportunityClassID>(Quote.Cache, Quote.Current);
            MAKLCROpportunityClassExt opClassE = PXCache<CROpportunityClass>.GetExtension<MAKLCROpportunityClassExt>(opClass);
            bool diffQty = opClassE.UsrMAKLUpsellMethod == MAKLUpsellMethod.Pricelist && quoteE.UsrMAKLType != MAKLOpportunityType.NewSale;

            if (row.Quantity < 0 && (row.Quantity - oldrow.Quantity < 0 || row.InventoryID != oldrow.InventoryID)
                || !sender.ObjectsEqual<MAKLCROpportunityProductsExt.usrMAKLContrSourceLineNbr>(row, oldrow))
            {
                decimal? oldQuantity = (rowE.UsrMAKLSourceLineType != MAKLOpportunitySourceLineTypes.Margin && oldrow.Quantity < 0
                    || rowE.UsrMAKLSourceLineType == MAKLOpportunitySourceLineTypes.Margin && oldrow.Quantity > 0)
                    ? oldrow.Quantity : 0;
                DoDowngrade(row, oldQuantity, e.ExternalCall);
            }
            else
            {
                UpdateUpsellDetail(row, row.Quantity - oldrow.Quantity);
            }

            if (rowE.UsrMAKLSourceLineType != MAKLOpportunitySourceLineTypes.Margin)
            {
                if (!sender.ObjectsEqual<CROpportunityProducts.inventoryID>(row, oldrow))
                {
                    RemoveAutoexplodeLines(row);
                    AddAutoexplodeLines(row);
                }
                else if (!sender.ObjectsEqual<CROpportunityProducts.quantity, CROpportunityProducts.uOM>(row, oldrow)
                    || !diffQty)
                {
                    AddUnitLines(row, diffQty);
                    if (!sender.ObjectsEqual<CROpportunityProducts.quantity, CROpportunityProducts.uOM>(row, oldrow))
                    {
                        UpdateAutoexplodeLines(row);
                    }
                }

                bool removeMargin = diffQty && row.Quantity < 0 && oldrow.Quantity >= 0 || !diffQty && row.Quantity == 0;
                if (!removeMargin && !diffQty && row.UOM != oldrow.UOM)
                {
                    bool isInt = int.TryParse(row.UOM, out int newUOM);
                    isInt &= int.TryParse(oldrow.UOM, out int oldUOM);
                    removeMargin = isInt && newUOM < oldUOM;
                }
                if (removeMargin)
                {
                    RemoveNonRecurringMargin(row, diffQty);
                }
            }

            DoBundleTree(row, oldrow.Quantity, MAKLBundleType.Child, diffQty);
            DoBundleTree(row, oldrow.Quantity, MAKLBundleType.Dependency, diffQty);

            if (InvokeBaseHandler != null) InvokeBaseHandler(sender, new PXRowUpdatedEventArgs(Products.Cache.Locate(row), oldrow, e.ExternalCall));

            row = (CROpportunityProducts)Products.Cache.Locate(row);
            rowE = PXCache<CROpportunityProducts>.GetExtension<MAKLCROpportunityProductsExt>(row);
            if (row.InventoryID == null) return;

            if (rowE.UsrMAKLSourceLineType != MAKLOpportunitySourceLineTypes.Margin)
            {
                if (!sender.ObjectsEqual<CROpportunityProducts.inventoryID>(row, oldrow))
                {
                    RemoveMarginLines(row);
                    AddMarginLines(row, diffQty);
                }
                else if (!sender.ObjectsEqual<CROpportunityProducts.curyUnitPrice, CROpportunityProducts.curyDiscAmt, CROpportunityProducts.quantity,
                        CROpportunityProducts.discPct, CROpportunityProducts.descr, CROpportunityProducts.uOM>(row, oldrow)
                        && rowE.UsrMAKLSourceLineType != MAKLOpportunitySourceLineTypes.Margin)
                {
                    UpdateMarginLines(row);

                    if (opClassE.UsrMAKLUpsellMethod == MAKLUpsellMethod.AmountDiff && quoteE.UsrMAKLType == MAKLOpportunityType.Upsell) //Greentree upsale only
                    {
                        UpdateUnitLines(row);
                    }
                }

                if (e.ExternalCall == true)
                {
                    if (rowE.UsrMAKLContrSourceLineNbr == null && Math.Max(row.CuryUnitPrice ?? 0, rowE.UsrMAKLCuryClippedUnitPrice ?? 0)
                         != Math.Max(oldrow.CuryUnitPrice ?? 0, oldrowE.UsrMAKLCuryClippedUnitPrice ?? 0))
                    {
                        UpdateOtherQuoteLinesPrice(row, Math.Max(row.CuryUnitPrice ?? 0, rowE.UsrMAKLCuryClippedUnitPrice ?? 0));
                    }

                    if (rowE.UsrMAKLContrSourceLineNbr != null && row.Quantity != oldrow.Quantity)
                    {
                        RepriceOtherQuoteLines(row);
                    }

                    if (quoteE.UsrMAKLIsNoCredit == true)
                    {
                        UndoRemoveCredit(Quote.Current);
                        RemoveCredit(Quote.Current);
                    }
                }
            }

            ResetManualFlags();

            if (!sender.ObjectsEqual<CROpportunityProducts.discPct, CROpportunityProducts.discountID, CROpportunityProducts.discountSequenceID,
                    MAKLCROpportunityProductsExt.usrMAKLFreeDuration, MAKLCROpportunityProductsExt.usrMAKLPriceDuration,
                    MAKLCROpportunityProductsExt.usrMAKLDiscountDuration>(row, oldrow))
            {
                Products.Cache.SetDefaultExt<MAKLCROpportunityProductsExt.usrMAKLIsPreapproved>(row);
            }

            if (e.ExternalCall == true)
            {
                Products.View.RequestRefresh();
            }
        }        

        public void AddUnitLines(CROpportunityProducts row, bool diffQty)
        {
            if (row == null) return;
            CRQuote quote = Quote.Current;
            if (quote == null) return;
            MAKLCRQuoteExt quoteE = quote.GetExtension<MAKLCRQuoteExt>();
            if (quoteE.UsrMAKLType == MAKLOpportunityType.NewSale) return;
            MAKLCROpportunityProductsExt rowE = PXCache<CROpportunityProducts>.GetExtension<MAKLCROpportunityProductsExt>(row);
            if (rowE.UsrMAKLSourceLineType != MAKLOpportunitySourceLineTypes.Unit
                && (row.Quantity == 1 || (!diffQty && row.Quantity >= 0)))
            {
                InventoryItem item = (InventoryItem)PXSelectorAttribute.Select<CROpportunityProducts.inventoryID>(Products.Cache, row);
                if (item?.BaseUnit != item?.SalesUnit || !diffQty)
                {
                    CROpportunityProducts previous = PXSelect<CROpportunityProducts,
                        Where<CROpportunityProducts.quoteID, Equal<Current<CRQuote.quoteID>>,
                            And<MAKLCROpportunityProductsExt.usrMAKLSourceLineType, Equal<MAKLOpportunitySourceLineTypes.unit>,
                            And<MAKLCROpportunityProductsExt.usrMAKLSourceLineNbr, Equal<Required<MAKLCROpportunityProductsExt.usrMAKLSourceLineNbr>>>>>>
                            .Select(Base, row.LineNbr);
                    if (previous != null) return;

                    List<MAKLUpsellQuoteDetails> downLines = GetDowngradeableLines(row.InventoryID);
                    foreach (MAKLUpsellQuoteDetails downLine in downLines)
                    {
                        CROpportunityProducts unitDowngrade = Products.Insert();
                        MAKLCROpportunityProductsExt unitDowngradeE = PXCache<CROpportunityProducts>.GetExtension<MAKLCROpportunityProductsExt>(unitDowngrade);
                        unitDowngradeE.UsrMAKLSourceLineType = MAKLOpportunitySourceLineTypes.Unit;
                        unitDowngradeE.UsrMAKLSourceLineNbr = row.LineNbr;
                        unitDowngrade.Quantity = -downLine.CurQuantity;
                        if (item?.BaseUnit != item?.SalesUnit)
                        {
                            unitDowngrade.UOM = downLine.UOM;
                        }
                        if (rowE.UsrMAKLSourceLineType != MAKLOpportunitySourceLineTypes.Bundle)
                        {
                            unitDowngrade.InventoryID = row.InventoryID;
                        }
                        else
                        {
                            CROpportunityProducts srcLine = PXSelect<CROpportunityProducts,
                                Where<CROpportunityProducts.quoteID, Equal<Current<CRQuote.quoteID>>,
                                    And<CROpportunityProducts.lineNbr, Equal<Required<CROpportunityProducts.lineNbr>>>>>
                                    .Select(Base, rowE.UsrMAKLSourceLineNbr);
                            unitDowngrade.InventoryID = srcLine?.InventoryID ?? row.InventoryID;
                        }

                        Products.Update(unitDowngrade);
                    }
                }
            }
        }

        public void RemoveUnitLines(CROpportunityProducts row)
        {
            if (row == null) return;

            foreach (CROpportunityProducts unitline in PXSelect<CROpportunityProducts,
                Where<CROpportunityProducts.quoteID, Equal<Required<CROpportunityProducts.quoteID>>,
                    And<MAKLCROpportunityProductsExt.usrMAKLSourceLineNbr, Equal<Required<CROpportunityProducts.lineNbr>>,
                    And<MAKLCROpportunityProductsExt.usrMAKLSourceLineType, Equal<MAKLOpportunitySourceLineTypes.unit>>>>>
                .Select(Base, row.QuoteID, row.LineNbr))
            {
                Products.Delete(unitline);
            }
        }

     

        public enum BundleItem
        {
            Parent,
            Child
        }

        public List<XRBBundleComp> GetBundle(int? InventoryID, BundleItem side, string BundleType, bool? FixedQty)
        {
            if (InventoryID == null) return new List<XRBBundleComp>();
            PXSelectBase<XRBBundleComp> sel = new PXSelect<XRBBundleComp,
                Where<MAKLXRBBundleCompExt.usrMAKLBundleType, Equal<Required<MAKLXRBBundleCompExt.usrMAKLBundleType>>>>(Base);
            if (side == BundleItem.Parent)
            {
                sel.WhereAnd<Where<XRBBundleComp.bundleInventoryID, Equal<Required<XRBBundleComp.bundleInventoryID>>>>();
            }
            else
            {
                sel.WhereAnd<Where<XRBBundleComp.inventoryID, Equal<Required<XRBBundleComp.inventoryID>>>>();
            }
            if (FixedQty != null)
            {
                sel.WhereAnd<Where<MAKLXRBBundleCompExt.usrMAKLFixedQty, Equal<Required<MAKLXRBBundleCompExt.usrMAKLFixedQty>>>>();
            }
            return sel.Select(BundleType, InventoryID, FixedQty).RowCast<XRBBundleComp>().ToList();
        }

        public List<XRBBundleComp> GetBundleTrans(int? InventoryID, BundleItem side, string BundleType, bool? FixedQty)
        {
            if (InventoryID == null) return new List<XRBBundleComp>();
            List<XRBBundleComp> bundle = GetBundle(InventoryID, side, BundleType, FixedQty);
            if (bundle.Count == 0)
            {
                bundle = GetBundle(GetILFBundle(InventoryID)?.BundleInventoryID, side, BundleType, FixedQty);
            }
            return bundle;
        }        

        public class MAKLQuoteMaintSalesPriceGraphExt : PXGraphExtension<QuoteMaint.SalesPrice, QuoteMaint>
        {
            public virtual void CROpportunityProducts_RowSelected(PXCache sender, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
            {
                Base.GetExtension<MAKLQuoteMaintExt>().CROpportunityProducts_RowSelected(sender, e, InvokeBaseHandler);
            }
        }

        public class MAKLQuoteMaintDiscountGraphExt : PXGraphExtension<QuoteMaint.Discount, QuoteMaint>
        {
            public virtual void CROpportunityProducts_RowSelected(PXCache sender, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
            {

            }
        }
    }
}

Userlevel 3
Badge

@Naveen Boga , I have shared piece of code relevant to CROpportunityProducts

Userlevel 7
Badge

Hi @vibindas were you able to find a solution? Thank you!

Userlevel 3
Badge

@Chris Hackett , yes I added a check at event  “CROpportunityProducts_RowInserting” 

 CROpportunityProducts row = (CROpportunityProducts)e.Row;
            if (row.InventoryID == null)
            {
                e.Cancel = true;
            }         

the additional row had the inventory id null, this check prevents the additional row from getting added to the cache.

Userlevel 7
Badge

Thank you for sharing your solution with the community @vibindas !

Reply


About Acumatica ERP system
Acumatica Cloud ERP provides the best business management solution for transforming your company to thrive in the new digital economy. Built on a future-proof platform with open architecture for rapid integrations, scalability, and ease of use, Acumatica delivers unparalleled value to small and midmarket organizations. Connected Business. Delivered.
© 2008 — 2024  Acumatica, Inc. All rights reserved