statistics.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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 get = async (req, res) => {
  6. const terminalIds = (await Terminal.findAll())
  7. .filter(loc => req.claims.TERMINAL_ALL_ACCESS || req.claims[`TERMINAL_${loc.key}_ACCESS`])
  8. .map(loc => loc.id)
  9. const [metricsOverTime, metadata] = await sequelize.query(`
  10. SELECT
  11. loc.key,
  12. wd.date,
  13. wd.laborCost,
  14. SUM(distinct svc.inbound) as inbound,
  15. cast(SUM(distinct wd.laborCost) as double) / SUM(distinct svc.inbound) as costPerCarton
  16. FROM workdays wd
  17. JOIN terminals loc on wd.terminalId = loc.id
  18. LEFT JOIN services svc on svc.workdayId = wd.id
  19. WHERE loc.id IN(:terminalIds)
  20. GROUP BY loc.key, wd.date
  21. `, {
  22. replacements: { terminalIds }
  23. })
  24. const metricsSql = (serviceColumn) => `
  25. select
  26. terminals.key as terminal,
  27. serviceCategories.key as serviceCategory,
  28. serviceCategories.displayOrder,
  29. laborCategories.key as laborCategory,
  30. sum(distinct labors.laborCost) as laborCost,
  31. sum(distinct services.${serviceColumn}) as ${serviceColumn},
  32. cast(sum(distinct labors.laborCost) as double) / sum(distinct services.${serviceColumn}) as costPer
  33. -- Start with Terminals
  34. from terminals
  35. -- Link to labor cost
  36. join workdays on workdays.terminalId = terminals.id
  37. join services on services.workdayId = workdays.id
  38. join labors on labors.workdayId = workdays.id
  39. -- Link to LaborCategory
  40. join staffMembers on labors.staffMemberId = staffMembers.id
  41. join laborCategories on staffMembers.laborCategoryId = laborCategories.id
  42. -- Link to ServiceCategory
  43. join laborServiceCategories on laborServiceCategories.laborCategoryId = laborCategories.id
  44. join serviceCategories on laborServiceCategories.serviceCategoryId = serviceCategories.id
  45. where terminals.id in(:terminalIds)
  46. and serviceCategories.key = :serviceCategory
  47. group by terminals.key, serviceCategories.key, laborCategories.key
  48. `
  49. const metricsPerServiceCategorySql = (serviceColumn) => `
  50. select
  51. serviceCategory,
  52. displayOrder,
  53. cast(sum(distinct laborCost) as double) / sum(distinct ${serviceColumn}) as costPer
  54. from (${metricsSql(serviceColumn)})
  55. group by serviceCategory
  56. order by displayOrder
  57. `
  58. const metricsPerLaborCategorySql = (serviceColumn) => `
  59. select
  60. serviceCategory,
  61. laborCategory,
  62. cast(sum(distinct laborCost) as double) / sum(distinct ${serviceColumn}) as costPer
  63. from (${metricsSql(serviceColumn)})
  64. group by laborCategory
  65. `
  66. const metricsPerTerminalAndServiceCategorySql = (serviceColumn) => `
  67. select
  68. terminal,
  69. serviceCategory,
  70. cast(sum(distinct laborCost) as double) / sum(distinct ${serviceColumn}) as costPer
  71. from (${metricsSql(serviceColumn)})
  72. group by terminal, serviceCategory
  73. `
  74. const metricsPerTerminalAndLaborCategorySql = (serviceColumn) => `
  75. select
  76. terminal,
  77. serviceCategory,
  78. laborCategory,
  79. cast(sum(distinct laborCost) as double) / sum(distinct ${serviceColumn}) as costPer
  80. from (${metricsSql(serviceColumn)})
  81. group by terminal, laborCategory
  82. `
  83. const lastMetricsSql = (serviceColumn) => `
  84. select
  85. workdays.date,
  86. terminals.key as terminal,
  87. sum(distinct labors.laborCost) as laborCost,
  88. sum(distinct services.${serviceColumn}) as ${serviceColumn},
  89. cast(sum(distinct labors.laborCost) as double) / sum(distinct services.${serviceColumn}) as costPer
  90. -- Start with Terminals
  91. from terminals
  92. -- Link to labor cost
  93. join workdays on workdays.terminalId = terminals.id
  94. join services on services.workdayId = workdays.id
  95. join labors on labors.workdayId = workdays.id
  96. -- Link to LaborCategory
  97. join staffMembers on labors.staffMemberId = staffMembers.id
  98. join laborCategories on staffMembers.laborCategoryId = laborCategories.id
  99. -- Link to ServiceCategory
  100. join laborServiceCategories on laborServiceCategories.laborCategoryId = laborCategories.id
  101. join serviceCategories on laborServiceCategories.serviceCategoryId = serviceCategories.id
  102. where workdays.date = (select max(date) from workdays where laborCost > 0)
  103. and serviceCategories.key = :serviceCategory
  104. and terminals.id in(:terminalIds)
  105. `
  106. const serviceCategories = (await ServiceCategory.findAll())
  107. //const opts = {replacements: {serviceCategory: sc.key, terminalIds}}
  108. const ret = {
  109. serviceCategories: await Promise.all(
  110. (await ServiceCategory.findAll()).map(async sc => {
  111. const opts = {
  112. replacements: {
  113. terminalIds,
  114. serviceCategory: sc.key
  115. }
  116. }
  117. const [mpsc] = await sequelize.query(metricsPerServiceCategorySql(sc.serviceColumn), opts)
  118. const [mplc] = await sequelize.query(metricsPerLaborCategorySql(sc.serviceColumn), opts)
  119. const [mptsc] = await sequelize.query(metricsPerTerminalAndServiceCategorySql(sc.serviceColumn), opts)
  120. const [mptlc] = await sequelize.query(metricsPerTerminalAndLaborCategorySql(sc.serviceColumn), opts)
  121. const [lastMetrics] = await sequelize.query(lastMetricsSql(sc.serviceColumn), opts)
  122. return {
  123. ...mpsc[0],
  124. key: sc.key,
  125. serviceColumn: sc.serviceColumn,
  126. displayOrder: sc.displayOrder,
  127. laborCategories: mplc,
  128. terminals: mptsc.map(t => ({
  129. ...t,
  130. key: t.terminal,
  131. laborCategories: mptlc.filter(l => l.terminal === t.terminal)
  132. })),
  133. lastMetrics: lastMetrics[0]
  134. }
  135. })
  136. ),
  137. metricsOverTime
  138. }
  139. return res.status(200).send(ret)
  140. const queries = await Promise.all(serviceCategories.map(async sc => ({
  141. metricsPerServiceCategory: await sanitize(req, (await sequelize.query(metricsPerServiceCategorySql(sc.serviceColumn), {replacements: {serviceCategory: sc.key, terminalIds}}))[0]),
  142. metricsPerLaborCategory: await sanitize(req, (await sequelize.query(metricsPerLaborCategorySql(sc.serviceColumn), {replacements: {serviceCategory: sc.key, terminalIds}}))[0]),
  143. metricsPerTerminalAndServiceCategory: await sanitize(req, (await sequelize.query(metricsPerTerminalAndServiceCategorySql(sc.serviceColumn), {replacements: {serviceCategory: sc.key, terminalIds}}))[0]),
  144. metricsPerTerminalAndLaborCategory: await sanitize(req, (await sequelize.query(metricsPerTerminalAndLaborCategorySql(sc.serviceColumn), {replacements: {serviceCategory: sc.key, terminalIds}}))[0]),
  145. lastMetrics: await sanitize(req, (await sequelize.query(lastMetricsSql(sc.serviceColumn), {replacements: {serviceCategory: sc.key, terminalIds}}))[0])
  146. })))
  147. const results = {
  148. serviceCategories: serviceCategories.map((sc, i) => Object.assign(queries[i].metricsPerServiceCategory[0], {
  149. laborCategories: queries[i].metricsPerLaborCategory.filter(mplc => mplc.serviceCategory === sc.key),
  150. terminals: queries[i].metricsPerTerminalAndServiceCategory.filter(mpt => mpt.serviceCategory === sc.key).map(mpt => Object.assign(mpt, {
  151. laborCategories: queries[i].metricsPerTerminalAndLaborCategory.filter(mptlc => mptlc.serviceCategory === sc.key && mptlc.terminal === mpt.terminal)
  152. })),
  153. lastMetrics: queries[i].lastMetrics
  154. })),
  155. metricsOverTime
  156. }
  157. res.status(200).send(results)
  158. }
  159. module.exports = {
  160. get
  161. }