fix: remove nested .git folders, re-add as normal directories
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* @module cw.opportunityService
|
||||
*
|
||||
* ConnectWise Opportunity Service
|
||||
* ================================
|
||||
*
|
||||
* Methods for ConnectWise integrations that the opportunity workflow
|
||||
* calls. Some are still stubs (marked with console.warn); others are
|
||||
* fully implemented against the CW REST API.
|
||||
*/
|
||||
|
||||
import { opportunityCw } from "../modules/cw-utils/opportunities/opportunities";
|
||||
import { connectWiseApi } from "../constants";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface TimeEntryInput {
|
||||
/** CW activity ID to charge the time entry to. */
|
||||
activityId: number;
|
||||
/** CW member ID of the user submitting time. */
|
||||
cwMemberId: number;
|
||||
/** ISO-8601 datetime when work started. */
|
||||
timeStart: string;
|
||||
/** ISO-8601 datetime when work ended. */
|
||||
timeEnd: string;
|
||||
notes: string;
|
||||
}
|
||||
|
||||
export interface TimeEntryResult {
|
||||
success: boolean;
|
||||
cwTimeEntryId: number | null;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface StatusSyncInput {
|
||||
opportunityId: number;
|
||||
statusCwId: number;
|
||||
}
|
||||
|
||||
export interface StatusSyncResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Service
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Submit a time entry to ConnectWise for an opportunity activity.
|
||||
*
|
||||
* Called automatically whenever `timeStart` and `timeEnd` are provided
|
||||
* on a workflow action.
|
||||
*/
|
||||
export async function submitTimeEntry(
|
||||
input: TimeEntryInput,
|
||||
): Promise<TimeEntryResult> {
|
||||
try {
|
||||
const stripMs = (d: string) => d.replace(/\.\d{3}Z$/, "Z");
|
||||
|
||||
const response = await connectWiseApi.post("/time/entries", {
|
||||
member: { id: input.cwMemberId },
|
||||
chargeToType: "Activity",
|
||||
chargeToId: input.activityId,
|
||||
timeStart: stripMs(input.timeStart),
|
||||
timeEnd: stripMs(input.timeEnd),
|
||||
notes: input.notes,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
cwTimeEntryId: response.data?.id ?? null,
|
||||
message: `Time entry ${response.data?.id} created for activity ${input.activityId}.`,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
`[cw.opportunityService] submitTimeEntry FAILED — activityId=${input.activityId}, cwMemberId=${input.cwMemberId}`,
|
||||
error?.response?.data ?? error,
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
cwTimeEntryId: null,
|
||||
message: `Failed to submit time entry: ${error?.message ?? "Unknown error"}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync an opportunity's status to ConnectWise.
|
||||
*
|
||||
* Called whenever the workflow transitions an opportunity to a new
|
||||
* status, ensuring the CW record stays in sync.
|
||||
*/
|
||||
export async function syncOpportunityStatus(
|
||||
input: StatusSyncInput,
|
||||
): Promise<StatusSyncResult> {
|
||||
try {
|
||||
await opportunityCw.update(input.opportunityId, {
|
||||
status: { id: input.statusCwId },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Opportunity ${input.opportunityId} status synced to CW status ID ${input.statusCwId}.`,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
`[cw.opportunityService] syncOpportunityStatus FAILED — opportunityId=${input.opportunityId}, statusCwId=${input.statusCwId}`,
|
||||
error?.response?.data ?? error,
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `Failed to sync opportunity status: ${error?.message ?? "Unknown error"}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user