source: trunk/grails-app/services/InventoryReportService.groovy @ 649

Last change on this file since 649 was 649, checked in by gav, 14 years ago

Small improvement to Stock Take By Location report, sort locations found and at '_' pattern info text to report.

File size: 7.5 KB
Line 
1
2/**
3* Service class that encapsulates the business logic for Inventory Reports.
4*/
5class InventoryReportService {
6
7    boolean transactional = false
8
9//     def authService
10//     def dateUtilService
11//     def messageSource
12
13    def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
14
15    // Protect java heap memory.
16    // Most likely want to set paramsMax and inClauseMax to the same values.
17    def paramsMax = 250
18
19    // At least with Oracle and MSSQL db limits are 1000 (in list) and 2000 (nodes) respectively.
20    // But 255 has also been mentioned on the internet as a possible limit for some databases.
21    def inClauseMax = 250
22
23    /**
24    * Get the data for the inventory stock take overiew report.
25    * @param params The request params, may contain params to specify the search.
26    * @param locale The locale to use when generating result.message.
27    */
28    def getStockTakeOverview(params, locale) {
29        def result = [:]
30
31        result.summaryOfCalculationMethod = 'This report should be used in conjunction with the `Stock Take (By Location)` Report.'
32
33        def namedParams = [:]
34
35        result.query = "from InventoryLocation as inventoryLocation \
36                                        left join inventoryLocation.inventoryStore as inventoryStore \
37                                        where (inventoryLocation.isActive = true \
38                                                    ) \
39                                        order by inventoryStore.name, inventoryLocation.name"
40
41        result.query = "select new Map(inventoryLocation.name as location, inventoryStore.name as store) " + result.query
42        result.queryResult = InventoryLocation.executeQuery(result.query, namedParams)
43        result.inventoryLocationCount = result.queryResult.size()
44
45        result.inventoryLocationList = result.queryResult
46
47        // Success.
48        return result
49
50    } // getStockTakeOverview()
51
52    /**
53    * Get the data for the inventory stock take by location report.
54    * @param params The request params, may contain params to specify the search.
55    * @param locale The locale to use when generating result.message.
56    */
57    def getStockTakeByLocation(params, locale) {
58        def result = [:]
59
60        result.inventoryItemList = []
61        result.inventoryItemCount = 0
62        result.locationCount = 0
63        result.errorMessage = null
64        result.summaryOfCalculationMethod = 'This report should be used in conjunction with the `Stock Take (Overview)` Report.'
65
66        def fail = { Map m ->
67            result.error = [ code: m.code, args: m.args ]
68            result.errorMessage = g.message(result.error)
69            result.locations = ''
70            return result
71        }
72
73        def paginateParams = [:]
74        paginateParams.max = Math.min(params?.max?.toInteger() ?: paramsMax, paramsMax)
75
76        def namedParams = [:]
77        namedParams.locationList = []
78
79        // Sanitise the user supplied locations string and convert to a list.
80        result.locations = params.locationString.trim()
81        if(result.locations.startsWith('e.g:'))
82            result.locations = result.locations.split(':')[-1].trim()
83        result.locations = result.locations.split(',')
84        result.locations = result.locations.collect {it.trim()}
85
86        // Fill namedParams.locationList.
87        result.locations.each() { location ->
88            if(namedParams.locationList.size() < paramsMax) {
89                // paramsMax+1 to ensure the too many locations check bellow is triggered.
90                namedParams.locationList += InventoryLocation.findAllByNameIlike(location, [max: paramsMax+1])
91            }
92            namedParams.locationList.unique()
93        }
94
95        // Return the actual locations as a string, along with a count.
96        result.locationCount = namedParams.locationList.size()
97        if(result.locationCount > 0) {
98            namedParams.locationList.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }
99            result.locations = namedParams.locationList.toString()[1..-2]
100        }
101        else
102            result.locations = g.message(code: 'default.none.text')
103
104        // Exit if empty location list.
105        // Protects against HQL unexpected end of subtree exception with an empty list.
106        if(namedParams.locationList.isEmpty())
107            return fail(code:'report.error.no.locations.found')
108
109        // Exit if IN clause list too big.
110        if(namedParams.locationList.size() > inClauseMax)
111            return fail(code:'report.error.too.many.locations', args: [inClauseMax])
112
113        // Inventory List.
114        result.inventoryListQuery = "from InventoryItem as inventoryItem \
115                                                        left join inventoryItem.inventoryLocation as inventoryLocation \
116                                                        where (inventoryItem.isActive = true \
117                                                                    and  inventoryItem.inventoryLocation in (:locationList) \
118                                                                    ) "
119
120        result.inventoryCountQuery = "select count(distinct inventoryItem) " + result.inventoryListQuery
121        result.inventoryItemCount = InventoryItem.executeQuery(result.inventoryCountQuery, namedParams)[0]
122
123        // Exit if too many results.
124        if(result.inventoryItemCount > paramsMax) 
125            return fail(code:'report.error.too.many.results', args: [paramsMax])
126
127        result.inventoryListQuery = "select distinct inventoryItem " + result.inventoryListQuery
128        def inventoryList = InventoryItem.executeQuery(result.inventoryListQuery, namedParams, paginateParams)
129
130        // Reset namedParams for next query.
131        namedParams = [:]
132        namedParams.inventoryList = inventoryList
133
134        // Exit if empty inventory list.
135        // Protects against HQL unexpected end of subtree exception with an empty list.
136        if(namedParams.inventoryList.isEmpty())
137            return fail(code:'report.error.no.inventory.items.found')
138
139        // Exit if inventory list too big.
140        if(namedParams.inventoryList.size() > inClauseMax)
141            return fail(code:'report.error.too.many.inventory.items', args: [inClauseMax])
142
143        // Note: HQL docs advise 'not using fetch aliases in where clause (or any other clause)'.
144        // Access is via the parent object, however that does not work for the order by clause in this case.
145        result.query = "from InventoryItem as inventoryItem \
146                                        left join fetch inventoryItem.unitOfMeasure as unitOfMeasure \
147                                        left join fetch inventoryItem.inventoryLocation as inventoryLocation \
148                                        left join fetch inventoryLocation.inventoryStore as inventoryStore \
149                                        left join fetch inventoryItem.picture as picture \
150                                        left join fetch picture.images as Image \
151                                        where (inventoryItem in (:inventoryList) \
152                                                    ) \
153                                        order by inventoryStore.name, inventoryLocation.name"
154
155        // MSSQL will not do distinct here, for some reason it tries to compare the image data type!
156        result.query = "select inventoryItem " + result.query
157        result.inventoryItemList = InventoryItem.executeQuery(result.query, namedParams, paginateParams)
158
159        result.inventoryItemList.unique()
160
161        // Success.
162        return result
163
164    } // getStockTakeOverview()
165
166} // end class
Note: See TracBrowser for help on using the repository browser.