Skip to content

Commit

Permalink
#89 use inventory snapshot for dashboard widgets
Browse files Browse the repository at this point in the history
* Hotfix 0.7.8 (#141)

* minor cleanup to prevent the assign identifier quartz job from throwing hundreds of "Duplicate key" errors if the pool of identifiers is small enough to cause lots of collisions

* removed unnecessarily verbose logging from inventory service

* Purchase Order UI changes to make it more consistent with other pages

* fixed #133: added ability to import order line items into an existing order; also added rollback feature

* fixed #50: Illegal attempt to associate a collection with two open sessions when receiving against purchase order

* Minor enhancements to make UI a little more consistent across entire app

* bumped app version to 0.7.8

* removed annoying inventory sampling report constraint

* fixed #89: made significant performances improvements to the dashboard

* Removed errant text that was committed by accident

* Hotfix/62 missing foreign key constraints (#145)

* added missing primary keys and foreign key constraints to several tables (fixed #62, #108)
* bumped app version to 0.7.9

* Fixed unit test for the show inventory report feature
  • Loading branch information
jmiranda committed May 10, 2016
1 parent a109567 commit 308cc42
Show file tree
Hide file tree
Showing 32 changed files with 1,944 additions and 616 deletions.
2 changes: 1 addition & 1 deletion application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ app.grails.version=1.3.9
app.name=openboxes
app.revisionNumber=11160
app.servlet.version=2.4
app.version=0.7.8
app.version=0.7.9
plugins.barcode4j=0.2.1
plugins.bubbling=2.1.4
plugins.clickstream=0.2.0
Expand Down
28 changes: 27 additions & 1 deletion grails-app/conf/spring/resources.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,33 @@ beans = {
dashboardCache(EhCacheFactoryBean) { bean ->
cacheManager = ref("springcacheCacheManager")
cacheName = "dashboardCache"
// these are just examples of properties you could set
eternal = false
diskPersistent = false
memoryStoreEvictionPolicy = "LRU"
timeToLive = 86400 // 1 day = 60 * 60 * 24
timeToIdle = 43200 // 12 hours = 60 * 60 * 12
}
dashboardTotalStockValueCache(EhCacheFactoryBean) { bean ->
cacheManager = ref("springcacheCacheManager")
cacheName = "dashboardTotalStockValueCache"
eternal = false
diskPersistent = false
memoryStoreEvictionPolicy = "LRU"
timeToLive = 86400 // 1 day = 60 * 60 * 24
timeToIdle = 43200 // 12 hours = 60 * 60 * 12
}
dashboardProductSummaryCache(EhCacheFactoryBean) { bean ->
cacheManager = ref("springcacheCacheManager")
cacheName = "dashboardProductSummaryCache"
eternal = false
diskPersistent = false
memoryStoreEvictionPolicy = "LRU"
timeToLive = 86400 // 1 day = 60 * 60 * 24
timeToIdle = 43200 // 12 hours = 60 * 60 * 12
}
dashboardGenericProductSummaryCache(EhCacheFactoryBean) { bean ->
cacheManager = ref("springcacheCacheManager")
cacheName = "dashboardGenericProductSummaryCache"
eternal = false
diskPersistent = false
memoryStoreEvictionPolicy = "LRU"
Expand Down
137 changes: 113 additions & 24 deletions grails-app/controllers/org/pih/warehouse/JsonController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -249,23 +249,51 @@ class JsonController {
}


// FIXME Remove - Only used for comparison
def getQuantityByProductMap = {
def location = Location.get(session?.warehouse?.id)
def quantityMap = inventoryService.getQuantityByProductMap(location.inventory)

render quantityMap as JSON
}


// FIXME Remove - Only used for compaison
def getQuantityByProductMap2 = {
def location = Location.get(session?.warehouse?.id)
def quantityMap = inventoryService.getCurrentInventory(location)

render quantityMap as JSON
}

def getQuantityByInventoryItem = {
def location = Location.get(session?.warehouse?.id)
def quantityMap = inventoryService.getQuantityForInventory(location.inventory)

quantityMap = quantityMap.sort()


render quantityMap as JSON
}


def getQuantityByInventoryItem2 = {
def location = Location.get(session?.warehouse?.id)
def quantityMap = inventoryService.getQuantityOnHandByInventoryItem(location)

quantityMap = quantityMap.sort()


render quantityMap as JSON
}



@Cacheable("dashboardCache")
def getDashboardAlerts = {
def location = Location.get(session?.warehouse?.id)
def dashboardAlerts = inventoryService.getDashboardAlerts(location)

//def expirationSummary = inventoryService.getExpirationSummary(location)
//expirationSummary.each { key, value ->
// dashboardAlerts[key] = value;
//}

//def totalStockSummary = inventoryService.getTotalStockValue(location)
//dashboardAlerts['totalStockValue'] = g.formatNumber(number: totalStockSummary.totalStockValue, type: 'currency', currencyCode: 'USD')
//dashboardAlerts['totalStockValue'] = totalStockSummary.totalStockValue
//dashboardAlerts['hitCount'] = totalStockSummary.hitCount
//dashboardAlerts['missCount'] = totalStockSummary.missCount
//dashboardAlerts['totalCount'] = totalStockSummary.totalCount

render dashboardAlerts as JSON
}

Expand All @@ -276,16 +304,47 @@ class JsonController {
render map as JSON
}

@Cacheable("dashboardCache")
//@Cacheable("dashboardTotalStockValueCache")
def getTotalStockValue = {
def location = Location.get(session?.warehouse?.id)
def result = inventoryService.getTotalStockValue(location)
def totalValue = g.formatNumber(number: result.totalStockValue)
def lastUpdated = inventoryService.getLastUpdatedInventorySnapshotDate()
lastUpdated = "Last updated " + prettytime.display([date: lastUpdated, showTime: true, capitalize: false]) + "."
def data = [
lastUpdated: lastUpdated,
anotherAttr: "anotherValue",
totalStockValue:result.totalStockValue,
hitCount: result.hitCount,
missCount: result.missCount,
totalCount: result.totalCount,
totalValue: totalValue]
render data as JSON
}

def map = [totalStockValue:result.totalStockValue, hitCount: result.hitCount, missCount: result.missCount, totalCount: result.totalCount]
def getStockValueByProduct = {
def location = Location.get(session?.warehouse?.id)
def result = inventoryService.getTotalStockValue(location)

def stockValueByProduct = []
result.stockValueByProduct.sort { it.value }.reverseEach { product, value ->

value = g.formatNumber(number: value, format: "#######.00")
stockValueByProduct << [id: product.id, productCode: product.productCode, productName: product.name, unitPrice: product.pricePerUnit, totalValue: value ]
}

def map = [aaData: stockValueByProduct]
render map as JSON
}


@CacheFlush("dashboardTotalStockValueCache")
def refreshTotalStockValue = {
render ([success:true] as JSON)
}



@Cacheable("dashboardCache")
def getReconditionedStockCount = {
def location = Location.get(params?.location?.id)
Expand Down Expand Up @@ -355,6 +414,34 @@ class JsonController {
}


def getInventorySnapshots = {

def location = Location.get(params?.location?.id)
def results = inventoryService.findInventorySnapshotByLocation(location)

def inStockCount = results.findAll { it.quantityOnHand > 0 && it.status == InventoryStatus.SUPPORTED }.size()
def lowStockCount = results.findAll { it.quantityOnHand > 0 && it.quantityOnHand <= it.minQuantity && it.status == InventoryStatus.SUPPORTED }.size()
def reoderStockCount = results.findAll { it.quantityOnHand > it.minQuantity && it.quantityOnHand <= it.reorderQuantity && it.status == InventoryStatus.SUPPORTED }.size()
def overStockCount = results.findAll { it.quantityOnHand > it.reorderQuantity && it.quantityOnHand <= it.maxQuantity && it.status == InventoryStatus.SUPPORTED }.size()
def stockOutCount = results.findAll { it.quantityOnHand <= 0 && it.status == InventoryStatus.SUPPORTED }.size()

def totalCount = results.size()


render ([
summary: [
totalCount: totalCount,
inStockCount: inStockCount,
lowStockCount: lowStockCount,
reoderStockCount: reoderStockCount,
overStockCount: overStockCount,
stockOutCount:stockOutCount
],
details: [results:results]
] as JSON)//results.collect { [productCode: it.productCode, quantityOnHand: it.quantityOnHand, ]}

}

def getQuantityOnHandMap = {
def startTime = System.currentTimeMillis()
//def location = Location.get(session?.warehouse?.id)
Expand All @@ -366,9 +453,10 @@ class JsonController {
}


Map<Product,Integer> getQuantityOnHand(Inventory inventory) {
return inventoryService.getQuantityByProductMap(inventory)
}
// FIXME Remove if not used
// Map<Product,Integer> getQuantityOnHand(Inventory inventory) {
// return inventoryService.getQuantityByProductMap(inventory)
// }


def findProductCodes = {
Expand Down Expand Up @@ -1260,39 +1348,39 @@ class JsonController {
if (numMonths >= 24) {
use(groovy.time.TimeCategory) {
numMonths.times { i ->
dates << (today - (i+1).months)
dates << (today - i.months)
}
}
format = "yyyy"
}
else if (numMonths > 12) {
use(groovy.time.TimeCategory) {
numMonths.times { i ->
dates << (today - (i+1).months)
dates << (today - i.months)
}
}
format = "MMM-yy"
}
else if (numMonths >= 6) {
use(groovy.time.TimeCategory) {
numMonths.times { i ->
dates << (today - (i+1).months)
dates << (today - i.months)
}
}
format = "MMM-yyyy"
format = "MMM-yy"
}
else if (numMonths >= 2) {
use(groovy.time.TimeCategory) {
(numMonths*4).times { i ->
dates << (today - (i+1).weeks)
dates << (today - i.weeks)
}
}
format = "'wk' w"
}
else {
use(groovy.time.TimeCategory) {
(numMonths*21).times { i ->
dates << (today - (i+1).days)
dates << (today - i.days)
}
}
format = "dd-MMM"
Expand All @@ -1319,6 +1407,8 @@ class JsonController {

// Get all inventory snapshots for the current product and location
//def inventorySnapshots = InventorySnapshot.findAllByProductAndLocation(product, location)

log.info "Inventory snapshots between " + dates[0] + " " + dates[dates.size()-1]
def inventorySnapshots = InventorySnapshot.createCriteria().list() {
eq("product", product)
eq("location", location)
Expand Down Expand Up @@ -1371,7 +1461,6 @@ class JsonController {
* Dashboard > Fast movers
*/
def getFastMovers = {
log.info "getRequisitionItems: " + params
def dateFormat = new SimpleDateFormat("MM/dd/yyyy")
def date = new Date()
if (params.date) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ class InventoryController {
def list = {
println "List " + params
def location = Location.get(session.warehouse.id)
def quantityMap = inventoryService.getQuantityByProductMap(location.inventory)
def quantityMap = inventoryService.getCurrentInventory(location)
def statusMap = inventoryService.getInventoryStatus(location)
println "QuantityMap: " + quantityMap
[quantityMap:quantityMap,statusMap: statusMap]
Expand Down Expand Up @@ -631,6 +631,21 @@ class InventoryController {
render (view: "list", model: [quantityMap:quantityMap, statusMap: statusMap])
}

def listHealthyStock = {
def location = Location.get(session.warehouse.id)
def quantityMap = inventoryService.getHealthyStock(location)
def statusMap = inventoryService.getInventoryStatus(location)
if (params.format == "csv") {
def filename = "Overstock - " + location.name + ".csv"
response.setHeader("Content-disposition", "attachment; filename='" + filename + "'")
render(contentType: "text/csv", text:getCsvForProductMap(quantityMap, statusMap))
return;
}

//[inventoryItems:lowStock, quantityMap:quantityMap]
render (view: "list", model: [quantityMap:quantityMap, statusMap: statusMap])
}


def listOverStock = {
def location = Location.get(session.warehouse.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,8 @@ class InventorySnapshotController {
def triggerCalculateQuantityOnHandJob = {
println "triggerCalculateQuantityOnHandJob: " + params

def results = CalculateQuantityJob.triggerNow([productId:params.product.id,locationId:params.location.id])
//def location = Location.get(params.location.id)
//def product = Product.get(params.product.id)
//def results = inventoryService.getTransactionDates(location, product)
def results = CalculateQuantityJob.triggerNow([productId:params.product.id,locationId:params.location.id,alltime:true])

println results
render ([started:true, results:results] as JSON)

}
Expand Down
Loading

0 comments on commit 308cc42

Please sign in to comment.