This commit is contained in:
2021-08-25 19:20:25 +00:00
parent 1b8929908d
commit 95c41a297e
10 changed files with 252 additions and 48 deletions

2
.vscode/launch.json vendored
View File

@@ -53,7 +53,7 @@
"AYANOVA_FOLDER_USER_FILES": "c:\\temp\\RavenTestData\\userfiles", "AYANOVA_FOLDER_USER_FILES": "c:\\temp\\RavenTestData\\userfiles",
"AYANOVA_FOLDER_BACKUP_FILES": "c:\\temp\\RavenTestData\\backupfiles", "AYANOVA_FOLDER_BACKUP_FILES": "c:\\temp\\RavenTestData\\backupfiles",
"AYANOVA_FOLDER_TEMPORARY_SERVER_FILES": "c:\\temp\\RavenTestData\\tempfiles", "AYANOVA_FOLDER_TEMPORARY_SERVER_FILES": "c:\\temp\\RavenTestData\\tempfiles",
"AYANOVA_SERVER_TEST_MODE": "true", "AYANOVA_SERVER_TEST_MODE": "false",
"AYANOVA_SERVER_TEST_MODE_SEEDLEVEL": "small", "AYANOVA_SERVER_TEST_MODE_SEEDLEVEL": "small",
"AYANOVA_SERVER_TEST_MODE_TZ_OFFSET": "-7", "AYANOVA_SERVER_TEST_MODE_TZ_OFFSET": "-7",
"AYANOVA_BACKUP_PG_DUMP_PATH": "C:\\data\\code\\postgres_13\\bin\\" "AYANOVA_BACKUP_PG_DUMP_PATH": "C:\\data\\code\\postgres_13\\bin\\"

View File

@@ -156,7 +156,7 @@ namespace AyaNova.Api.Controllers
/// <param name="vendorId">optional vendor id will return matches to Part objects manufacturer, wholesaler or alternative wholesaler</param> /// <param name="vendorId">optional vendor id will return matches to Part objects manufacturer, wholesaler or alternative wholesaler</param>
/// <returns>PurchaseOrder</returns> /// <returns>PurchaseOrder</returns>
[HttpGet("restock-by-vendor/{vendorId}")] [HttpGet("restock-by-vendor/{vendorId}")]
public async Task<IActionResult> GetPurchaseOrder([FromRoute] long? vendorId) public async Task<IActionResult> GetRestockByVendor([FromRoute] long? vendorId)
{ {
if (!serverState.IsOpen) if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));

View File

@@ -9,38 +9,14 @@ namespace AyaNova.DataList
{ {
public PartInventoryDataList() public PartInventoryDataList()
{ {
DefaultListAType = AyaType.PartInventory; DefaultListAType = AyaType.PartInventoryDataList;
SQLFrom = "from vpartinventorylist "; SQLFrom = "from vpartinventorylist ";
/*
"CREATE VIEW vpartinventorylist AS select apart.partnumber,apartwarehouse.name as whsname,vpartinventorynow.*,vpartsonorder.quantityonorder,"
+"vpartsonordercommitted.quantityonordercommitted,apart.name as prtname, apart.active, apart.cost, apartstocklevel.minimumquantity,apart.retail, aws.name AS whslrname, aaws.name AS altwhslrname, "
+"GREATEST( COALESCE(apartstocklevel.minimumquantity, 0) - (COALESCE(vpartinventorynow.balance, 0) + COALESCE(vpartsonorder.quantityonorder, 0) - "
+"COALESCE(vpartsonordercommitted.quantityonordercommitted, 0)) ,0) AS reorderquantity FROM vpartinventorynow LEFT JOIN vpartsonordercommitted ON "
+"vpartinventorynow.partid = vpartsonordercommitted.partid AND vpartinventorynow.partwarehouseid = vpartsonordercommitted.partwarehouseid "
+"LEFT JOIN vpartsonorder ON vpartinventorynow.partid = vpartsonorder.partid AND vpartinventorynow.partwarehouseid = vpartsonorder.partwarehouseid "
+"LEFT JOIN apart ON (vpartinventorynow.partid = apart.id) LEFT JOIN apartwarehouse ON (vpartinventorynow.partwarehouseid = apartwarehouse.id) "
+"left join avendor AS aws on (apart.wholesalerid = aws.id) left join avendor AS aaws on (apart.alternativewholesalerid = aaws.id) "
+"left join apartstocklevel on (apartstocklevel.partid = apart.id AND apartstocklevel.partwarehouseid = vpartinventorynow.partwarehouseid);"
await ExecQueryAsync("CREATE VIEW vpartinventorylist AS select vpartinventorynow.*, vpartsonordercommitted.quantityonordercommitted, vpartsonorder.quantityonorder from vpartinventorynow "
+ "left join vpartsonordercommitted on (vpartinventorynow.partid = vpartsonordercommitted.partid and vpartinventorynow.partwarehouseid = vpartsonordercommitted.partwarehouseid)"
+ "left join vpartsonorder on (vpartinventorynow.partid = vpartsonorder.partid and vpartinventorynow.partwarehouseid = vpartsonorder.partwarehouseid)");
*/
//NEEDED COLUMNS
/*
partid, partnumber, partname, partactive, partcost, partretail, partwarehouseid, partwarehousename,wholesalername,wholesalerid,altwholesalername,altwholesalerid,
onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty, vpartinventorynow.description, vpartinventorynow.id
*/
var RoleSet = BizRoles.GetRoleSet(DefaultListAType); var RoleSet = BizRoles.GetRoleSet(DefaultListAType);
AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change; AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change;
DefaultColumns = new List<string>() { "PartPartNumber", "PartWarehouseName", "PartInventoryBalance", "PartByWarehouseInventoryQuantityOnOrder", "PartByWarehouseInventoryQtyOnOrderCommitted" }; DefaultColumns = new List<string>() { "PartPartNumber", "PartWarehouseName", "PartInventoryBalance", "PartByWarehouseInventoryMinStockLevel","PartByWarehouseInventoryReorderQuantity","PartByWarehouseInventoryQuantityOnOrder", "PartByWarehouseInventoryQtyOnOrderCommitted", "Active" };
DefaultSortBy = new Dictionary<string, string>() { { "PartPartNumber", "+" }, { "PartWarehouseName", "+" } }; DefaultSortBy = new Dictionary<string, string>() { { "PartPartNumber", "+" }, { "PartWarehouseName", "+" } };
FieldDefinitions = new List<DataListFieldDefinition>(); FieldDefinitions = new List<DataListFieldDefinition>();
@@ -50,8 +26,8 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
FieldKey = "PartPartNumber", FieldKey = "PartPartNumber",
AType = (int)AyaType.Part, AType = (int)AyaType.Part,
UiFieldDataType = (int)UiFieldDataType.Text, UiFieldDataType = (int)UiFieldDataType.Text,
SqlIdColumnName = "apart.id", SqlIdColumnName = "partid",
SqlValueColumnName = "apart.partnumber" SqlValueColumnName = "partnumber"
}); });
FieldDefinitions.Add(new DataListFieldDefinition FieldDefinitions.Add(new DataListFieldDefinition
@@ -60,8 +36,52 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
FieldKey = "PartName", FieldKey = "PartName",
AType = (int)AyaType.Part, AType = (int)AyaType.Part,
UiFieldDataType = (int)UiFieldDataType.Text, UiFieldDataType = (int)UiFieldDataType.Text,
SqlIdColumnName = "apart.id", SqlIdColumnName = "partid",
SqlValueColumnName = "vpartinventorylist.vpartinventorylist" SqlValueColumnName = "partname"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "Active",
FieldKey = "Active",
UiFieldDataType = (int)UiFieldDataType.Bool,
SqlValueColumnName = "partactive"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "PartCost",
FieldKey = "PartCost",
UiFieldDataType = (int)UiFieldDataType.Currency,
SqlValueColumnName = "partcost"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "PartRetail",
FieldKey = "PartRetail",
UiFieldDataType = (int)UiFieldDataType.Currency,
SqlValueColumnName = "partretail"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
FieldKey = "PartWholesalerID",
TKey = "PartWholesalerID",
UiFieldDataType = (int)UiFieldDataType.Text,
AType = (int)AyaType.Vendor,
SqlIdColumnName = "wholesalerid",
SqlValueColumnName = "wholesalername"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
FieldKey = "PartAlternativeWholesalerID",
TKey = "PartAlternativeWholesalerID",
UiFieldDataType = (int)UiFieldDataType.Text,
AType = (int)AyaType.Vendor,
SqlIdColumnName = "altwholesalerid",
SqlValueColumnName = "altwholesalername"
}); });
FieldDefinitions.Add(new DataListFieldDefinition FieldDefinitions.Add(new DataListFieldDefinition
@@ -70,8 +90,8 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
FieldKey = "PartWarehouseName", FieldKey = "PartWarehouseName",
AType = (int)AyaType.PartWarehouse, AType = (int)AyaType.PartWarehouse,
UiFieldDataType = (int)UiFieldDataType.Text, UiFieldDataType = (int)UiFieldDataType.Text,
SqlIdColumnName = "apartwarehouse.id", SqlIdColumnName = "partwarehouseid",
SqlValueColumnName = "apartwarehouse.name" SqlValueColumnName = "partwarehousename"
}); });
FieldDefinitions.Add(new DataListFieldDefinition FieldDefinitions.Add(new DataListFieldDefinition
@@ -80,8 +100,8 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
FieldKey = "PartInventoryTransactionDescription", FieldKey = "PartInventoryTransactionDescription",
AType = (int)AyaType.PartInventory, AType = (int)AyaType.PartInventory,
UiFieldDataType = (int)UiFieldDataType.Text, UiFieldDataType = (int)UiFieldDataType.Text,
SqlIdColumnName = "vpartinventorylist.id",//NEW: vpartinventorynow.id is actually apartinventory.id required for reporting purposes SqlIdColumnName = "partinventoryid",
SqlValueColumnName = "vpartinventorylist.description",//NEW: vpartinventorynow.description which is actually apartinventory.description SqlValueColumnName = "partinventorydescription",
IsMeta = true,//only so it doesn't show in the UI but is required for report IsMeta = true,//only so it doesn't show in the UI but is required for report
IsRowId = true IsRowId = true
}); });
@@ -91,7 +111,7 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
TKey = "PartInventoryBalance", TKey = "PartInventoryBalance",
FieldKey = "PartInventoryBalance", FieldKey = "PartInventoryBalance",
UiFieldDataType = (int)UiFieldDataType.Decimal, UiFieldDataType = (int)UiFieldDataType.Decimal,
SqlValueColumnName = "vpartinventorylist.balance" SqlValueColumnName = "onhandqty"
}); });
@@ -100,7 +120,7 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
TKey = "PartByWarehouseInventoryQuantityOnOrder", TKey = "PartByWarehouseInventoryQuantityOnOrder",
FieldKey = "PartByWarehouseInventoryQuantityOnOrder", FieldKey = "PartByWarehouseInventoryQuantityOnOrder",
UiFieldDataType = (int)UiFieldDataType.Decimal, UiFieldDataType = (int)UiFieldDataType.Decimal,
SqlValueColumnName = "vpartinventorylist.quantityonorder" SqlValueColumnName = "onorderqty"
}); });
FieldDefinitions.Add(new DataListFieldDefinition FieldDefinitions.Add(new DataListFieldDefinition
@@ -108,7 +128,25 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
TKey = "PartByWarehouseInventoryQtyOnOrderCommitted", TKey = "PartByWarehouseInventoryQtyOnOrderCommitted",
FieldKey = "PartByWarehouseInventoryQtyOnOrderCommitted", FieldKey = "PartByWarehouseInventoryQtyOnOrderCommitted",
UiFieldDataType = (int)UiFieldDataType.Decimal, UiFieldDataType = (int)UiFieldDataType.Decimal,
SqlValueColumnName = "vpartinventorylist.quantityonordercommitted" SqlValueColumnName = "onordercommittedqty"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "PartByWarehouseInventoryMinStockLevel",
FieldKey = "PartByWarehouseInventoryMinStockLevel",
UiFieldDataType = (int)UiFieldDataType.Decimal,
SqlValueColumnName = "restockminqty"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "PartByWarehouseInventoryReorderQuantity",
FieldKey = "PartByWarehouseInventoryReorderQuantity",
UiFieldDataType = (int)UiFieldDataType.Decimal,
SqlValueColumnName = "reorderqty"
}); });
@@ -118,8 +156,8 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
FieldKey = "metapartnumber", FieldKey = "metapartnumber",
UiFieldDataType = (int)UiFieldDataType.Text, UiFieldDataType = (int)UiFieldDataType.Text,
SqlIdColumnName = "apart.id", SqlIdColumnName = "partid",
SqlValueColumnName = "apart.partnumber", SqlValueColumnName = "partnumber",
IsMeta = true IsMeta = true
}); });
@@ -128,8 +166,8 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
{ {
FieldKey = "metawarehouse", FieldKey = "metawarehouse",
UiFieldDataType = (int)UiFieldDataType.Text, UiFieldDataType = (int)UiFieldDataType.Text,
SqlIdColumnName = "apartwarehouse.id", SqlIdColumnName = "partwarehouseid",
SqlValueColumnName = "apartwarehouse.name", SqlValueColumnName = "partwarehousename",
IsMeta = true IsMeta = true
}); });

View File

@@ -136,7 +136,7 @@ namespace AyaNova.Biz
PartInventory = 67, PartInventory = 67,
DataListColumnView = 68, DataListColumnView = 68,
PartInventoryRestock = 69,//for list only, synthetic object PartInventoryRestock = 69,//for list only, synthetic object
PartInventoryRequest = 70,//for list only not, synthetic object PartInventoryRequest = 70,//for list only, synthetic object
WorkOrderStatus = 71, WorkOrderStatus = 71,
TaskGroup = 72, TaskGroup = 72,
WorkOrderItemOutsideService = 73, WorkOrderItemOutsideService = 73,
@@ -166,7 +166,8 @@ namespace AyaNova.Biz
PMItemTravel = 87, PMItemTravel = 87,
[CoreBizObject] [CoreBizObject]
PMItemUnit = 88, PMItemUnit = 88,
PMItemOutsideService = 89 PMItemOutsideService = 89,
PartInventoryDataList = 90//for list/reporting only, synthetic object

View File

@@ -61,6 +61,8 @@ namespace AyaNova.Biz
return new PartAssemblyBiz(ct, userId, translationId, roles); return new PartAssemblyBiz(ct, userId, translationId, roles);
case AyaType.PartInventory: case AyaType.PartInventory:
return new PartInventoryBiz(ct, userId, translationId, roles); return new PartInventoryBiz(ct, userId, translationId, roles);
case AyaType.PartInventoryDataList:
return new PartInventoryDataListBiz(ct, userId, translationId, roles);
case AyaType.Project: case AyaType.Project:

View File

@@ -241,6 +241,22 @@ namespace AyaNova.Biz
Select = AuthorizationRoles.All Select = AuthorizationRoles.All
}); });
/////////////////////////////////////////////////////////////////
//PartInventoryDataList
// same as PO
//
roles.Add(AyaType.PartInventoryDataList, new BizRoleSet()
{
Change = AuthorizationRoles.Inventory
| AuthorizationRoles.BizAdmin
| AuthorizationRoles.Accounting,
ReadFullRecord = AuthorizationRoles.Service
| AuthorizationRoles.InventoryRestricted
| AuthorizationRoles.BizAdminRestricted
| AuthorizationRoles.ServiceRestricted,
Select = AuthorizationRoles.All
});
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,106 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Models;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
namespace AyaNova.Biz
{
internal class PartInventoryDataListBiz : BizObject, IReportAbleObject, IExportAbleObject
{
internal PartInventoryDataListBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
{
ct = dbcontext;
UserId = currentUserId;
UserTranslationId = userTranslationId;
CurrentUserRoles = UserRoles;
BizType = AyaType.PartInventoryDataList;
}
internal static PartInventoryDataListBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
{
if (httpContext != null)
return new PartInventoryDataListBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
else
return new PartInventoryDataListBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdmin);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//REPORTING
//
public async Task<JArray> GetReportData(DataListSelectedRequest dataListSelectedRequest)
{
var idList = dataListSelectedRequest.SelectedRowIds;
JArray ReportData = new JArray();
while (idList.Any())
{
var batch = idList.Take(IReportAbleObject.REPORT_DATA_BATCH_SIZE);
idList = idList.Skip(IReportAbleObject.REPORT_DATA_BATCH_SIZE).ToArray();
//query for this batch, comes back in db natural order unfortunately
var batchResults = await ct.VPartInventoryList.AsNoTracking().Where(z => batch.Contains(z.Id)).ToArrayAsync();
//order the results back into original
var orderedList = from id in batch join z in batchResults on id equals z.Id select z;
//cache frequent viz data
var AyaTypesEnumList = await AyaNova.Api.Controllers.EnumListController.GetEnumList(
StringUtil.TrimTypeName(typeof(AyaType).ToString()),
UserTranslationId,
CurrentUserRoles);
using (var command = ct.Database.GetDbConnection().CreateCommand())
{
ct.Database.OpenConnection();
foreach (PartInventory w in orderedList)
{
await PopulateVizFields(w, AyaTypesEnumList, command);
var jo = JObject.FromObject(w);
if (!JsonUtil.JTokenIsNullOrEmpty(jo["CustomFields"]))
jo["CustomFields"] = JObject.Parse((string)jo["CustomFields"]);
ReportData.Add(jo);
}
}
}
return ReportData;
}
//populate viz fields from provided object
private async Task PopulateVizFields(PartInventory o, List<NameIdItem> ayaTypesEnumList, System.Data.Common.DbCommand cmd)
{
o.PartViz = await ct.Part.AsNoTracking().Where(x => x.Id == o.PartId).Select(x => x.PartNumber).FirstOrDefaultAsync();
o.PartWarehouseViz = await ct.PartWarehouse.AsNoTracking().Where(x => x.Id == o.PartWarehouseId).Select(x => x.Name).FirstOrDefaultAsync();
if (o.SourceType != null)
o.SourceTypeViz = ayaTypesEnumList.Where(x => x.Id == (long)o.SourceType).Select(x => x.Name).First();
if (o.SourceType != null && o.SourceId != null)
o.SourceViz = BizObjectNameFetcherDirect.Name((AyaType)o.SourceType, (long)o.SourceId, cmd);
}
////////////////////////////////////////////////////////////////////////////////////////////////
// IMPORT EXPORT
//
public async Task<JArray> GetExportData(DataListSelectedRequest dataListSelectedRequest)
{
//for now just re-use the report data code
//this may turn out to be the pattern for most biz object types but keeping it seperate allows for custom usage from time to time
return await GetReportData(dataListSelectedRequest);
}
/////////////////////////////////////////////////////////////////////
}//eoc
}//eons

View File

@@ -122,6 +122,7 @@ namespace AyaNova.Models
// public virtual DbSet<ServiceBank> ServiceBank { get; set; } // public virtual DbSet<ServiceBank> ServiceBank { get; set; }
public virtual DbSet<ViewRestockRequired> ViewRestockRequired { get; set; } public virtual DbSet<ViewRestockRequired> ViewRestockRequired { get; set; }
public virtual DbSet<VPartInventoryList> VPartInventoryList { get; set; }

View File

@@ -0,0 +1,40 @@
namespace AyaNova.Models
{
//Note this is how to define a View backed model with no key (id)
[Microsoft.EntityFrameworkCore.Keyless]
public class VPartInventoryList
{
public long PartId { get; set; }
public string PartNumber { get; set; }
public string PartName { get; set; }
public bool PartActive { get; set; }
public decimal PartCost { get; set; }
public decimal PartRetail { get; set; }
public long PartWarehouseId { get; set; }
public string PartWarehouseName { get; set; }
public long WholesalerId { get; set; }
public string WholesalerName { get; set; }
public long AltWholesalerId { get; set; }
public string AltWholesalerName { get; set; }
public decimal OnHandQty { get; set; }
public decimal OnOrderQty { get; set; }
public decimal OnOrderCommittedQty { get; set; }
public decimal RestockMinQty { get; set; }
public decimal ReOrderQty { get; set; }
public long PartInventoryId { get; set; }
public string PartInventoryDescription { get; set; }
}//eoc
}//eons
/*
vpartinventorylist AS select apart.id as partid, apart.partnumber, apart.name as partname, apart.active as partactive, apart.cost as partcost, apart.retail as partretail,"
+"apartwarehouse.id as partwarehouseid, apartwarehouse.name as partwarehousename, awholesaler.name as wholesalername, awholesaler.id as wholesalerid, "
+"aaltwholesaler.id as altwholesalerid, aaltwholesaler.name as altwholesalername, vpartinventorynow.balance as onhandqty,vpartsonorder.quantityonorder as onorderqty, "
+"vpartsonordercommitted.quantityonordercommitted as onordercommittedqty,apartstocklevel.minimumquantity as restockminqty, "
+"GREATEST( COALESCE(apartstocklevel.minimumquantity, 0) - (COALESCE(vpartinventorynow.balance, 0) + COALESCE(vpartsonorder.quantityonorder, 0) - COALESCE(vpartsonordercommitted.quantityonordercommitted, 0)) ,0) AS reorderqty,"
+"vpartinventorynow.id as partinventoryid, vpartinventorynow.description as partinventorydescription "
*/

View File

@@ -904,8 +904,8 @@ $BODY$ LANGUAGE PLPGSQL STABLE");
await ExecQueryAsync( await ExecQueryAsync(
"CREATE VIEW vpartinventorylist AS select apart.id as partid, apart.partnumber, apart.name as partname, apart.active as partactive, apart.cost as partcost, apart.retail as partretail," "CREATE VIEW vpartinventorylist AS select apart.id as partid, apart.partnumber, apart.name as partname, apart.active as partactive, apart.cost as partcost, apart.retail as partretail,"
+"apartwarehouse.id as partwarehouseid, apartwarehouse.name as partwarehousename, awholesaler.name as wholesalername, awholesaler.id as wholesalerid, " +"apartwarehouse.id as partwarehouseid, apartwarehouse.name as partwarehousename, awholesaler.name as wholesalername, awholesaler.id as wholesalerid, "
+"aaltwholesaler.id as altwholesalerid, aaltwholesaler.name as altwholesalername, vpartinventorynow.balance as onhandqty,vpartsonorder.quantityonorder as onorderqty, " +"aaltwholesaler.id as altwholesalerid, aaltwholesaler.name as altwholesalername, vpartinventorynow.balance as onhandqty,COALESCE(vpartsonorder.quantityonorder,0) as onorderqty, "
+"vpartsonordercommitted.quantityonordercommitted as onordercommittedqty,apartstocklevel.minimumquantity as restockminqty, " +"COALESCE(vpartsonordercommitted.quantityonordercommitted,0) as onordercommittedqty,COALESCE(apartstocklevel.minimumquantity,0) as restockminqty, "
+"GREATEST( COALESCE(apartstocklevel.minimumquantity, 0) - (COALESCE(vpartinventorynow.balance, 0) + COALESCE(vpartsonorder.quantityonorder, 0) - COALESCE(vpartsonordercommitted.quantityonordercommitted, 0)) ,0) AS reorderqty," +"GREATEST( COALESCE(apartstocklevel.minimumquantity, 0) - (COALESCE(vpartinventorynow.balance, 0) + COALESCE(vpartsonorder.quantityonorder, 0) - COALESCE(vpartsonordercommitted.quantityonordercommitted, 0)) ,0) AS reorderqty,"
+"vpartinventorynow.id as partinventoryid, vpartinventorynow.description as partinventorydescription " +"vpartinventorynow.id as partinventoryid, vpartinventorynow.description as partinventorydescription "
+"FROM vpartinventorynow LEFT JOIN vpartsonordercommitted ON " +"FROM vpartinventorynow LEFT JOIN vpartsonordercommitted ON "