feat(MemberOrders): implement member orders feature
- Added new models and routes for managing member orders and order history. - Updated server.js to include member order routes and sync functionality. - Enhanced frontend with new components and dialogs for viewing and managing orders. - Integrated internationalization support for order-related texts across multiple languages. - Updated navigation and views to include access to the new orders feature, improving user experience.
This commit is contained in:
55
backend/controllers/memberOrderController.js
Normal file
55
backend/controllers/memberOrderController.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import memberOrderService from '../services/memberOrderService.js';
|
||||
|
||||
const getMemberOrders = async (req, res) => {
|
||||
try {
|
||||
const { clubId, memberId } = req.params;
|
||||
const { authcode: userToken } = req.headers;
|
||||
const result = await memberOrderService.getMemberOrders(userToken, clubId, memberId);
|
||||
res.status(result.status).json(result.response);
|
||||
} catch (error) {
|
||||
console.error('[getMemberOrders] - Error:', error);
|
||||
res.status(500).json({ success: false, error: 'Bestellungen konnten nicht geladen werden.' });
|
||||
}
|
||||
};
|
||||
|
||||
const createMemberOrder = async (req, res) => {
|
||||
try {
|
||||
const { clubId, memberId } = req.params;
|
||||
const { authcode: userToken } = req.headers;
|
||||
const result = await memberOrderService.createMemberOrder(userToken, clubId, memberId, req.body || {});
|
||||
res.status(result.status).json(result.response);
|
||||
} catch (error) {
|
||||
console.error('[createMemberOrder] - Error:', error);
|
||||
res.status(500).json({ success: false, error: 'Bestellung konnte nicht gespeichert werden.' });
|
||||
}
|
||||
};
|
||||
|
||||
const updateMemberOrder = async (req, res) => {
|
||||
try {
|
||||
const { clubId, memberId, orderId } = req.params;
|
||||
const { authcode: userToken } = req.headers;
|
||||
const result = await memberOrderService.updateMemberOrder(userToken, clubId, memberId, orderId, req.body || {});
|
||||
res.status(result.status).json(result.response);
|
||||
} catch (error) {
|
||||
console.error('[updateMemberOrder] - Error:', error);
|
||||
res.status(500).json({ success: false, error: 'Bestellung konnte nicht aktualisiert werden.' });
|
||||
}
|
||||
};
|
||||
|
||||
const getGlobalOrders = async (req, res) => {
|
||||
try {
|
||||
const { authcode: userToken } = req.headers;
|
||||
const result = await memberOrderService.getGlobalOrders(userToken);
|
||||
res.status(result.status).json(result.response);
|
||||
} catch (error) {
|
||||
console.error('[getGlobalOrders] - Error:', error);
|
||||
res.status(500).json({ success: false, error: 'Bestellübersicht konnte nicht geladen werden.' });
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
getMemberOrders,
|
||||
createMemberOrder,
|
||||
updateMemberOrder,
|
||||
getGlobalOrders
|
||||
};
|
||||
36
backend/migrations/20260324_create_member_orders_tables.sql
Normal file
36
backend/migrations/20260324_create_member_orders_tables.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
CREATE TABLE IF NOT EXISTS member_orders (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
member_id INT NOT NULL,
|
||||
club_id INT NOT NULL,
|
||||
item VARCHAR(255) NOT NULL,
|
||||
status ENUM('requested', 'ordered', 'arrived', 'handed_over') NOT NULL DEFAULT 'requested',
|
||||
order_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
status_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
cost DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
paid_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_member_orders_member_id (member_id),
|
||||
KEY idx_member_orders_club_id (club_id),
|
||||
KEY idx_member_orders_status (status)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS member_order_history (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
member_order_id INT NOT NULL,
|
||||
member_id INT NOT NULL,
|
||||
club_id INT NOT NULL,
|
||||
item VARCHAR(255) NOT NULL,
|
||||
status ENUM('requested', 'ordered', 'arrived', 'handed_over') NOT NULL,
|
||||
changed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
cost DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
paid_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_member_order_history_order_id (member_order_id),
|
||||
KEY idx_member_order_history_member_id (member_id),
|
||||
KEY idx_member_order_history_club_id (club_id),
|
||||
KEY idx_member_order_history_changed_at (changed_at)
|
||||
);
|
||||
64
backend/models/MemberOrder.js
Normal file
64
backend/models/MemberOrder.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import { DataTypes } from 'sequelize';
|
||||
import sequelize from '../database.js';
|
||||
|
||||
const MemberOrder = sequelize.define('MemberOrder', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
allowNull: false
|
||||
},
|
||||
memberId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
field: 'member_id'
|
||||
},
|
||||
clubId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
field: 'club_id'
|
||||
},
|
||||
item: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('requested', 'ordered', 'arrived', 'handed_over'),
|
||||
allowNull: false,
|
||||
defaultValue: 'requested'
|
||||
},
|
||||
orderDate: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
field: 'order_date'
|
||||
},
|
||||
statusDate: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
field: 'status_date'
|
||||
},
|
||||
cost: {
|
||||
type: DataTypes.DECIMAL(10, 2),
|
||||
allowNull: false,
|
||||
defaultValue: 0
|
||||
},
|
||||
paidAmount: {
|
||||
type: DataTypes.DECIMAL(10, 2),
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
field: 'paid_amount'
|
||||
}
|
||||
}, {
|
||||
underscored: true,
|
||||
tableName: 'member_orders',
|
||||
timestamps: true,
|
||||
indexes: [
|
||||
{ fields: ['member_id'] },
|
||||
{ fields: ['club_id'] },
|
||||
{ fields: ['status'] }
|
||||
]
|
||||
});
|
||||
|
||||
export default MemberOrder;
|
||||
63
backend/models/MemberOrderHistory.js
Normal file
63
backend/models/MemberOrderHistory.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { DataTypes } from 'sequelize';
|
||||
import sequelize from '../database.js';
|
||||
|
||||
const MemberOrderHistory = sequelize.define('MemberOrderHistory', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
allowNull: false
|
||||
},
|
||||
memberOrderId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
field: 'member_order_id'
|
||||
},
|
||||
memberId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
field: 'member_id'
|
||||
},
|
||||
clubId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
field: 'club_id'
|
||||
},
|
||||
item: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('requested', 'ordered', 'arrived', 'handed_over'),
|
||||
allowNull: false
|
||||
},
|
||||
changedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
field: 'changed_at'
|
||||
},
|
||||
cost: {
|
||||
type: DataTypes.DECIMAL(10, 2),
|
||||
allowNull: false,
|
||||
defaultValue: 0
|
||||
},
|
||||
paidAmount: {
|
||||
type: DataTypes.DECIMAL(10, 2),
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
field: 'paid_amount'
|
||||
}
|
||||
}, {
|
||||
underscored: true,
|
||||
tableName: 'member_order_history',
|
||||
timestamps: true,
|
||||
indexes: [
|
||||
{ fields: ['member_order_id'] },
|
||||
{ fields: ['member_id'] },
|
||||
{ fields: ['club_id'] },
|
||||
{ fields: ['changed_at'] }
|
||||
]
|
||||
});
|
||||
|
||||
export default MemberOrderHistory;
|
||||
@@ -49,6 +49,8 @@ import MemberTransferConfig from './MemberTransferConfig.js';
|
||||
import MemberContact from './MemberContact.js';
|
||||
import MemberImage from './MemberImage.js';
|
||||
import MemberTtrHistory from './MemberTtrHistory.js';
|
||||
import MemberOrder from './MemberOrder.js';
|
||||
import MemberOrderHistory from './MemberOrderHistory.js';
|
||||
import TrainingGroup from './TrainingGroup.js';
|
||||
import MemberTrainingGroup from './MemberTrainingGroup.js';
|
||||
import ClubDisabledPresetGroup from './ClubDisabledPresetGroup.js';
|
||||
@@ -94,6 +96,13 @@ MemberNote.belongsTo(Member, { foreignKey: 'memberId' });
|
||||
Member.hasMany(MemberTtrHistory, { as: 'ttrHistoryEntries', foreignKey: 'memberId' });
|
||||
MemberTtrHistory.belongsTo(Member, { as: 'member', foreignKey: 'memberId' });
|
||||
|
||||
Member.hasMany(MemberOrder, { as: 'orders', foreignKey: 'memberId' });
|
||||
MemberOrder.belongsTo(Member, { as: 'member', foreignKey: 'memberId' });
|
||||
Club.hasMany(MemberOrder, { as: 'memberOrders', foreignKey: 'clubId' });
|
||||
MemberOrder.belongsTo(Club, { as: 'club', foreignKey: 'clubId' });
|
||||
MemberOrder.hasMany(MemberOrderHistory, { as: 'historyEntries', foreignKey: 'memberOrderId' });
|
||||
MemberOrderHistory.belongsTo(MemberOrder, { as: 'order', foreignKey: 'memberOrderId' });
|
||||
|
||||
DiaryDate.hasMany(DiaryNote, { as: 'diaryNotes', foreignKey: 'diaryDateId' });
|
||||
DiaryNote.belongsTo(DiaryDate, { foreignKey: 'diaryDateId' });
|
||||
|
||||
@@ -422,6 +431,8 @@ export {
|
||||
MemberContact,
|
||||
MemberImage,
|
||||
MemberTtrHistory,
|
||||
MemberOrder,
|
||||
MemberOrderHistory,
|
||||
TrainingGroup,
|
||||
MemberTrainingGroup,
|
||||
ClubDisabledPresetGroup,
|
||||
|
||||
18
backend/routes/memberOrderRoutes.js
Normal file
18
backend/routes/memberOrderRoutes.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import express from 'express';
|
||||
import { authenticate } from '../middleware/authMiddleware.js';
|
||||
import { authorize } from '../middleware/authorizationMiddleware.js';
|
||||
import {
|
||||
getMemberOrders,
|
||||
createMemberOrder,
|
||||
updateMemberOrder,
|
||||
getGlobalOrders
|
||||
} from '../controllers/memberOrderController.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/global', authenticate, getGlobalOrders);
|
||||
router.get('/:clubId/:memberId', authenticate, authorize('members', 'read'), getMemberOrders);
|
||||
router.post('/:clubId/:memberId', authenticate, authorize('members', 'write'), createMemberOrder);
|
||||
router.patch('/:clubId/:memberId/:orderId', authenticate, authorize('members', 'write'), updateMemberOrder);
|
||||
|
||||
export default router;
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
PredefinedActivity, PredefinedActivityImage, DiaryDateActivity, DiaryMemberActivity, Match, League, Team, ClubTeam, TeamDocument, Group,
|
||||
GroupActivity, Tournament, TournamentGroup, TournamentMatch, TournamentResult,
|
||||
TournamentMember, Accident, UserToken, OfficialTournament, OfficialCompetition, OfficialCompetitionMember, MyTischtennis, ClickTtAccount, MyTischtennisUpdateHistory, MyTischtennisFetchLog, ApiLog, MemberTransferConfig, MemberContact, MemberTtrHistory
|
||||
, MemberOrder, MemberOrderHistory
|
||||
} from './models/index.js';
|
||||
import authRoutes from './routes/authRoutes.js';
|
||||
import clubRoutes from './routes/clubRoutes.js';
|
||||
@@ -54,6 +55,7 @@ import clickTtHttpPageRoutes from './routes/clickTtHttpPageRoutes.js';
|
||||
import memberTransferConfigRoutes from './routes/memberTransferConfigRoutes.js';
|
||||
import trainingGroupRoutes from './routes/trainingGroupRoutes.js';
|
||||
import trainingTimeRoutes from './routes/trainingTimeRoutes.js';
|
||||
import memberOrderRoutes from './routes/memberOrderRoutes.js';
|
||||
import schedulerService from './services/schedulerService.js';
|
||||
import { requestLoggingMiddleware } from './middleware/requestLoggingMiddleware.js';
|
||||
import HttpError from './exceptions/HttpError.js';
|
||||
@@ -208,6 +210,7 @@ app.use('/api/clicktt', clickTtHttpPageRoutes);
|
||||
app.use('/api/member-transfer-config', memberTransferConfigRoutes);
|
||||
app.use('/api/training-groups', trainingGroupRoutes);
|
||||
app.use('/api/training-times', trainingTimeRoutes);
|
||||
app.use('/api/member-orders', memberOrderRoutes);
|
||||
|
||||
// Middleware für dynamischen kanonischen Tag (vor express.static)
|
||||
const setCanonicalTag = (req, res, next) => {
|
||||
@@ -443,6 +446,8 @@ app.use((err, req, res, next) => {
|
||||
await safeSync(MemberTransferConfig);
|
||||
await safeSync(MemberContact);
|
||||
await safeSync(MemberTtrHistory);
|
||||
await safeSync(MemberOrder);
|
||||
await safeSync(MemberOrderHistory);
|
||||
await safeSync(ClubTeam);
|
||||
await safeSync(TeamDocument);
|
||||
|
||||
|
||||
296
backend/services/memberOrderService.js
Normal file
296
backend/services/memberOrderService.js
Normal file
@@ -0,0 +1,296 @@
|
||||
import { Op } from 'sequelize';
|
||||
import Member from '../models/Member.js';
|
||||
import MemberOrder from '../models/MemberOrder.js';
|
||||
import MemberOrderHistory from '../models/MemberOrderHistory.js';
|
||||
import Club from '../models/Club.js';
|
||||
import UserClub from '../models/UserClub.js';
|
||||
import { checkAccess, getUserByToken } from '../utils/userUtils.js';
|
||||
|
||||
const ORDER_STATUSES = ['requested', 'ordered', 'arrived', 'handed_over'];
|
||||
|
||||
const normalizeAmount = (value) => {
|
||||
const parsed = Number.parseFloat(value ?? 0);
|
||||
if (!Number.isFinite(parsed)) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(0, Number(parsed.toFixed(2)));
|
||||
};
|
||||
|
||||
const serializeOrder = (order) => {
|
||||
const plain = typeof order.toJSON === 'function' ? order.toJSON() : { ...order };
|
||||
const cost = normalizeAmount(plain.cost);
|
||||
const paidAmount = normalizeAmount(plain.paidAmount);
|
||||
return {
|
||||
...plain,
|
||||
cost,
|
||||
paidAmount,
|
||||
openAmount: Math.max(0, Number((cost - paidAmount).toFixed(2)))
|
||||
};
|
||||
};
|
||||
|
||||
const serializeHistoryEntry = (entry) => {
|
||||
const plain = typeof entry.toJSON === 'function' ? entry.toJSON() : { ...entry };
|
||||
const cost = normalizeAmount(plain.cost);
|
||||
const paidAmount = normalizeAmount(plain.paidAmount);
|
||||
return {
|
||||
...plain,
|
||||
cost,
|
||||
paidAmount,
|
||||
openAmount: Math.max(0, Number((cost - paidAmount).toFixed(2)))
|
||||
};
|
||||
};
|
||||
|
||||
class MemberOrderService {
|
||||
async _ensureMemberAccess(userToken, clubId, memberId) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const member = await Member.findOne({
|
||||
where: {
|
||||
id: memberId,
|
||||
clubId
|
||||
}
|
||||
});
|
||||
if (!member) {
|
||||
throw new Error('Mitglied nicht gefunden');
|
||||
}
|
||||
return member;
|
||||
}
|
||||
|
||||
async _createHistorySnapshot(order, transaction = null) {
|
||||
return MemberOrderHistory.create({
|
||||
memberOrderId: order.id,
|
||||
memberId: order.memberId,
|
||||
clubId: order.clubId,
|
||||
item: order.item,
|
||||
status: order.status,
|
||||
changedAt: new Date(),
|
||||
cost: normalizeAmount(order.cost),
|
||||
paidAmount: normalizeAmount(order.paidAmount)
|
||||
}, transaction ? { transaction } : undefined);
|
||||
}
|
||||
|
||||
async getMemberOrders(userToken, clubId, memberId) {
|
||||
await this._ensureMemberAccess(userToken, clubId, memberId);
|
||||
const orders = await MemberOrder.findAll({
|
||||
where: { clubId, memberId },
|
||||
include: [
|
||||
{
|
||||
model: MemberOrderHistory,
|
||||
as: 'historyEntries',
|
||||
required: false
|
||||
}
|
||||
],
|
||||
order: [
|
||||
['createdAt', 'DESC'],
|
||||
[{ model: MemberOrderHistory, as: 'historyEntries' }, 'changedAt', 'DESC']
|
||||
]
|
||||
});
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
response: {
|
||||
success: true,
|
||||
orders: orders.map((order) => {
|
||||
const serialized = serializeOrder(order);
|
||||
serialized.historyEntries = (order.historyEntries || []).map(serializeHistoryEntry);
|
||||
return serialized;
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async createMemberOrder(userToken, clubId, memberId, payload) {
|
||||
await this._ensureMemberAccess(userToken, clubId, memberId);
|
||||
|
||||
const item = String(payload?.item || '').trim();
|
||||
const status = ORDER_STATUSES.includes(payload?.status) ? payload.status : 'requested';
|
||||
const cost = normalizeAmount(payload?.cost);
|
||||
const paidAmount = normalizeAmount(payload?.paidAmount);
|
||||
|
||||
if (!item) {
|
||||
return {
|
||||
status: 400,
|
||||
response: { success: false, error: 'Artikel fehlt' }
|
||||
};
|
||||
}
|
||||
|
||||
const order = await MemberOrder.create({
|
||||
memberId,
|
||||
clubId,
|
||||
item,
|
||||
status,
|
||||
orderDate: new Date(),
|
||||
statusDate: new Date(),
|
||||
cost,
|
||||
paidAmount
|
||||
});
|
||||
|
||||
await this._createHistorySnapshot(order);
|
||||
|
||||
const fresh = await MemberOrder.findByPk(order.id, {
|
||||
include: [
|
||||
{
|
||||
model: MemberOrderHistory,
|
||||
as: 'historyEntries',
|
||||
required: false,
|
||||
order: [['changedAt', 'DESC']]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const serialized = serializeOrder(fresh);
|
||||
serialized.historyEntries = (fresh.historyEntries || []).map(serializeHistoryEntry);
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
response: {
|
||||
success: true,
|
||||
order: serialized
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async updateMemberOrder(userToken, clubId, memberId, orderId, payload) {
|
||||
await this._ensureMemberAccess(userToken, clubId, memberId);
|
||||
const order = await MemberOrder.findOne({
|
||||
where: {
|
||||
id: orderId,
|
||||
clubId,
|
||||
memberId
|
||||
}
|
||||
});
|
||||
|
||||
if (!order) {
|
||||
return {
|
||||
status: 404,
|
||||
response: { success: false, error: 'Bestellung nicht gefunden' }
|
||||
};
|
||||
}
|
||||
|
||||
let changed = false;
|
||||
const nextItem = payload?.item != null ? String(payload.item).trim() : order.item;
|
||||
const nextStatus = payload?.status && ORDER_STATUSES.includes(payload.status) ? payload.status : order.status;
|
||||
const nextCost = payload?.cost != null ? normalizeAmount(payload.cost) : normalizeAmount(order.cost);
|
||||
const nextPaidAmount = payload?.paidAmount != null ? normalizeAmount(payload.paidAmount) : normalizeAmount(order.paidAmount);
|
||||
|
||||
if (nextItem && nextItem !== order.item) {
|
||||
order.item = nextItem;
|
||||
changed = true;
|
||||
}
|
||||
if (nextStatus !== order.status) {
|
||||
order.status = nextStatus;
|
||||
order.statusDate = new Date();
|
||||
changed = true;
|
||||
}
|
||||
if (nextCost !== normalizeAmount(order.cost)) {
|
||||
order.cost = nextCost;
|
||||
changed = true;
|
||||
}
|
||||
if (nextPaidAmount !== normalizeAmount(order.paidAmount)) {
|
||||
order.paidAmount = nextPaidAmount;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
const existing = await this.getMemberOrders(userToken, clubId, memberId);
|
||||
const current = existing.response.orders.find((entry) => String(entry.id) === String(orderId)) || null;
|
||||
return {
|
||||
status: 200,
|
||||
response: {
|
||||
success: true,
|
||||
order: current
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
await order.save();
|
||||
await this._createHistorySnapshot(order);
|
||||
|
||||
const updated = await MemberOrder.findByPk(order.id, {
|
||||
include: [
|
||||
{
|
||||
model: MemberOrderHistory,
|
||||
as: 'historyEntries',
|
||||
required: false
|
||||
}
|
||||
],
|
||||
order: [[{ model: MemberOrderHistory, as: 'historyEntries' }, 'changedAt', 'DESC']]
|
||||
});
|
||||
|
||||
const serialized = serializeOrder(updated);
|
||||
serialized.historyEntries = (updated.historyEntries || []).map(serializeHistoryEntry);
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
response: {
|
||||
success: true,
|
||||
order: serialized
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async getGlobalOrders(userToken) {
|
||||
const user = await getUserByToken(userToken);
|
||||
const userClubs = await UserClub.findAll({
|
||||
where: {
|
||||
userId: user.id,
|
||||
approved: true
|
||||
},
|
||||
attributes: ['clubId']
|
||||
});
|
||||
const clubIds = userClubs.map((entry) => entry.clubId).filter(Boolean);
|
||||
|
||||
if (!clubIds.length) {
|
||||
return {
|
||||
status: 200,
|
||||
response: {
|
||||
success: true,
|
||||
orders: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const orders = await MemberOrder.findAll({
|
||||
where: {
|
||||
clubId: {
|
||||
[Op.in]: clubIds
|
||||
}
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: Member,
|
||||
as: 'member',
|
||||
attributes: ['id', 'firstName', 'lastName']
|
||||
},
|
||||
{
|
||||
model: Club,
|
||||
as: 'club',
|
||||
attributes: ['id', 'name']
|
||||
},
|
||||
{
|
||||
model: MemberOrderHistory,
|
||||
as: 'historyEntries',
|
||||
required: false
|
||||
}
|
||||
],
|
||||
order: [
|
||||
['statusDate', 'DESC'],
|
||||
['createdAt', 'DESC'],
|
||||
[{ model: MemberOrderHistory, as: 'historyEntries' }, 'changedAt', 'DESC']
|
||||
]
|
||||
});
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
response: {
|
||||
success: true,
|
||||
orders: orders.map((order) => {
|
||||
const serialized = serializeOrder(order);
|
||||
serialized.historyEntries = (order.historyEntries || []).map(serializeHistoryEntry);
|
||||
return serialized;
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default new MemberOrderService();
|
||||
Reference in New Issue
Block a user