"""Fixture payloads for ``POST /qqweb/WhatsAppUser/businessSalesList`` (M3). These records mirror the real field names captured from the live CRM (see the data dictionary §3 and phase0-findings.md §5): ``uid`` (PK, ``@s.whatsapp.net``), ``userId`` / ``userName`` (owning staff real name), ``nickName``, ``phone`` / ``salePhone`` / ``salesBindPhone``, ``contactsNum`` / ``groupNum`` / ``talkerCount`` / ``friendAddNum``, ``isOnJob``, ``lastUpdate``. The envelope differs from the WeChat list endpoints: rows sit under ``data.overviews`` (NOT ``data.data``) and the declared row count is ``data.totalCount`` (NOT ``data.total``); the page count is ``data.pageCount``:: {"message":"调用成功","code":"10000","success":true,"traceId":"...", "data":{"page":.., "pageCount":.., "totalCount":.., "overviews":[...]}} The cassette ships two pages so a test can drive the page-number paginator across the empty-page stop signal: * page 1 -> 2 of the 4 fixture records, ``totalCount=4`` * page 2 -> the other 2 records, ``totalCount=4`` * (page 3 -> empty ``overviews`` array, served inline by the test) Records carry every field with a target ``WhatsappAccount`` column so the test can assert a full mapping end-to-end, plus the embedded ``userId`` + ``userName`` staff pair (the owning employee) so the embedded-staff repo upsert can be exercised against capture-shaped input (including a no-``userId`` orphan to exercise the best-effort skip path). """ from __future__ import annotations from typing import Any # --------------------------------------------------------------------------- # Capture-shaped WhatsApp staff-account records (4 total across 2 pages). # --------------------------------------------------------------------------- WA_ACCOUNT_RECORDS: list[dict[str, Any]] = [ { "uid": "8613800000001@s.whatsapp.net", "userId": "wa_staff_001", "userName": "Alice Wang", "nickName": "Alice WA", "phone": "8613800000001", "salePhone": "8613800000001", "salesBindPhone": "8613900000001", "contactsNum": 421, "groupNum": 12, "talkerCount": 38, "friendAddNum": 7, "isOnJob": 1, "lastUpdate": 1700000000123, "version": "v1.20260731.alice.wa", }, { "uid": "8613800000002@s.whatsapp.net", "userId": "wa_staff_002", "userName": "Bob Chen", "nickName": "Bob WA", "phone": "8613800000002", "salePhone": "8613800000002", "salesBindPhone": "8613900000002", "contactsNum": 58, "groupNum": 2, "talkerCount": 9, "friendAddNum": 1, "isOnJob": 1, "lastUpdate": 1700000000456, "version": "v1.20260731.bob.wa", }, { "uid": "8613800000003@s.whatsapp.net", "userId": "wa_staff_003", "userName": "Carol Liu", "nickName": "Carol WA", "phone": "8613800000003", "salePhone": "8613800000003", "salesBindPhone": "8613900000003", "contactsNum": 1899, "groupNum": 64, "talkerCount": 210, "friendAddNum": 33, "isOnJob": 0, # departed staff — exercises the falsy isOnJob path "lastUpdate": 1700000000789, "version": "v1.20260731.carol.wa", }, { # Account with NO embedded staff (userId missing) — the scraper must # still yield it; the embedded-staff repo upsert must skip it cleanly. "uid": "8613800000004@s.whatsapp.net", "userId": None, "userName": None, "nickName": "Orphan WA", "phone": "8613800000004", "salePhone": "8613800000004", "salesBindPhone": "8613900000004", "contactsNum": 3, "groupNum": 0, "talkerCount": 1, "friendAddNum": 0, "isOnJob": 0, "lastUpdate": 1700000001000, "version": "v1.20260731.orphan.wa", }, ] # Default page size for the fixture (and the scraper). totalCount=4 means the # fixture fills exactly 2 pages of size 2 plus a terminating empty page. TOTAL = len(WA_ACCOUNT_RECORDS) DEFAULT_PAGE_SIZE = 2 def make_wa_accounts_response( records: list[dict[str, Any]], *, total_count: int, page: int = 1, page_size: int = DEFAULT_PAGE_SIZE, page_count: int | None = None, trace_id: str | None = None, ) -> dict[str, Any]: """Build a capture-shaped ``businessSalesList`` response envelope. Shape:: {"message":"调用成功","code":"10000","success":true,"traceId":"...", "data":{"page":.., "pageCount":.., "totalCount":.., "overviews":[...]}} ``page_count`` defaults to ``ceil(total_count / page_size)`` when omitted. """ if page_count is None: import math page_count = math.ceil(total_count / page_size) if page_size else 1 return { "message": "调用成功", "code": "10000", "success": True, "traceId": trace_id or f"trace-wa-acct-{page}", "data": { "page": page, "pageCount": page_count, "totalCount": total_count, "overviews": list(records), }, }