# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: api-verification.spec.ts >> 预约 API >> 创建预约（完整字段）
- Location: tests/api-verification.spec.ts:340:7

# Error details

```
Error: expect(received).toBe(expected) // Object.is equality

Expected: 201
Received: 409
```

# Test source

```ts
  253 |       },
  254 |     });
  255 |     // 假 voucher_id 不应该成功！应该返回 400/404
  256 |     expect(res.status()).toBeGreaterThanOrEqual(400);
  257 |   });
  258 | });
  259 | 
  260 | // ═══════════════════════════════════════════
  261 | // 正常支付对照
  262 | // ═══════════════════════════════════════════
  263 | test.describe('正常支付（对照组）', () => {
  264 | 
  265 |   test('散客现金结账', async ({ request }) => {
  266 |     const res = await request.post(`${BASE}/nail/pos/transactions`, {
  267 |       headers: headers(),
  268 |       data: {
  269 |         pos_session_id: sessionId,
  270 |         items: [{ type: 'service', reference_id: SVC_PURE, name: '纯色美甲', quantity: 1, unit_price_cents: 8800 }],
  271 |         goods_items: [], tip_cents: 0, service_charge_cents: 0, discount_cents: 0,
  272 |         order_date: today(), method: 'cash',
  273 |         split_payments: [{ method: 'cash', amount_cents: 8800 }],
  274 |         stored_value_splits: [],
  275 |       },
  276 |     });
  277 |     expect(res.status()).toBe(201);
  278 |     const data = await res.json();
  279 |     expect(data.status).toBe('completed');
  280 |   });
  281 | 
  282 |   test('会员现金结账', async ({ request }) => {
  283 |     const res = await request.post(`${BASE}/nail/pos/transactions`, {
  284 |       headers: headers(),
  285 |       data: {
  286 |         pos_session_id: sessionId, client_id: ADA_ID,
  287 |         items: [{ type: 'service', reference_id: SVC_PURE, name: '纯色美甲', quantity: 1, unit_price_cents: 8800 }],
  288 |         goods_items: [], tip_cents: 0, service_charge_cents: 0, discount_cents: 0,
  289 |         order_date: today(), method: 'cash',
  290 |         split_payments: [{ method: 'cash', amount_cents: 8800 }],
  291 |         stored_value_splits: [],
  292 |       },
  293 |     });
  294 |     expect(res.status()).toBe(201);
  295 |     const data = await res.json();
  296 |     expect(data.status).toBe('completed');
  297 |   });
  298 | 
  299 |   test('member_card 支付', async ({ request }) => {
  300 |     const res = await request.post(`${BASE}/nail/pos/transactions`, {
  301 |       headers: headers(),
  302 |       data: {
  303 |         pos_session_id: sessionId, client_id: ADA_ID,
  304 |         items: [{ type: 'service', reference_id: SVC_PURE, name: '纯色美甲', quantity: 1, unit_price_cents: 8800 }],
  305 |         goods_items: [], tip_cents: 0, service_charge_cents: 0, discount_cents: 0,
  306 |         order_date: today(), method: 'member_card',
  307 |         split_payments: [{ method: 'member_card', amount_cents: 8800 }],
  308 |         stored_value_splits: [],
  309 |       },
  310 |     });
  311 |     expect(res.status()).toBe(201);
  312 |   });
  313 | 
  314 |   test('多服务现金结账', async ({ request }) => {
  315 |     const res = await request.post(`${BASE}/nail/pos/transactions`, {
  316 |       headers: headers(),
  317 |       data: {
  318 |         pos_session_id: sessionId, client_id: ADA_ID,
  319 |         items: [
  320 |           { type: 'service', reference_id: SVC_PURE, name: '纯色美甲', quantity: 1, unit_price_cents: 8800 },
  321 |           { type: 'service', reference_id: '01KT3VBKHPTV2NTMPSF1XJ7HQC', name: '渐变美甲', quantity: 1, unit_price_cents: 13800 },
  322 |         ],
  323 |         goods_items: [], tip_cents: 0, service_charge_cents: 0, discount_cents: 0,
  324 |         order_date: today(), method: 'cash',
  325 |         split_payments: [{ method: 'cash', amount_cents: 22600 }],
  326 |         stored_value_splits: [],
  327 |       },
  328 |     });
  329 |     expect(res.status()).toBe(201);
  330 |     const data = await res.json();
  331 |     expect(data.total_cents).toBe(22600);
  332 |   });
  333 | });
  334 | 
  335 | // ═══════════════════════════════════════════
  336 | // 预约 API
  337 | // ═══════════════════════════════════════════
  338 | test.describe('预约 API', () => {
  339 | 
  340 |   test('创建预约（完整字段）', async ({ request }) => {
  341 |     const res = await request.post(`${BASE}/nail/appointments`, {
  342 |       headers: headers(),
  343 |       data: {
  344 |         client_id: ADA_ID,
  345 |         service_id: SVC_PURE,
  346 |         employee_id: EMPLOYEE_ID,
  347 |         start_time: '2026-07-26T10:00:00Z',
  348 |         source: 'sys_employee',
  349 |         items: [{ service_id: SVC_PURE, employee_id: EMPLOYEE_ID, start_time: '2026-07-26T10:00:00Z', price_cents: 8800 }],
  350 |         notes: 'E2E测试',
  351 |       },
  352 |     });
> 353 |     expect(res.status()).toBe(201);
      |                          ^ Error: expect(received).toBe(expected) // Object.is equality
  354 |     const data = await res.json();
  355 |     expect(data.status).toBe('confirmed');
  356 |   });
  357 | 
  358 |   test('创建预约（前端格式，缺顶层字段）', async ({ request }) => {
  359 |     const res = await request.post(`${BASE}/nail/appointments`, {
  360 |       headers: headers(),
  361 |       data: {
  362 |         items: [{ service_id: SVC_PURE, employee_id: null, start_time: '2026-07-26T14:00:00Z' }],
  363 |         notes: '',
  364 |         client_id: ADA_ID,
  365 |       },
  366 |     });
  367 |     // 不应该 500（之前修好了）
  368 |     expect(res.status()).not.toBe(500);
  369 |   });
  370 | 
  371 |   test('创建预约 - 无效 service_id 应报错', async ({ request }) => {
  372 |     const res = await request.post(`${BASE}/nail/appointments`, {
  373 |       headers: headers(),
  374 |       data: {
  375 |         client_id: ADA_ID,
  376 |         service_id: 'invalid_service_id',
  377 |         employee_id: EMPLOYEE_ID,
  378 |         start_time: '2026-07-26T16:00:00Z',
  379 |         source: 'sys_employee',
  380 |         items: [{ service_id: 'invalid_service_id', employee_id: EMPLOYEE_ID, start_time: '2026-07-26T16:00:00Z', price_cents: 8800 }],
  381 |         notes: '',
  382 |       },
  383 |     });
  384 |     // 无效 service_id 应该返回 400/404，不是 500
  385 |     expect(res.status()).not.toBe(500);
  386 |   });
  387 | });
  388 | 
  389 | // ═══════════════════════════════════════════
  390 | // 会员数据验证
  391 | // ═══════════════════════════════════════════
  392 | test.describe('会员数据验证', () => {
  393 | 
  394 |   test('会员详情 - 消费记录更新', async ({ request }) => {
  395 |     const res = await request.get(`${BASE}/nail/clients/${ADA_ID}`, {
  396 |       headers: headers(),
  397 |     });
  398 |     expect(res.status()).toBe(200);
  399 |     const data = await res.json();
  400 |     expect(data.name).toBe('Ada');
  401 |     expect(data.total_visits).toBeGreaterThan(0);
  402 |     expect(data.total_spent_cents).toBeGreaterThan(0);
  403 |   });
  404 | 
  405 |   test('会员详情 - debt_amount 不为负', async ({ request }) => {
  406 |     const res = await request.get(`${BASE}/nail/clients/${ADA_ID}`, {
  407 |       headers: headers(),
  408 |     });
  409 |     expect(res.status()).toBe(200);
  410 |     const data = await res.json();
  411 |     expect(data.debt_amount_cents).toBeGreaterThanOrEqual(0);
  412 |   });
  413 | });
  414 | 
```