| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- const _ = require('lodash')
- const moment = require('moment-immutable')
- const { getWeeks, formatDate, parseDate } = require('../dates')
- const { StaffMember, Workday, Terminal, Labor, sequelize } = require('../database')
- const { Op } = require('sequelize')
- const list = async (req, res) => {
- const terminalKey = req.params.terminal
- const terminal = await Terminal.findOne({where: {key: terminalKey}})
- if (!terminal) return res.status(404).end()
- const workdays = await Workday.findAll({where: { terminalId: terminal.id }})
- let workweeks = _.groupBy(workdays, d => formatDate(moment(d.date).startOf('week')))
-
- // fill workweeks
- getWeeks(10).forEach(week => {
- const w = formatDate(week)
- if (!workweeks[w]) workweeks[w] = []
- })
- // fill workdays
- workweeks = _.chain(workweeks)
- .toPairs()
- .map(([w, days]) => {
- days = _.chain(days)
- .map(d => [moment(d.date).weekday(), d])
- .fromPairs()
- .value()
-
- days = _.range(0, 7).map(d => days[d] || null)
- .map(day => day && {
- regularHours: day.regularHours,
- overtimeHours: day.overtimeHours,
- laborCost: day.laborCost
- })
- return {
- workweek: w,
- workdays: days
- }
- })
- .sortBy(x => x.workweek)
- .reverse()
- .value()
- res.status(200).send(workweeks)
- }
- const get = async (req, res) => {
- const terminalKey = req.params.terminal
- const terminal = await Terminal.findOne({where: {key: terminalKey}})
- const week = parseDate(req.params.week)
- const workdays = await Workday.findAll({
- where: {
- terminalId: terminal.id,
- date: {
- [Op.gte]: week,
- [Op.lte]: moment(week).endOf('week')
- }
- },
- order: [ 'date' ]
- })
- const labor = await Labor.findAll({
- where: {
- workdayId: {
- [Op.in]: workdays.map(x => x.id)
- }
- }
- })
- const laborByWorkday = _.groupBy(labor, x => x.workdayId)
- const extraStaffMembers = _.chain(labor)
- .map(x => x.staffMemberId)
- .filter(x => x)
- .uniq()
- .value()
- const staffMembers = await StaffMember.findAll({
- where: {
- [Op.or]: [
- { terminalId: terminal.id },
- {
- id: {
- [Op.in]: extraStaffMembers
- }
- }
- ]
- },
- order: [ 'name' ]
- })
- const staffMembersById = _.chain(staffMembers).map(x => [x.id, x]).fromPairs().value()
- // Fill in empty days
- const workdaysByKey = _.chain(workdays).map(wd => [formatDate(wd.date), wd]).fromPairs().value()
- const allWorkdays = []
- for (let day = week, i = 0; i < 7; i++, day = day.add(1, 'day')) {
- const wd = workdaysByKey[formatDate(day)]
- allWorkdays[i] = wd ? wd.toJSON() : {}
- }
- const workdaysWithLabor = allWorkdays.map(wd => {
- const labor = laborByWorkday[wd.id] || []
- const laborByStaffMember = _.chain(labor).map(l => [l.staffMemberId, l]).fromPairs().value()
- // Map from staffMembers to preserve sorting
- wd.labor = staffMembers
- .map(sm => laborByStaffMember[sm.id] || {staffMemberId: sm.id})
- // Labor requires regularHours and overtimeHours, model requires hours
- .map(sm => ({
- staffMemberId: sm.staffMemberId,
- hours: (sm.regularHours + sm.overtimeHours) || null
- }))
- // Restore any staffMembers that are no longer assigned this terminal
- labor.forEach(l => {
- if (!staffMembersById[l.staffMemberId]) {
- wd.labor.push(l)
- }
- })
- return wd
- })
- res.status(200).send({
- workdays: allWorkdays
- })
- }
- const patch = async (req, res) => {
- const transaction = await sequelize.transaction()
- try {
- const terminalKey = req.params.terminal
- const terminal = await Terminal.findOne({where: {key: terminalKey}})
- const week = parseDate(req.params.week)
- const workdays = await Workday.findAll({
- where: {
- terminalId: terminal.id,
- date: {
- [Op.gte]: week,
- [Op.lt]: moment(week).endOf('week')
- }
- },
- order: [ 'date' ]
- })
- const labor = await Labor.findAll({
- where: {
- workdayId: {
- [Op.in]: workdays.map(x => x.id)
- }
- }
- })
- const laborByWorkday = _.groupBy(labor, x => x.workdayId)
- const extraStaffMembers = _.chain(labor)
- .map(x => x.staffMemberId)
- .filter(x => x)
- .uniq()
- .value()
- const staffMembers = await StaffMember.findAll({
- where: {
- [Op.or]: [
- { terminalId: terminal.id },
- {
- id: {
- [Op.in]: extraStaffMembers
- }
- }
- ]
- },
- order: [ 'name' ]
- })
- const staffMembersById = _.chain(staffMembers).map(x => [x.id, x]).fromPairs().value()
- // Fill in empty days
- const workdaysByKey = _.chain(workdays).map(wd => [formatDate(wd.date), wd]).fromPairs().value()
- const allWorkdays = []
- for (let day = week, i = 0; i < 7; i++, day = day.add(1, 'day')) {
- const wd = workdaysByKey[formatDate(day)]
- allWorkdays[i] = wd || Workday.build({
- terminalId: terminal.id,
- date: week.add(i, 'day')
- })
- }
- const workdaysWithLabor = allWorkdays.map(wd => {
- const labor = laborByWorkday[wd.id] || []
- const laborByStaffMember = _.chain(labor).map(l => [l.staffMemberId, l]).fromPairs().value()
- // Map from staffMembers to preserve sorting
- wd.labor = staffMembers.map(sm => laborByStaffMember[sm.id] || Labor.build({staffMemberId: sm.id, workdayId: wd.id}))
- // Restore any staffMembers that are no longer assigned this terminal
- labor.forEach(l => {
- if (!staffMembersById[l.staffMemberId]) {
- wd.labor.push(l)
- }
- })
- return wd
- })
- // Update with model
- const model = req.body
- await Promise.all(model.workdays.map(async (modelWorkday, i) => {
- const modelLaborById = _.chain(modelWorkday.labor).map(l => [l.staffMemberId, l]).fromPairs().value()
- const workday = allWorkdays[i]
- workday.labor.forEach(labor => {
- const modelLabor = modelLaborById[labor.staffMemberId]
- // modelLabor has hours, labor needs regularHours and overtimeHours
- labor.regularHours = Math.min(8, modelLabor.hours) || 0
- labor.overtimeHours = Math.max(0, modelLabor.hours - 8) || 0
- })
- // TODO: Update laborCost when wages are in
- workday.overtimeHours = workday.labor.map(l => l.overtimeHours || 0).reduce((a, b) => a + b, 0)
- workday.regularHours = workday.labor.map(l => l.regularHours || 0).reduce((a, b) => a + b, 0)
- workday.laborCost = workday.labor.map(l => {
- const staffMember = staffMembersById[l.staffMemberId]
- const wage = staffMember.wage || 0
- return ((l.regularHours || 0) * wage) + ((l.overtimeHours || 0) * wage * 1.5)
- }).reduce((a, b) => a + b, 0)
-
- if (workday.overtimeHours || workday.regularHours || !workday.isNewRecord) {
- await workday.save({ transaction })
- }
- }))
- await Promise.all(workday.labor.map(async labor => {
- if (labor.regularHours || labor.overtimeHours || !labor.isNewRecord) {
- await labor.save({ transaction })
- }
- }))
- await transaction.commit()
-
- res.status(200).end()
- } catch (err) {
- await transaction.rollback()
- throw err
- }
- }
- module.exports = {
- list,
- get,
- patch
- }
|