/////////////////////////////////////////////////////////// // PartInventoryList.cs // Implementation of Class PartInventoryList // CSLA type: Read only collection // Created on: 28-Nov-2007 // Object design: John // Coded: 28-Nov-2007 /////////////////////////////////////////////////////////// using System; using System.Data; using GZTW.Data; using CSLA.Data; using CSLA; using System.ComponentModel; using System.Collections.Generic; namespace GZTW.AyaNova.BLL { #pragma warning disable 1591 /// /// Similar to but /// for retrieving inventory and serials of a single part /// specified by part id. /// /// Used by AyaNova WBI Only in PartInventoryView page /// /// [Serializable] public class PartInventoryList : ReadOnlyCollectionBase, ITypedList { #region ITypedList //Implementation of ITypedList to allow a grid to display a read only child collection //within a read only collection PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) { if (listAccessors == null)//then return properties of this (the parent) collection { //This class this code is in is the implementation of the //PartInventoryList read only collection business object return TypeDescriptor.GetProperties(typeof(PartInventoryListInfo)); } else if (listAccessors.Length == 1)//then return the properties of a child object { //Return the properties of the struct inside the //PartInventoryList read only collection if (listAccessors[0].Name == "LT_PartSerial_Label_List") return TypeDescriptor.GetProperties(typeof(PartSerialPickList.PartSerialPickListInfo)); //add more here if there are more children or grandchildren } return null; } /// /// Not required for grid but must be implemented /// /// /// string ITypedList.GetListName(PropertyDescriptor[] listAccessors) { return ""; } #endregion #region Data structure /// /// Properties /// [Serializable] public struct PartInventoryListInfo { internal Guid mID; internal GridNameValueCellItem mPartWarehouseID; internal GridNameValueCellItem mPartID; internal decimal mQuantityOnHand; internal decimal mQuantityOnOrder; internal decimal mReorderQuantity; internal decimal mMinStockLevel; internal GridNameValueCellItem mPartCategory; internal GridNameValueCellItem mPartAssembly; //case 265 internal bool mPartActive; //case 1975 internal decimal mQtyOnOrderCommitted; [SqlColumnNameAttribute("aPartAssembly.aName", "aPartAssembly.aID"), Display(DisplayType.Button, RootObjectType = RootObjectTypes.PartAssembly)] public GridNameValueCellItem LT_O_PartAssembly { get { return mPartAssembly; } } [SqlColumnNameAttribute("aPartCategory.aName", "aPartCategory.aID"), Display(DisplayType.Button, RootObjectType = RootObjectTypes.PartCategory)] public GridNameValueCellItem LT_O_PartCategory { get { return mPartCategory; } } internal PartSerialPickList mSerials; [SqlColumnNameAttribute("grid"), Display(DisplayType.Text)] public PartSerialPickList LT_PartSerial_Label_List { get { return mSerials; } } [Display(DisplayType.Hidden)] public Guid ID { get { return mID; } } [SqlColumnNameAttribute("aPartWarehouse.aName", "aPartByWarehouseInventory.aPartWarehouseID"), Display(DisplayType.Button, RootObjectType = RootObjectTypes.PartWarehouse)] public GridNameValueCellItem LT_O_PartWarehouse { get { return mPartWarehouseID; } } [SqlColumnNameAttribute("aPart.aPartNumber", "aPartByWarehouseInventory.aPartID"), Display(DisplayType.Button, RootObjectType = RootObjectTypes.Part)] public GridNameValueCellItem LT_O_Part { get { return mPartID; } } [Display(DisplayType.DecimalNumber)] public decimal LT_PartByWarehouseInventory_Label_QuantityOnHand { get { return mQuantityOnHand; } } [Display(DisplayType.DecimalNumber)] public decimal LT_PartByWarehouseInventory_Label_QuantityOnOrder { get { return mQuantityOnOrder; } } [Display(DisplayType.DecimalNumber)] public decimal LT_PartByWarehouseInventory_Label_MinStockLevel { get { return mMinStockLevel; } } [SqlColumnNameAttribute("AREORDERQUANTITY"), Display(DisplayType.DecimalNumber)] public decimal LT_PartByWarehouseInventory_Label_ReorderQuantity { get { return mReorderQuantity; } } //Case 265 [Display(DisplayType.TrueFalse)] public bool LT_Part_Label_Active { get { return mPartActive; } } //Case 58 internal GridNameValueCellItem mRegion; [SqlColumnNameAttribute("aRegion.aName", "aRegion.aID"), Display(DisplayType.Button, RootObjectType = RootObjectTypes.Region)] public GridNameValueCellItem LT_O_Region { get { return mRegion; } } //case 1975 [Display(DisplayType.DecimalNumber)] public decimal LT_PartByWarehouseInventory_Label_QtyOnOrderCommitted { get { return mQtyOnOrderCommitted; } } //case 1975 internal decimal mTotal; public decimal Total { get { return mTotal; } } /// /// /// /// public bool Equals(PartInventoryListInfo obj) { return this.mID.Equals(obj.mID); } }//end PartInventoryListInfo #endregion #region Constructor protected PartInventoryList() { // AllowSort=false; // AllowFind=true; // AllowEdit=false; // AllowNew=false; // AllowRemove=false; } #endregion #region Business properties and methods /// /// Get item by index /// /// public PartInventoryListInfo this[int Item] { get { return (PartInventoryListInfo)List[Item]; } } /// /// Returns display text that matches passed in itemid value /// /// public string this[Guid ItemID] { get { foreach (PartInventoryListInfo child in List) { if (child.mID == ItemID) return child.ToString(); } return "Missing: " + ItemID.ToString(); } } #endregion #region contains /// /// Check if item in collection /// /// public bool Contains(PartInventoryListInfo obj) { foreach (PartInventoryListInfo child in List) { if (child.Equals(obj)) return true; } return false; } #endregion #region Reporting and shared UI editor helpers /// /// Returns the report key which is a property of /// reports used to link all reports that can be used /// with a particular data source. /// public static string ReportKey { get { return "PartInventoryList"; } } /// /// Returns the Detailed report key /// which is used to determine which reports and objects /// will be used for detailed reports /// /// If empty string then indicates there is no detailed report object or reports /// public static string DetailedReportKey { get { return ""; } } /// /// Base object that this list is reporting on /// used by shared UI editor to instantiate new objects /// when user selects new in UI elements that display this list /// /// (I.E. when user clicks on new in a read only list grid, this is the object type created) /// public static RootObjectTypes BaseObjectType { get { return RootObjectTypes.Nothing; } } /// /// Locale key so that generic list editor /// UI code knows what title to give the list in a /// grid /// public string LocaleKey { get { return "PartByWarehouseInventory.Label.List"; } } /// /// The Type of the struct used to store list records /// Used to fetch the custom display attributes of the fields /// contained within the record to modify the grid display accordingly /// /// public static Type ListRecordType { get { return typeof(PartInventoryListInfo); } } /// /// Field that contains the ID of the objects /// that are the basis of this list. /// /// Used for compiling an ID list for reporting from user /// selections in a grid. /// public static string IDField { get { return "ID"; } } /// /// Same as IDField but for detailed reports /// public static string IDFieldDetailed { get { return IDField; } } #endregion #region Static methods /// /// Internal method used by list factory /// internal static PartInventoryList Get(string Filter, int MaxRecords, List IDList) { return (PartInventoryList)DataPortal.Fetch(new Criteria(Filter, IDList, MaxRecords)); } /// /// Get all PartByWarehouseInventory (filtered by crit) /// /// Use AyaNova UI to easily build xmlCriteria and Ctrl-Alt-g keyboard command to display it for use in your code /// public static PartInventoryList GetList(string xmlCriteria) { return (PartInventoryList)DataPortal.Fetch(new Criteria(xmlCriteria, null, -1)); } /// /// Takes a single PART ID and returns a "list" of one object /// /// ID of PART public static PartInventoryList GetListForSingleItem(Guid PartID) { //Case 556 List l = new List(); l.Add(PartID); return GetListFromIDList(l); } /// /// Get list by part id's indicated in IDList /// /// Generic list of Part Guid's /// public static PartInventoryList GetListFromIDList(List IDList) { //case 556 //Handle empty list if (IDList.Count == 0) return new PartInventoryList(); return (PartInventoryList)DataPortal.Fetch(new Criteria("", IDList, -1)); } /// /// Return an empty list /// used for initializing grid /// /// public static PartInventoryList GetEmptyList() { return new PartInventoryList(); } //case 1975 /// /// Takes a single PART ID and returns a "list" of one object /// /// ID of PART /// Maximum records to return /// public static PartInventoryList GetListForSingleItem(Guid PartID, int MaxRecords) { //Case 556 List l = new List(); l.Add(PartID); return (PartInventoryList)DataPortal.Fetch(new Criteria("", l, MaxRecords)); } #endregion #region DAL DATA ACCESS /// /// protected override void DataPortal_Fetch(object Criteria) { Criteria crit = (Criteria)Criteria; SafeDataReader dr = null; try { //case 1975 decimal dTotal = 0; //Workaround for sorting on the calculated column string sSort = AyaBizUtils.GetGridSortOrderColumns(crit.CriteriaXML); sSort = sSort.Replace("AREORDERQUANTITY", "(aPartByWarehouseInventory.aMinStockLevel-(aPartByWarehouseInventory.aQuantityOnHand + aPartByWarehouseInventory.aQuantityOnOrder))"); //This is a funky workaround because AREORDERQUANTITY is based on a calculated value //you can't filter on an alias even though you can sort on one wierd in MS SQL (In firebird you can't // do either. AREORDERQUANTITY //so when it's filtered, it's distinctive name is easy to replace with the filter calculation //whereas when it's sorted it simply uses the alias so we replace here in case of filter" //Also we display zero when a reorderqty is less than zero so there needs to be a //hack to change a filter of =0 (as the user sees it) to <1 string sFilter = AyaBizUtils.GetGridColumnCriteria(crit.CriteriaXML, true); if (sFilter.IndexOf("AREORDERQUANTITY =0") != -1) sFilter = sFilter.Replace("AREORDERQUANTITY =0", "(aPartByWarehouseInventory.aMinStockLevel-(aPartByWarehouseInventory.aQuantityOnHand + aPartByWarehouseInventory.aQuantityOnOrder)) <1"); else if (sFilter.IndexOf("AREORDERQUANTITY <0") != -1) sFilter = sFilter.Replace("AREORDERQUANTITY <0", "(aPartByWarehouseInventory.aMinStockLevel-(aPartByWarehouseInventory.aQuantityOnHand + aPartByWarehouseInventory.aQuantityOnOrder)) <1"); else sFilter = sFilter.Replace("AREORDERQUANTITY", "(aPartByWarehouseInventory.aMinStockLevel-(aPartByWarehouseInventory.aQuantityOnHand + aPartByWarehouseInventory.aQuantityOnOrder)) "); string q =//************************************************************ "SELECT ~MAXRECS~ aPart.aPartNumber AS aPARTNUMBER, aPart.aName, aPart.aActive, " +//case 265 added apartactive " aPartByWarehouseInventory.aPartWarehouseID, " + " aPartWarehouse.aName AS aPartWarehouseName, " + " aPartWarehouse.aRegionID, aRegion.aName AS aRegionName, " + //case 58 " aPartByWarehouseInventory.aQuantityOnHand, " + " aPartByWarehouseInventory.aQtyOnOrderCommitted, " + " aPartByWarehouseInventory.aQuantityOnOrder, " + " aPartByWarehouseInventory.aMinStockLevel, " + "aPartByWarehouseInventory.aID, aPartByWarehouseInventory.aMinStockLevel " + "- (aPartByWarehouseInventory.aQuantityOnHand " + "+ aPartByWarehouseInventory.aQuantityOnOrder) " + "AS AREORDERQUANTITY, " + "aPartByWarehouseInventory.aPartID, aPart.aUPC, " + " aVendor.aName AS aMANUFACTURERNAME, aPartCategory.aName " + "AS aPARTCATEGORYNAME, aPartCategory.aID " + "AS aPartCategoryID, aPartAssembly.aName " + "AS aPARTASSEMBLYNAME, aPartAssembly.aID " + "AS aPartAssemblyID " + "FROM " + " APARTBYWAREHOUSEINVENTORY " + " LEFT OUTER JOIN APART ON (APARTBYWAREHOUSEINVENTORY.APARTID=APART.AID) " + " LEFT OUTER JOIN AVENDOR ON (APART.AWHOLESALERID=AVENDOR.AID) " + " LEFT OUTER JOIN APARTWAREHOUSE ON (APARTBYWAREHOUSEINVENTORY.APARTWAREHOUSEID=APARTWAREHOUSE.AID) " + "LEFT OUTER JOIN aRegion ON APARTWAREHOUSE.aRegionID = aRegion.aID " + //Case 58 " LEFT OUTER JOIN APARTCATEGORY ON (APART.APARTCATEGORYID=APARTCATEGORY.AID) " + " LEFT OUTER JOIN APARTASSEMBLY ON (APART.APARTASSEMBLYID=APARTASSEMBLY.AID) "; //************************************************************ if (crit.IDList != null) { //case 1975 dTotal = DBUtil.ScalarToDecimal(DBUtil.GetScalarFromSQLString( "SELECT COUNT(AID) FROM APARTBYWAREHOUSEINVENTORY WHERE APARTBYWAREHOUSEINVENTORY.APARTID=@ID", crit.IDList[0])); //Case 556 System.Text.StringBuilder sbIN = new System.Text.StringBuilder(); sbIN.Append(" WHERE (aPartByWarehouseInventory.aPartID in ("); foreach (Guid gItem in crit.IDList) { sbIN.Append("'"); sbIN.Append("{"); sbIN.Append(gItem.ToString().ToUpperInvariant()); sbIN.Append("}"); sbIN.Append("',"); } sbIN.Length = sbIN.Length - 1; sbIN.Append(")) "); if (crit.MaxRecords > 0) q = q.Replace("~MAXRECS~", "TOP " + crit.MaxRecords.ToString()); else q = q.Replace("~MAXRECS~", ""); if (string.IsNullOrEmpty(sSort)) sSort = " ORDER BY APARTWAREHOUSE.ANAME"; q = q + sbIN.ToString() + sSort; } else { //Case 566 if (string.IsNullOrEmpty(sFilter)) sFilter = " WHERE (AQUANTITYONHAND <> 0 OR AQUANTITYONORDER <> 0 OR AMINSTOCKLEVEL <> 0) "; else sFilter += " AND (AQUANTITYONHAND <> 0 OR AQUANTITYONORDER <> 0 OR AMINSTOCKLEVEL <> 0) "; q = q + sFilter + sSort; if (crit.MaxRecords > 0) q = q.Replace("~MAXRECS~", "TOP " + crit.MaxRecords.ToString()); else q = q.Replace("~MAXRECS~", ""); q = DBUtil.AddRegionFilter(q, "aPartWarehouse", "");//case 58 } dr = DBUtil.GetReaderFromSQLString(q); //case 1975 bool bFirst = true; while (dr.Read()) { //******************************************* PartInventoryListInfo info = new PartInventoryListInfo(); info.mPartWarehouseID = new GridNameValueCellItem( dr.GetGuid("aPartWarehouseID"), dr.GetString("aPartWarehouseName"), RootObjectTypes.PartWarehouse); //Case 58 info.mRegion = new GridNameValueCellItem( dr.GetGuid("aRegionID"), dr.GetString("aRegionName"), RootObjectTypes.Region); info.mPartID = new GridNameValueCellItem( dr.GetGuid("aPartID"), Part.PartDisplayFormatter(dr.GetString("aName"), dr.GetString("aPARTNUMBER"), dr.GetString("aUPC"), dr.GetString("aMANUFACTURERNAME"), dr.GetString("aPARTCATEGORYNAME"), dr.GetString("aPARTASSEMBLYNAME"), AyaBizUtils.GlobalSettings.DefaultPartDisplayFormat), RootObjectTypes.Part); info.mPartCategory = new GridNameValueCellItem( dr.GetGuid("aPartCategoryID"), dr.GetString("aPartCategoryName"), RootObjectTypes.PartCategory); info.mPartAssembly = new GridNameValueCellItem( dr.GetGuid("aPartAssemblyID"), dr.GetString("aPartAssemblyName"), RootObjectTypes.PartAssembly); info.mQuantityOnHand = dr.GetDecimal("aQuantityOnHand"); info.mQuantityOnOrder = dr.GetDecimal("aQuantityOnOrder"); info.mMinStockLevel = dr.GetDecimal("aMinStockLevel"); //case 1975 info.mQtyOnOrderCommitted = dr.GetDecimal("aQtyOnOrderCommitted"); //TODO: Why is this necessary, if I pull in the value from the query //under firebird it's over by a factor of 10000 //haven't tested with mssql yet //info.mReorderQuantity=dr.GetDecimal("AREORDERQUANTITY"); info.mReorderQuantity = info.mMinStockLevel - (info.mQuantityOnHand + info.mQuantityOnOrder); if (info.mReorderQuantity < 0) info.mReorderQuantity = 0; info.mID = dr.GetGuid("aID"); info.mSerials = PartSerialPickList.GetList(info.mPartID.Value, info.mPartWarehouseID.Value); //case 265 info.mPartActive = dr.GetBoolean("AACTIVE"); if (bFirst) { info.mTotal = dTotal; bFirst = false; } InnerList.Add(info); //******************************************* } } finally { if (dr != null) dr.Close(); } } #endregion #region criteria /// /// Criteria for identifying existing object /// [Serializable] private class Criteria { public List IDList; public string CriteriaXML; public int MaxRecords; public Criteria(string _CriteriaXML, List _IDList, int _MaxRecords) { CriteriaXML = _CriteriaXML; IDList = _IDList; MaxRecords = _MaxRecords; } } #endregion }//end PartInventoryList #pragma warning restore 1591 }//end namespace GZTW.AyaNova.BLL