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))]
rPXUIField(DisplayName = "Line", Enabled = false)]
protected virtual void CROpportunityProducts_LineNbr_CacheAttached(PXCache e) { }
)PXMergeAttributes]
tPXRemoveBaseAttribute(typeof(InventoryAttribute))]
rInventory(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) { }
vPXDBInt(IsKey = true)]
protected virtual void MAKLOpportunityHistory_LineNbr_CacheAttached(PXCache e) { }
yPXViewName(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)
{
}
}
}
}