feat(psa): implement post_note and update_ticket_status in CW provider

Replaces NotImplementedError stubs with working implementations:
- post_note maps note_type to CW flag fields (internalAnalysisFlag,
  resolutionFlag, detailDescriptionFlag) with appropriate visibility
  and notification settings
- update_ticket_status uses CW JSON Patch format to update status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-14 23:28:46 -04:00
parent 7059969d05
commit 74ee5009c2

View File

@@ -155,7 +155,7 @@ class ConnectWiseProvider(PSAProvider):
status=data.get("status", {}).get("name") if data.get("status") else None,
)
# ── Stubs for later slices ───────────────────────────────────────
# ── Notes & status updates ───────────────────────────────────────
async def post_note(
self,
@@ -164,12 +164,71 @@ class ConnectWiseProvider(PSAProvider):
note_type: str,
member_id: str | None = None,
) -> PSANote:
raise NotImplementedError("Implemented in Slice 4")
"""Post a note to a CW ticket.
Maps ResolutionFlow note types to CW flag fields:
- internal_analysis → internalAnalysisFlag (internal only)
- resolution → resolutionFlag (internal, triggers notifications)
- description → detailDescriptionFlag (external, triggers notifications)
"""
from app.services.psa.types import NoteType
flags = {
NoteType.INTERNAL_ANALYSIS: {
"internalAnalysisFlag": True,
"resolutionFlag": False,
"detailDescriptionFlag": False,
"internalFlag": True,
"processNotifications": False,
},
NoteType.RESOLUTION: {
"internalAnalysisFlag": False,
"resolutionFlag": True,
"detailDescriptionFlag": False,
"internalFlag": True,
"processNotifications": True,
},
NoteType.DESCRIPTION: {
"internalAnalysisFlag": False,
"resolutionFlag": False,
"detailDescriptionFlag": True,
"internalFlag": False,
"processNotifications": True,
},
}
note_flags = flags.get(note_type, flags[NoteType.INTERNAL_ANALYSIS])
body: dict = {
"text": text,
**note_flags,
}
if member_id:
body["member"] = {"id": int(member_id)}
data = await self.client.post(
f"/service/tickets/{ticket_id}/notes", json_body=body
)
return PSANote(
id=str(data.get("id", "")),
text=data.get("text", ""),
note_type=note_type,
created_at=data.get("dateCreated"),
)
async def update_ticket_status(
self, ticket_id: str, status_id: int
) -> PSATicket:
raise NotImplementedError("Implemented in Slice 4")
"""Update a CW ticket's status using JSON Patch format."""
patch_body = [
{"op": "replace", "path": "status", "value": {"id": status_id}}
]
data = await self.client.patch(
f"/service/tickets/{ticket_id}", json_body=patch_body
)
return self._map_ticket(data)
async def list_members(self) -> list[PSAMember]:
raise NotImplementedError("Implemented in Slice 5")