fix(psa): fix time entry AttributeError and show all users in member mapping

- Fix create_time_entry() using self._client instead of self.client
- GET /member-mappings now returns all active account users, not just mapped
  ones — allows manual assignment when auto-match by email doesn't work
- PsaMemberMappingResponse mapping fields are now Optional (id, external_member_id,
  external_member_name, matched_by) to represent unmapped users
- Frontend MemberMappingTab skips null external_member_id when building
  localMappings, and derives user list from all returned entries
- Add docs/connectwise-psa-testing-checklist.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-04-14 06:09:01 +00:00
parent b433b232dc
commit 8eb814283d
6 changed files with 197 additions and 34 deletions

View File

@@ -648,10 +648,12 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
setCwMembers(members)
setMappings(existingMappings)
// Build local mapping state from existing mappings
// Build local mapping state from existing mappings (skip unmapped entries)
const lookup: Record<string, { external_member_id: string; external_member_name: string }> = {}
for (const m of existingMappings) {
lookup[m.user_id] = { external_member_id: m.external_member_id, external_member_name: m.external_member_name }
if (m.external_member_id) {
lookup[m.user_id] = { external_member_id: m.external_member_id, external_member_name: m.external_member_name ?? '' }
}
}
setLocalMappings(lookup)
setIsDirty(false)
@@ -716,14 +718,11 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
}
}
// Derive user list from mappings response (all account users are returned)
const userRows = mappings.length > 0
// All account users — includes both mapped and unmapped
const uniqueUsers = hasLoaded
? mappings.map(m => ({ user_id: m.user_id, user_email: m.user_email, user_name: m.user_name, matched_by: m.matched_by }))
: []
// Deduplicate: mappings may only contain mapped users, so we show what we have
const uniqueUsers = hasLoaded ? userRows : []
if (!connection) {
return (
<div className="max-w-3xl">

View File

@@ -108,13 +108,13 @@ export interface PsaMemberResponse {
}
export interface PsaMemberMappingResponse {
id: string
id: string | null
user_id: string
user_email: string
user_name: string
external_member_id: string
external_member_name: string
matched_by: string
external_member_id: string | null
external_member_name: string | null
matched_by: string | null
}
export interface AutoMatchResult {