statistics.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. const _ = require('lodash')
  2. const { Terminal, ServiceCategory } = require('../database')
  3. const sequelize = require('../database/sequelize')
  4. const { sanitize } = require('@alancnet/material-framework/lib/util')
  5. const moment = require('moment-immutable')
  6. const get = async (req, res) => {
  7. const terminalIds = (await Terminal.findAll())
  8. .filter(loc => req.claims.TERMINAL_ALL_ACCESS || req.claims[`TERMINAL_${loc.key}_ACCESS`])
  9. .map(loc => loc.id)
  10. const date = req.params.date
  11. ? moment(req.params.date)
  12. : moment((await sequelize.query('select max(date) as date from workdays where laborCost > 0'))[0].date || moment.now())
  13. const [metricsOverTime, metadata] = await sequelize.query(`
  14. SELECT
  15. loc.key,
  16. wd.date,
  17. wd.laborCost,
  18. SUM(distinct svc.inbound) as inbound,
  19. cast(SUM(distinct wd.laborCost) as double) / SUM(distinct svc.inbound) as costPerCarton
  20. FROM workdays wd
  21. JOIN terminals loc on wd.terminalId = loc.id
  22. LEFT JOIN services svc on svc.workdayId = wd.id
  23. WHERE loc.id IN(:terminalIds)
  24. AND wd.date <= :date
  25. AND wd.date >= :startDate
  26. GROUP BY loc.key, wd.date
  27. `, {
  28. replacements: {
  29. terminalIds,
  30. date: date.format('YYYY-MM-DD'),
  31. startDate: date.add(-30, 'days').format('YYYY-MM-DD')
  32. }
  33. })
  34. const metricsOverTimeSql = (serviceColumn) => `
  35. select
  36. terminals.key,
  37. workdays.date,
  38. sum(distinct labors.laborCost) as laborCost,
  39. sum(distinct services.${serviceColumn}) as ${serviceColumn},
  40. cast(sum(distinct labors.laborCost) as double) / sum(distinct services.${serviceColumn}) as costPer
  41. -- Start with Terminals
  42. from terminals
  43. -- Link to labor cost
  44. join workdays on workdays.terminalId = terminals.id
  45. join services on services.workdayId = workdays.id
  46. join labors on labors.workdayId = workdays.id
  47. -- Link to LaborCategory
  48. join staffMembers on labors.staffMemberId = staffMembers.id
  49. join laborCategories on staffMembers.laborCategoryId = laborCategories.id
  50. -- Link to ServiceCategory
  51. join laborServiceCategories on laborServiceCategories.laborCategoryId = laborCategories.id
  52. join serviceCategories on laborServiceCategories.serviceCategoryId = serviceCategories.id
  53. where terminals.id in(:terminalIds)
  54. and serviceCategories.key = :serviceCategory
  55. and workdays.date <= :date
  56. and workdays.date >= :startDate
  57. group by terminals.key, workdays.date
  58. `
  59. const metricsSql = (serviceColumn) => `
  60. select
  61. terminals.key as terminal,
  62. serviceCategories.key as serviceCategory,
  63. serviceCategories.displayOrder,
  64. laborCategories.key as laborCategory,
  65. sum(distinct labors.laborCost) as laborCost,
  66. sum(distinct services.${serviceColumn}) as ${serviceColumn},
  67. cast(sum(distinct labors.laborCost) as double) / sum(distinct services.${serviceColumn}) as costPer
  68. -- Start with Terminals
  69. from terminals
  70. -- Link to labor cost
  71. join workdays on workdays.terminalId = terminals.id
  72. join services on services.workdayId = workdays.id
  73. join labors on labors.workdayId = workdays.id
  74. -- Link to LaborCategory
  75. join staffMembers on labors.staffMemberId = staffMembers.id
  76. join laborCategories on staffMembers.laborCategoryId = laborCategories.id
  77. -- Link to ServiceCategory
  78. join laborServiceCategories on laborServiceCategories.laborCategoryId = laborCategories.id
  79. join serviceCategories on laborServiceCategories.serviceCategoryId = serviceCategories.id
  80. where terminals.id in(:terminalIds)
  81. and workdays.date = :date
  82. and serviceCategories.key = :serviceCategory
  83. group by terminals.key, serviceCategories.key, laborCategories.key
  84. `
  85. const metricsPerServiceCategorySql = (serviceColumn) => `
  86. select
  87. serviceCategory,
  88. displayOrder,
  89. cast(sum(distinct laborCost) as double) / sum(distinct ${serviceColumn}) as costPer
  90. from (${metricsSql(serviceColumn)})
  91. group by serviceCategory
  92. order by displayOrder
  93. `
  94. const metricsPerLaborCategorySql = (serviceColumn) => `
  95. select
  96. serviceCategory,
  97. laborCategory,
  98. cast(sum(distinct laborCost) as double) / sum(distinct ${serviceColumn}) as costPer
  99. from (${metricsSql(serviceColumn)})
  100. group by laborCategory
  101. `
  102. const metricsPerTerminalAndServiceCategorySql = (serviceColumn) => `
  103. select
  104. terminal,
  105. serviceCategory,
  106. cast(sum(distinct laborCost) as double) / sum(distinct ${serviceColumn}) as costPer
  107. from (${metricsSql(serviceColumn)})
  108. group by terminal, serviceCategory
  109. `
  110. const metricsPerTerminalAndLaborCategorySql = (serviceColumn) => `
  111. select
  112. terminal,
  113. serviceCategory,
  114. laborCategory,
  115. cast(sum(distinct laborCost) as double) / sum(distinct ${serviceColumn}) as costPer
  116. from (${metricsSql(serviceColumn)})
  117. group by terminal, laborCategory
  118. `
  119. const lastMetricsSql = (serviceColumn) => `
  120. select
  121. workdays.date,
  122. terminals.key as terminal,
  123. sum(distinct labors.laborCost) as laborCost,
  124. sum(distinct services.${serviceColumn}) as ${serviceColumn},
  125. cast(sum(distinct labors.laborCost) as double) / sum(distinct services.${serviceColumn}) as costPer
  126. -- Start with Terminals
  127. from terminals
  128. -- Link to labor cost
  129. join workdays on workdays.terminalId = terminals.id
  130. join services on services.workdayId = workdays.id
  131. join labors on labors.workdayId = workdays.id
  132. -- Link to LaborCategory
  133. join staffMembers on labors.staffMemberId = staffMembers.id
  134. join laborCategories on staffMembers.laborCategoryId = laborCategories.id
  135. -- Link to ServiceCategory
  136. join laborServiceCategories on laborServiceCategories.laborCategoryId = laborCategories.id
  137. join serviceCategories on laborServiceCategories.serviceCategoryId = serviceCategories.id
  138. where workdays.date = :date
  139. and serviceCategories.key = :serviceCategory
  140. and terminals.id in(:terminalIds)
  141. `
  142. const serviceCategories = (await ServiceCategory.findAll())
  143. //const opts = {replacements: {serviceCategory: sc.key, terminalIds}}
  144. const ret = {
  145. date,
  146. serviceCategories: await Promise.all(
  147. (await ServiceCategory.findAll()).map(async sc => {
  148. const opts = {
  149. replacements: {
  150. terminalIds,
  151. serviceCategory: sc.key,
  152. date: date.format('YYYY-MM-DD'),
  153. startDate: date.add(-30, 'days').format('YYYY-MM-DD')
  154. }
  155. }
  156. const [mpsc] = await sequelize.query(metricsPerServiceCategorySql(sc.serviceColumn), opts)
  157. const [mplc] = await sequelize.query(metricsPerLaborCategorySql(sc.serviceColumn), opts)
  158. const [mptsc] = await sequelize.query(metricsPerTerminalAndServiceCategorySql(sc.serviceColumn), opts)
  159. const [mptlc] = await sequelize.query(metricsPerTerminalAndLaborCategorySql(sc.serviceColumn), opts)
  160. const [lastMetrics] = await sequelize.query(lastMetricsSql(sc.serviceColumn), opts)
  161. const [metricsOverTime] = await sequelize.query(metricsOverTimeSql(sc.serviceColumn), opts)
  162. return {
  163. ...mpsc[0],
  164. key: sc.key,
  165. serviceColumn: sc.serviceColumn,
  166. displayOrder: sc.displayOrder,
  167. laborCategories: mplc,
  168. terminals: mptsc.map(t => ({
  169. ...t,
  170. key: t.terminal,
  171. laborCategories: mptlc.filter(l => l.terminal === t.terminal)
  172. })),
  173. lastMetrics: lastMetrics[0],
  174. metricsOverTime
  175. }
  176. })
  177. ),
  178. metricsOverTime
  179. }
  180. return res.status(200).send(ret)
  181. const queries = await Promise.all(serviceCategories.map(async sc => ({
  182. metricsPerServiceCategory: await sanitize(req, (await sequelize.query(metricsPerServiceCategorySql(sc.serviceColumn), {replacements: {serviceCategory: sc.key, terminalIds}}))[0]),
  183. metricsPerLaborCategory: await sanitize(req, (await sequelize.query(metricsPerLaborCategorySql(sc.serviceColumn), {replacements: {serviceCategory: sc.key, terminalIds}}))[0]),
  184. metricsPerTerminalAndServiceCategory: await sanitize(req, (await sequelize.query(metricsPerTerminalAndServiceCategorySql(sc.serviceColumn), {replacements: {serviceCategory: sc.key, terminalIds}}))[0]),
  185. metricsPerTerminalAndLaborCategory: await sanitize(req, (await sequelize.query(metricsPerTerminalAndLaborCategorySql(sc.serviceColumn), {replacements: {serviceCategory: sc.key, terminalIds}}))[0]),
  186. lastMetrics: await sanitize(req, (await sequelize.query(lastMetricsSql(sc.serviceColumn), {replacements: {serviceCategory: sc.key, terminalIds}}))[0])
  187. })))
  188. const results = {
  189. serviceCategories: serviceCategories.map((sc, i) => Object.assign(queries[i].metricsPerServiceCategory[0], {
  190. laborCategories: queries[i].metricsPerLaborCategory.filter(mplc => mplc.serviceCategory === sc.key),
  191. terminals: queries[i].metricsPerTerminalAndServiceCategory.filter(mpt => mpt.serviceCategory === sc.key).map(mpt => Object.assign(mpt, {
  192. laborCategories: queries[i].metricsPerTerminalAndLaborCategory.filter(mptlc => mptlc.serviceCategory === sc.key && mptlc.terminal === mpt.terminal)
  193. })),
  194. lastMetrics: queries[i].lastMetrics,
  195. metricsOverTime: queries[i].metricsOverTime
  196. })),
  197. metricsOverTime
  198. }
  199. res.status(200).send(results)
  200. }
  201. module.exports = {
  202. get
  203. }