diff --git a/.vscode/launch.json b/.vscode/launch.json
index fcc80a75..b77f3b95 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -53,7 +53,7 @@
"AYANOVA_FOLDER_USER_FILES": "c:\\temp\\RavenTestData\\userfiles",
"AYANOVA_FOLDER_BACKUP_FILES": "c:\\temp\\RavenTestData\\backupfiles",
"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_TZ_OFFSET": "-7",
"AYANOVA_BACKUP_PG_DUMP_PATH": "C:\\data\\code\\postgres_13\\bin\\"
diff --git a/server/AyaNova/Controllers/PurchaseOrderController.cs b/server/AyaNova/Controllers/PurchaseOrderController.cs
index e15b0a97..8df8f943 100644
--- a/server/AyaNova/Controllers/PurchaseOrderController.cs
+++ b/server/AyaNova/Controllers/PurchaseOrderController.cs
@@ -156,7 +156,7 @@ namespace AyaNova.Api.Controllers
/// optional vendor id will return matches to Part objects manufacturer, wholesaler or alternative wholesaler
/// PurchaseOrder
[HttpGet("restock-by-vendor/{vendorId}")]
- public async Task GetPurchaseOrder([FromRoute] long? vendorId)
+ public async Task GetRestockByVendor([FromRoute] long? vendorId)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
diff --git a/server/AyaNova/DataList/PartInventoryDataList.cs b/server/AyaNova/DataList/PartInventoryDataList.cs
index c446046f..fa9f6673 100644
--- a/server/AyaNova/DataList/PartInventoryDataList.cs
+++ b/server/AyaNova/DataList/PartInventoryDataList.cs
@@ -9,38 +9,14 @@ namespace AyaNova.DataList
{
public PartInventoryDataList()
{
- DefaultListAType = AyaType.PartInventory;
+ DefaultListAType = AyaType.PartInventoryDataList;
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);
AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change;
- DefaultColumns = new List() { "PartPartNumber", "PartWarehouseName", "PartInventoryBalance", "PartByWarehouseInventoryQuantityOnOrder", "PartByWarehouseInventoryQtyOnOrderCommitted" };
+ DefaultColumns = new List() { "PartPartNumber", "PartWarehouseName", "PartInventoryBalance", "PartByWarehouseInventoryMinStockLevel","PartByWarehouseInventoryReorderQuantity","PartByWarehouseInventoryQuantityOnOrder", "PartByWarehouseInventoryQtyOnOrderCommitted", "Active" };
DefaultSortBy = new Dictionary() { { "PartPartNumber", "+" }, { "PartWarehouseName", "+" } };
FieldDefinitions = new List();
@@ -50,8 +26,8 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
FieldKey = "PartPartNumber",
AType = (int)AyaType.Part,
UiFieldDataType = (int)UiFieldDataType.Text,
- SqlIdColumnName = "apart.id",
- SqlValueColumnName = "apart.partnumber"
+ SqlIdColumnName = "partid",
+ SqlValueColumnName = "partnumber"
});
FieldDefinitions.Add(new DataListFieldDefinition
@@ -60,8 +36,52 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
FieldKey = "PartName",
AType = (int)AyaType.Part,
UiFieldDataType = (int)UiFieldDataType.Text,
- SqlIdColumnName = "apart.id",
- SqlValueColumnName = "vpartinventorylist.vpartinventorylist"
+ SqlIdColumnName = "partid",
+ 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
@@ -70,8 +90,8 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
FieldKey = "PartWarehouseName",
AType = (int)AyaType.PartWarehouse,
UiFieldDataType = (int)UiFieldDataType.Text,
- SqlIdColumnName = "apartwarehouse.id",
- SqlValueColumnName = "apartwarehouse.name"
+ SqlIdColumnName = "partwarehouseid",
+ SqlValueColumnName = "partwarehousename"
});
FieldDefinitions.Add(new DataListFieldDefinition
@@ -80,8 +100,8 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
FieldKey = "PartInventoryTransactionDescription",
AType = (int)AyaType.PartInventory,
UiFieldDataType = (int)UiFieldDataType.Text,
- SqlIdColumnName = "vpartinventorylist.id",//NEW: vpartinventorynow.id is actually apartinventory.id required for reporting purposes
- SqlValueColumnName = "vpartinventorylist.description",//NEW: vpartinventorynow.description which is actually apartinventory.description
+ SqlIdColumnName = "partinventoryid",
+ SqlValueColumnName = "partinventorydescription",
IsMeta = true,//only so it doesn't show in the UI but is required for report
IsRowId = true
});
@@ -91,7 +111,7 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
TKey = "PartInventoryBalance",
FieldKey = "PartInventoryBalance",
UiFieldDataType = (int)UiFieldDataType.Decimal,
- SqlValueColumnName = "vpartinventorylist.balance"
+ SqlValueColumnName = "onhandqty"
});
@@ -100,7 +120,7 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
TKey = "PartByWarehouseInventoryQuantityOnOrder",
FieldKey = "PartByWarehouseInventoryQuantityOnOrder",
UiFieldDataType = (int)UiFieldDataType.Decimal,
- SqlValueColumnName = "vpartinventorylist.quantityonorder"
+ SqlValueColumnName = "onorderqty"
});
FieldDefinitions.Add(new DataListFieldDefinition
@@ -108,7 +128,25 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
TKey = "PartByWarehouseInventoryQtyOnOrderCommitted",
FieldKey = "PartByWarehouseInventoryQtyOnOrderCommitted",
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",
UiFieldDataType = (int)UiFieldDataType.Text,
- SqlIdColumnName = "apart.id",
- SqlValueColumnName = "apart.partnumber",
+ SqlIdColumnName = "partid",
+ SqlValueColumnName = "partnumber",
IsMeta = true
});
@@ -128,8 +166,8 @@ onhandqty, onorderqty, onordercommittedqty, restocklevel/minimumqty, reorderqty,
{
FieldKey = "metawarehouse",
UiFieldDataType = (int)UiFieldDataType.Text,
- SqlIdColumnName = "apartwarehouse.id",
- SqlValueColumnName = "apartwarehouse.name",
+ SqlIdColumnName = "partwarehouseid",
+ SqlValueColumnName = "partwarehousename",
IsMeta = true
});
diff --git a/server/AyaNova/biz/AyaType.cs b/server/AyaNova/biz/AyaType.cs
index 8982eb27..6abe9860 100644
--- a/server/AyaNova/biz/AyaType.cs
+++ b/server/AyaNova/biz/AyaType.cs
@@ -136,7 +136,7 @@ namespace AyaNova.Biz
PartInventory = 67,
DataListColumnView = 68,
PartInventoryRestock = 69,//for list only, synthetic object
- PartInventoryRequest = 70,//for list only not, synthetic object
+ PartInventoryRequest = 70,//for list only, synthetic object
WorkOrderStatus = 71,
TaskGroup = 72,
WorkOrderItemOutsideService = 73,
@@ -166,7 +166,8 @@ namespace AyaNova.Biz
PMItemTravel = 87,
[CoreBizObject]
PMItemUnit = 88,
- PMItemOutsideService = 89
+ PMItemOutsideService = 89,
+ PartInventoryDataList = 90//for list/reporting only, synthetic object
diff --git a/server/AyaNova/biz/BizObjectFactory.cs b/server/AyaNova/biz/BizObjectFactory.cs
index 43f0ef6c..bd293099 100644
--- a/server/AyaNova/biz/BizObjectFactory.cs
+++ b/server/AyaNova/biz/BizObjectFactory.cs
@@ -61,6 +61,8 @@ namespace AyaNova.Biz
return new PartAssemblyBiz(ct, userId, translationId, roles);
case AyaType.PartInventory:
return new PartInventoryBiz(ct, userId, translationId, roles);
+ case AyaType.PartInventoryDataList:
+ return new PartInventoryDataListBiz(ct, userId, translationId, roles);
case AyaType.Project:
diff --git a/server/AyaNova/biz/BizRoles.cs b/server/AyaNova/biz/BizRoles.cs
index 9a04dc5c..fb3f5d12 100644
--- a/server/AyaNova/biz/BizRoles.cs
+++ b/server/AyaNova/biz/BizRoles.cs
@@ -241,6 +241,22 @@ namespace AyaNova.Biz
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
+ });
+
////////////////////////////////////////////////////////////
diff --git a/server/AyaNova/biz/PartInventoryDataListBiz.cs b/server/AyaNova/biz/PartInventoryDataListBiz.cs
new file mode 100644
index 00000000..9a1c20be
--- /dev/null
+++ b/server/AyaNova/biz/PartInventoryDataListBiz.cs
@@ -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 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 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 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
+
diff --git a/server/AyaNova/models/AyContext.cs b/server/AyaNova/models/AyContext.cs
index 06636b91..24a294df 100644
--- a/server/AyaNova/models/AyContext.cs
+++ b/server/AyaNova/models/AyContext.cs
@@ -122,6 +122,7 @@ namespace AyaNova.Models
// public virtual DbSet ServiceBank { get; set; }
public virtual DbSet ViewRestockRequired { get; set; }
+ public virtual DbSet VPartInventoryList { get; set; }
diff --git a/server/AyaNova/models/VPartInventoryList.cs b/server/AyaNova/models/VPartInventoryList.cs
new file mode 100644
index 00000000..7c21ac9a
--- /dev/null
+++ b/server/AyaNova/models/VPartInventoryList.cs
@@ -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 "
+*/
\ No newline at end of file
diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs
index 19fce594..a093f01d 100644
--- a/server/AyaNova/util/AySchema.cs
+++ b/server/AyaNova/util/AySchema.cs
@@ -904,8 +904,8 @@ $BODY$ LANGUAGE PLPGSQL STABLE");
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,"
+"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, "
+ +"aaltwholesaler.id as altwholesalerid, aaltwholesaler.name as altwholesalername, vpartinventorynow.balance as onhandqty,COALESCE(vpartsonorder.quantityonorder,0) as onorderqty, "
+ +"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,"
+"vpartinventorynow.id as partinventoryid, vpartinventorynow.description as partinventorydescription "
+"FROM vpartinventorynow LEFT JOIN vpartsonordercommitted ON "