feat: use stubs for orders data (#64)
This commit is contained in:
		
							parent
							
								
									d88e680413
								
							
						
					
					
						commit
						87c1cbb19e
					
				| @ -1,9 +1,13 @@ | ||||
| const router = require('express').Router() | ||||
| const { OrderModel } = require('./model/order') | ||||
| 
 | ||||
| router.post('/orders', (req, res) => { | ||||
|     res | ||||
|         .status(200) | ||||
|         .send(require(`./json/arm-orders/success.json`)) | ||||
| router.get('/orders', async (req, res, next) => { | ||||
|     try { | ||||
|         const orders = await OrderModel.find({}) | ||||
|         res.status(200).send({ success: true, body: orders }) | ||||
|     } catch (error) { | ||||
|         next(error) | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| module.exports = router | ||||
| module.exports = router | ||||
| @ -1,10 +1,12 @@ | ||||
| const router = require('express').Router() | ||||
| const armMasterRouter  = require('./arm-master') | ||||
| const armOrdersRouter  = require('./arm-orders') | ||||
| const orderRouter  = require('./order') | ||||
| 
 | ||||
| 
 | ||||
| router.use('/arm', armMasterRouter) | ||||
| router.use('/arm', armOrdersRouter) | ||||
| router.use('/order', orderRouter) | ||||
| 
 | ||||
| 
 | ||||
| module.exports = router | ||||
|  | ||||
| @ -2,24 +2,59 @@ | ||||
|   "success": true, | ||||
|   "body": [ | ||||
|     { | ||||
|       "id": "order1", | ||||
|       "carNumber": "A123BC", | ||||
|       "startWashTime": "2024-11-24T10:30:00.000Z", | ||||
|       "endWashTime": "2024-11-24T16:30:00.000Z", | ||||
|       "orderDate": "2024-11-24T08:41:46.366Z", | ||||
|       "phone": "+79876543210", | ||||
|       "carNumber": "А123ВЕ16", | ||||
|       "carBody": 1, | ||||
|       "carColor": "#ffff00", | ||||
|       "startWashTime": "2025-05-12T08:21:00.000Z", | ||||
|       "endWashTime": "2025-05-12T08:22:00.000Z", | ||||
|       "location": "55.792799704829854,49.11034340707925 Республика Татарстан (Татарстан), Казань, улица Чернышевского", | ||||
|       "status": "progress", | ||||
|       "phone": "79001234563", | ||||
|       "location": "Казань, ул. Баумана, 1" | ||||
|       "notes": "", | ||||
|       "created": "2025-01-18T17:43:21.488Z", | ||||
|       "updated": "2025-01-18T17:43:21.492Z" | ||||
|     }, | ||||
|     { | ||||
|       "id": "order2", | ||||
|       "carNumber": "A245BC", | ||||
|       "startWashTime": "2024-11-24T11:30:00.000Z", | ||||
|       "endWashTime": "2024-11-24T17:30:00.000Z", | ||||
|       "orderDate": "2024-11-24T07:40:46.366Z", | ||||
|       "phone": "89876543210", | ||||
|       "carNumber": "К456МН23", | ||||
|       "carBody": 2, | ||||
|       "carColor": "#ffffff", | ||||
|       "startWashTime": "2025-01-12T08:21:00Z", | ||||
|       "endWashTime": "2025-01-12T08:22:00Z", | ||||
|       "location": "55.808430668108585,49.198608125449255 Республика Татарстан (Татарстан), Казань, улица Академика Губкина, 50/1", | ||||
|       "status": "pending", | ||||
|       "notes": "заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки", | ||||
|       "created": "2025-01-18T17:46:10.388Z", | ||||
|       "updated": "2025-01-18T17:46:10.395Z", | ||||
|       "id": "678be8e211e62f4a61790cca" | ||||
|     }, | ||||
|     { | ||||
|       "phone": "4098765432105", | ||||
|       "carNumber": "О789РС777", | ||||
|       "carBody": 3, | ||||
|       "carColor": "красный", | ||||
|       "startWashTime": "2025-08-12T08:21:00.000Z", | ||||
|       "endWashTime": "2025-08-12T08:22:00.000Z", | ||||
|       "location": "55.78720449830353,49.12111640202319 Республика Татарстан (Татарстан), Казань, улица Пушкина, 5/43", | ||||
|       "status": "cancelled", | ||||
|       "notes": "Заказ отменен по запросу самого клиента", | ||||
|       "created": "2025-01-18T17:47:46.294Z", | ||||
|       "updated": "2025-01-18T17:47:46.295Z", | ||||
|       "id": "678be8e211e62f4a61790ccb" | ||||
|     }, | ||||
|     { | ||||
|       "phone": "+79876543210", | ||||
|       "carNumber": "Т123УХ716", | ||||
|       "carBody": 99, | ||||
|       "carColor": "чайная роза", | ||||
|       "startWashTime": "2025-01-11T11:21:00.000Z", | ||||
|       "endWashTime": "2025-01-12T11:22:00.000Z", | ||||
|       "location": "55.77063673480112,49.22182909159608 Республика Татарстан (Татарстан), Казань, Советский район, микрорайон Азино-2", | ||||
|       "status": "progress", | ||||
|       "phone": "79001234567", | ||||
|       "location": "Казань, ул. Баумана, 43" | ||||
|       "notes": "Клиент остался доволен, предложить в следующий раз акцию", | ||||
|       "created": "2025-01-18T17:55:05.691Z", | ||||
|       "updated": "2025-01-18T17:55:05.695Z", | ||||
|       "id": "678be8e211e62f4a61790ccc" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										11
									
								
								server/routers/dry-wash/model/const.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								server/routers/dry-wash/model/const.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| const orderStatus = { | ||||
|   CANCELLED: 'cancelled', | ||||
|   PROGRESS: 'progress', | ||||
|   PENDING: 'pending', | ||||
|   WORKING: 'working', | ||||
|   COMPLETE: 'complete', | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
|   orderStatus | ||||
| } | ||||
| @ -1,26 +1,58 @@ | ||||
| const { Schema, model } = require('mongoose') | ||||
| const { orderStatus } = require('./const') | ||||
| 
 | ||||
| const schema = new Schema({ | ||||
|     startWashTime: {type: String, required: true}, | ||||
|     endWashTime: {type: String, required: true}, | ||||
|     orderDate: {type: String, required: true}, | ||||
|     location: {type: String, required: true}, | ||||
|     phone: {type: String, required: true}, | ||||
|     status: {type: String, required: true}, | ||||
|     carNumber: {type: String, required: true}, | ||||
|     phone: { | ||||
|         type: String, | ||||
|         required: true | ||||
|     }, | ||||
|     carNumber: { | ||||
|         type: String, | ||||
|         required: true | ||||
|     }, | ||||
|     carBody: { | ||||
|         type: Number, | ||||
|         required: true | ||||
|     }, | ||||
|     carColor: String, | ||||
|     startWashTime: { | ||||
|         type: Date, | ||||
|         required: true | ||||
|     }, | ||||
|     endWashTime: { | ||||
|         type: Date, | ||||
|         required: true | ||||
|     }, | ||||
|     location: { | ||||
|         type: String, | ||||
|         required: true | ||||
|     }, | ||||
|     status: { | ||||
|         type: String, | ||||
|         required: true, | ||||
|         enum: Object.values(orderStatus) | ||||
|     }, | ||||
|     master: { | ||||
|         type: Schema.Types.ObjectId, | ||||
|         ref: 'dry-wash-master' | ||||
|     }, | ||||
|     notes: String, | ||||
|     created: { | ||||
|         type: Date, default: () => new Date().toISOString(), | ||||
|         type: Date, | ||||
|         default: () => new Date().toISOString(), | ||||
|     }, | ||||
|     updated: { | ||||
|         type: Date, default: () => new Date().toISOString(), | ||||
|         type: Date, | ||||
|         default: () => new Date().toISOString(), | ||||
|     }, | ||||
|     master: {type: Schema.Types.ObjectId, ref: 'dry-wash-master'}, | ||||
|     notes:  String, | ||||
| }) | ||||
| 
 | ||||
| schema.set('toJSON', { | ||||
|     virtuals: true, | ||||
|     versionKey: false, | ||||
|     transform(_doc, ret) { | ||||
|         delete ret._id | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| schema.virtual('id').get(function () { | ||||
|  | ||||
							
								
								
									
										251
									
								
								server/routers/dry-wash/order.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								server/routers/dry-wash/order.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,251 @@ | ||||
| const mongoose = require("mongoose") | ||||
| const router = require('express').Router() | ||||
| const { MasterModel } = require('./model/master') | ||||
| const { OrderModel } = require('./model/order') | ||||
| const { orderStatus } = require('./model/const') | ||||
| 
 | ||||
| const isValidPhoneNumber = (value) => /^(\+)?\d{9,15}/.test(value) | ||||
| const isValidCarNumber = (value) => /^[авекмнорстух][0-9]{3}[авекмнорстух]{2}[0-9]{2,3}$/i.test(value) | ||||
| const isValidCarBodyType = (value) => typeof value === 'number' && value > 0 && value < 100 | ||||
| const isValidCarColor = (value) => value.length < 50 && /^[#a-z0-9а-я-\s,.()]+$/i.test(value) | ||||
| const isValidISODate = (value) => /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:.\d{1,3})?Z$/.test(value) | ||||
| 
 | ||||
| const latitudeRe = /^(-?[1-8]?\d(?:\.\d{1,18})?|90(?:\.0{1,18})?)$/ | ||||
| const longitudeRe = /^(-?(?:1[0-7]|[1-9])?\d(?:\.\d{1,18})?|180(?:\.0{1,18})?)$/ | ||||
| const addressRe = /^[а-я0-9\s,.'-/()]*$/i | ||||
| const isValidLocation = (value) => { | ||||
|     if (value.length > 200) { | ||||
|         return false | ||||
|     } | ||||
| 
 | ||||
|     const [coordinates, address] = value.split(' ') | ||||
|     const [latitude, longitude] = coordinates.split(',') | ||||
|     return latitudeRe.test(latitude) && longitudeRe.test(longitude) && addressRe.test(address) | ||||
| } | ||||
| 
 | ||||
| const isValidOrderStatus = (value) => Object.values(orderStatus).includes(value) | ||||
| const isValidOrderNotes = (value) => value.length < 500 | ||||
| 
 | ||||
| const VALIDATION_MESSAGES = { | ||||
|     order: { | ||||
|         notFound: 'Order not found' | ||||
|     }, | ||||
|     orderId: { | ||||
|         invalid: 'Valid order ID is required', | ||||
|     }, | ||||
|     orderStatus: { | ||||
|         invalid: 'Invalid order status' | ||||
|     }, | ||||
|     orderNotes: { | ||||
|         invalid: 'Invalid order notes' | ||||
|     }, | ||||
|     master: { | ||||
|         notFound: 'Master not found' | ||||
|     }, | ||||
|     masterId: { | ||||
|         invalid: 'Invalid master ID', | ||||
|     }, | ||||
|     phoneNumber: { | ||||
|         required: 'Phone number is required', | ||||
|         invalid: 'Invalid phone number' | ||||
|     }, | ||||
|     carNumber: { | ||||
|         required: 'Car number is required', | ||||
|         invalid: 'Invalid car number' | ||||
|     }, | ||||
|     carBody: { | ||||
|         required: 'Car body type is required', | ||||
|         invalid: 'Invalid car body type' | ||||
|     }, | ||||
|     carColor: { | ||||
|         invalid: 'Invalid car color' | ||||
|     }, | ||||
|     washingBegin: { | ||||
|         required: 'Begin time of washing is required', | ||||
|         invalid: 'Invalid begin time of washing' | ||||
|     }, | ||||
|     washingEnd: { | ||||
|         required: 'End time of washing is required', | ||||
|         invalid: 'Invalid end time of washing' | ||||
|     }, | ||||
|     washingLocation: { | ||||
|         required: 'Location of washing is required', | ||||
|         invalid: 'Invalid location of washing' | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| router.post('/create', async (req, res, next) => { | ||||
|     const bodyErrors = [] | ||||
| 
 | ||||
|     const { customer } = req.body | ||||
|     if (!customer.phone) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.phoneNumber.required) | ||||
|     } else if (!isValidPhoneNumber(customer.phone)) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.phoneNumber.invalid) | ||||
|     } | ||||
| 
 | ||||
|     const { car } = req.body | ||||
|     if (!car.number) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.carNumber.required) | ||||
|     } else if (!isValidCarNumber(car.number)) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.carNumber.invalid) | ||||
|     } | ||||
|     if (!car.body) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.carBody.required) | ||||
|     } else if (!isValidCarBodyType(car.body)) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.carBody.invalid) | ||||
|     } | ||||
|     if (!isValidCarColor(car.color)) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.carColor.invalid) | ||||
|     } | ||||
| 
 | ||||
|     const { washing } = req.body | ||||
|     if (!washing.begin) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.washingBegin.required) | ||||
|     } else if (!isValidISODate(washing.begin)) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.washingBegin.invalid) | ||||
|     } | ||||
|     if (!washing.end) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.washingEnd.required) | ||||
|     } else if (!isValidISODate(washing.end)) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.washingEnd.invalid) | ||||
|     } | ||||
|     if (!washing.location) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.washingLocation.required) | ||||
|     } else if (!isValidLocation(washing.location)) { | ||||
|         bodyErrors.push(VALIDATION_MESSAGES.washingLocation.invalid) | ||||
|     } | ||||
| 
 | ||||
|     if (bodyErrors.length > 0) { | ||||
|         throw new Error(bodyErrors.join(', ')) | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|         const order = await OrderModel.create({ | ||||
|             phone: customer.phone, | ||||
|             carNumber: car.number, | ||||
|             carBody: car.body, | ||||
|             carColor: car.color, | ||||
|             startWashTime: washing.begin, | ||||
|             endWashTime: washing.end, | ||||
|             location: washing.location, | ||||
|             status: orderStatus.PROGRESS, | ||||
|             notes: '', | ||||
|             created: new Date().toISOString(), | ||||
|         }) | ||||
| 
 | ||||
|         res.status(200).send({ success: true, body: order }) | ||||
| 
 | ||||
|     } catch (error) { | ||||
|         next(error) | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| router.get('/:id', async (req, res, next) => { | ||||
|     const { id } = req.params | ||||
|     if (!mongoose.Types.ObjectId.isValid(id)) { | ||||
|         throw new Error(VALIDATION_MESSAGES.orderId.invalid) | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|         const order = await OrderModel.findById(id) | ||||
|         if (!order) { | ||||
|             throw new Error(VALIDATION_MESSAGES.order.notFound) | ||||
|         } | ||||
| 
 | ||||
|         res.status(200).send({ success: true, body: order }) | ||||
|     } catch (error) { | ||||
|         next(error) | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| router.patch('/:id', async (req, res, next) => { | ||||
|     const { id } = req.params | ||||
|     if (!mongoose.Types.ObjectId.isValid(id)) { | ||||
|         throw new Error(VALIDATION_MESSAGES.orderId.invalid) | ||||
|     } | ||||
| 
 | ||||
|     const bodyErrors = [] | ||||
| 
 | ||||
|     const { status } = req.body | ||||
|     if (status) { | ||||
|         if (!isValidOrderStatus(status)) { | ||||
|             bodyErrors.push(VALIDATION_MESSAGES.orderStatus.invalid) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     const { master: masterId } = req.body | ||||
|     if (masterId) { | ||||
|         if (!mongoose.Types.ObjectId.isValid(masterId)) { | ||||
|             bodyErrors.push(VALIDATION_MESSAGES.masterId.invalid) | ||||
|         } else { | ||||
|             try { | ||||
|                 const master = await MasterModel.findById(masterId) | ||||
|                 if (!master) { | ||||
|                     bodyErrors.push(VALIDATION_MESSAGES.master.notFound) | ||||
|                 } | ||||
|             } catch (error) { | ||||
|                 next(error) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     const { notes } = req.body | ||||
|     if (notes) { | ||||
|         if (!isValidOrderNotes(notes)) { | ||||
|             bodyErrors.push(VALIDATION_MESSAGES.orderNotes.invalid) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (bodyErrors.length > 0) { | ||||
|         throw new Error(bodyErrors.join(', ')) | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|         const updateData = {} | ||||
|         if (status) { | ||||
|             updateData.status = status | ||||
|         } | ||||
|         if (masterId) { | ||||
|             updateData.master = masterId | ||||
|         } | ||||
|         if (notes) { | ||||
|             updateData.notes = notes | ||||
|         } | ||||
|         updateData.updated = new Date().toISOString() | ||||
| 
 | ||||
|         const order = await OrderModel.findByIdAndUpdate( | ||||
|             id, | ||||
|             updateData, | ||||
|             { new: true } | ||||
|         ) | ||||
|         if (!order) { | ||||
|             throw new Error(VALIDATION_MESSAGES.order.notFound) | ||||
|         } | ||||
| 
 | ||||
|         res.status(200).send({ success: true, body: order }) | ||||
|     } catch (error) { | ||||
|         next(error) | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| router.delete('/:id', async (req, res, next) => { | ||||
|     const { id } = req.params | ||||
|     if (!mongoose.Types.ObjectId.isValid(id)) { | ||||
|         throw new Error(VALIDATION_MESSAGES.orderId.invalid) | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|         const order = await OrderModel.findByIdAndDelete(id, { | ||||
|             new: true, | ||||
|         }) | ||||
|         if (!order) { | ||||
|             throw new Error(VALIDATION_MESSAGES.order.notFound) | ||||
|         } | ||||
|         res.status(200).send({ success: true, body: order }) | ||||
|     } catch (error) { | ||||
|         next(error) | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| module.exports = router | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user