Mbonea commited on
Commit
918afce
·
1 Parent(s): cb4d2cc

Require SMS number for manual sales

Browse files
API_DOCUMENTATION.md CHANGED
@@ -1103,6 +1103,7 @@ Plan selection is mandatory. The backend copies the selected plan's real price,
1103
  **Validation:**
1104
  - `device_id` required
1105
  - `plan_id` required
 
1106
  - selected plan must belong to the selected device
1107
  - selected plan must belong to the authenticated tenant
1108
  - selected plan must be active
 
1103
  **Validation:**
1104
  - `device_id` required
1105
  - `plan_id` required
1106
+ - `customer_phone` required so the generated token can be sent by SMS
1107
  - selected plan must belong to the selected device
1108
  - selected plan must belong to the authenticated tenant
1109
  - selected plan must be active
src/routes/manualSales.routes.js CHANGED
@@ -71,12 +71,16 @@ router.get('/', async (req, res) => {
71
  // POST /api/manual-sales
72
  router.post('/', async (req, res) => {
73
  const { device_id, plan_id, customer_phone = null, notes = null } = req.body;
 
74
 
75
  if (!device_id || !plan_id) {
76
  return res.status(400).json({ error: 'device_id and plan_id are required' });
77
  }
78
- if (customer_phone !== null && typeof customer_phone !== 'string') {
79
- return res.status(400).json({ error: 'customer_phone must be a string or null' });
 
 
 
80
  }
81
  if (notes !== null && typeof notes !== 'string') {
82
  return res.status(400).json({ error: 'notes must be a string or null' });
@@ -172,7 +176,7 @@ router.post('/', async (req, res) => {
172
  req.client.id,
173
  device.id,
174
  plan.id,
175
- customer_phone?.trim() || null,
176
  'other',
177
  req.client.id,
178
  notes?.trim() || null,
@@ -195,16 +199,12 @@ router.post('/', async (req, res) => {
195
 
196
  await connection.commit();
197
 
198
- const cleanCustomerPhone = customer_phone?.trim() || null;
199
- let smsSent = false;
200
- if (cleanCustomerPhone) {
201
- const duration = formatDuration(plan.duration_seconds);
202
- const expiryPreview = formatTzExpiryPreview(plan.duration_seconds);
203
- smsSent = Boolean(await sms.sendSMS(
204
- cleanCustomerPhone,
205
- messages.manualWifiCode(code, plan.name, duration, device.name, expiryPreview)
206
- ).catch(() => false));
207
- }
208
 
209
  res.status(201).json({
210
  payment_id: paymentResult.insertId,
@@ -223,8 +223,10 @@ router.post('/', async (req, res) => {
223
  duration_seconds: plan.duration_seconds,
224
  notes: notes?.trim() || null,
225
  sms_sent: smsSent,
226
- expires_at_preview: formatTzExpiryPreview(plan.duration_seconds),
227
- message: 'Token generated successfully',
 
 
228
  });
229
  } catch (err) {
230
  await connection.rollback();
 
71
  // POST /api/manual-sales
72
  router.post('/', async (req, res) => {
73
  const { device_id, plan_id, customer_phone = null, notes = null } = req.body;
74
+ const cleanCustomerPhone = typeof customer_phone === 'string' ? customer_phone.trim() : '';
75
 
76
  if (!device_id || !plan_id) {
77
  return res.status(400).json({ error: 'device_id and plan_id are required' });
78
  }
79
+ if (typeof customer_phone !== 'string' || !cleanCustomerPhone) {
80
+ return res.status(400).json({ error: 'customer_phone is required so the token can be sent by SMS' });
81
+ }
82
+ if (cleanCustomerPhone.replace(/\D/g, '').length < 9) {
83
+ return res.status(400).json({ error: 'customer_phone must be a valid phone number' });
84
  }
85
  if (notes !== null && typeof notes !== 'string') {
86
  return res.status(400).json({ error: 'notes must be a string or null' });
 
176
  req.client.id,
177
  device.id,
178
  plan.id,
179
+ cleanCustomerPhone,
180
  'other',
181
  req.client.id,
182
  notes?.trim() || null,
 
199
 
200
  await connection.commit();
201
 
202
+ const duration = formatDuration(plan.duration_seconds);
203
+ const expiryPreview = formatTzExpiryPreview(plan.duration_seconds);
204
+ const smsSent = Boolean(await sms.sendSMS(
205
+ cleanCustomerPhone,
206
+ messages.manualWifiCode(code, plan.name, duration, device.name, expiryPreview)
207
+ ).catch(() => false));
 
 
 
 
208
 
209
  res.status(201).json({
210
  payment_id: paymentResult.insertId,
 
223
  duration_seconds: plan.duration_seconds,
224
  notes: notes?.trim() || null,
225
  sms_sent: smsSent,
226
+ expires_at_preview: expiryPreview,
227
+ message: smsSent
228
+ ? 'Token generated and SMS sent successfully'
229
+ : 'Token generated, but SMS failed to send',
230
  });
231
  } catch (err) {
232
  await connection.rollback();