Fix UserController permission serialization and include current updates
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
import { Opportunity } from "../../generated/prisma/client";
|
||||
import { prisma } from "../constants";
|
||||
import { fetchOpportunity } from "../modules/cw-utils/opportunities/fetchOpportunity";
|
||||
import { CWOpportunity } from "../modules/cw-utils/opportunities/opportunity.types";
|
||||
|
||||
/**
|
||||
* Opportunity Controller
|
||||
*
|
||||
* Domain model class that encapsulates an Opportunity entity and provides
|
||||
* methods for accessing, refreshing from ConnectWise, and serializing
|
||||
* opportunity data.
|
||||
*/
|
||||
export class OpportunityController {
|
||||
public readonly id: string;
|
||||
public readonly cwOpportunityId: number;
|
||||
public name: string;
|
||||
public notes: string | null;
|
||||
|
||||
public typeName: string | null;
|
||||
public typeCwId: number | null;
|
||||
public stageName: string | null;
|
||||
public stageCwId: number | null;
|
||||
public statusName: string | null;
|
||||
public statusCwId: number | null;
|
||||
public priorityName: string | null;
|
||||
public priorityCwId: number | null;
|
||||
public ratingName: string | null;
|
||||
public ratingCwId: number | null;
|
||||
public source: string | null;
|
||||
public campaignName: string | null;
|
||||
public campaignCwId: number | null;
|
||||
|
||||
public primarySalesRepName: string | null;
|
||||
public primarySalesRepIdentifier: string | null;
|
||||
public primarySalesRepCwId: number | null;
|
||||
public secondarySalesRepName: string | null;
|
||||
public secondarySalesRepIdentifier: string | null;
|
||||
public secondarySalesRepCwId: number | null;
|
||||
|
||||
public companyCwId: number | null;
|
||||
public companyName: string | null;
|
||||
public contactCwId: number | null;
|
||||
public contactName: string | null;
|
||||
public siteCwId: number | null;
|
||||
public siteName: string | null;
|
||||
public customerPO: string | null;
|
||||
|
||||
public totalSalesTax: number;
|
||||
|
||||
public locationName: string | null;
|
||||
public locationCwId: number | null;
|
||||
public departmentName: string | null;
|
||||
public departmentCwId: number | null;
|
||||
|
||||
public expectedCloseDate: Date | null;
|
||||
public pipelineChangeDate: Date | null;
|
||||
public dateBecameLead: Date | null;
|
||||
public closedDate: Date | null;
|
||||
public closedFlag: boolean;
|
||||
public closedByName: string | null;
|
||||
public closedByCwId: number | null;
|
||||
|
||||
public companyId: string | null;
|
||||
public cwLastUpdated: Date | null;
|
||||
|
||||
public readonly createdAt: Date;
|
||||
public updatedAt: Date;
|
||||
|
||||
constructor(data: Opportunity) {
|
||||
this.id = data.id;
|
||||
this.cwOpportunityId = data.cwOpportunityId;
|
||||
this.name = data.name;
|
||||
this.notes = data.notes;
|
||||
|
||||
this.typeName = data.typeName;
|
||||
this.typeCwId = data.typeCwId;
|
||||
this.stageName = data.stageName;
|
||||
this.stageCwId = data.stageCwId;
|
||||
this.statusName = data.statusName;
|
||||
this.statusCwId = data.statusCwId;
|
||||
this.priorityName = data.priorityName;
|
||||
this.priorityCwId = data.priorityCwId;
|
||||
this.ratingName = data.ratingName;
|
||||
this.ratingCwId = data.ratingCwId;
|
||||
this.source = data.source;
|
||||
this.campaignName = data.campaignName;
|
||||
this.campaignCwId = data.campaignCwId;
|
||||
|
||||
this.primarySalesRepName = data.primarySalesRepName;
|
||||
this.primarySalesRepIdentifier = data.primarySalesRepIdentifier;
|
||||
this.primarySalesRepCwId = data.primarySalesRepCwId;
|
||||
this.secondarySalesRepName = data.secondarySalesRepName;
|
||||
this.secondarySalesRepIdentifier = data.secondarySalesRepIdentifier;
|
||||
this.secondarySalesRepCwId = data.secondarySalesRepCwId;
|
||||
|
||||
this.companyCwId = data.companyCwId;
|
||||
this.companyName = data.companyName;
|
||||
this.contactCwId = data.contactCwId;
|
||||
this.contactName = data.contactName;
|
||||
this.siteCwId = data.siteCwId;
|
||||
this.siteName = data.siteName;
|
||||
this.customerPO = data.customerPO;
|
||||
|
||||
this.totalSalesTax = data.totalSalesTax;
|
||||
|
||||
this.locationName = data.locationName;
|
||||
this.locationCwId = data.locationCwId;
|
||||
this.departmentName = data.departmentName;
|
||||
this.departmentCwId = data.departmentCwId;
|
||||
|
||||
this.expectedCloseDate = data.expectedCloseDate;
|
||||
this.pipelineChangeDate = data.pipelineChangeDate;
|
||||
this.dateBecameLead = data.dateBecameLead;
|
||||
this.closedDate = data.closedDate;
|
||||
this.closedFlag = data.closedFlag;
|
||||
this.closedByName = data.closedByName;
|
||||
this.closedByCwId = data.closedByCwId;
|
||||
|
||||
this.companyId = data.companyId;
|
||||
this.cwLastUpdated = data.cwLastUpdated;
|
||||
|
||||
this.createdAt = data.createdAt;
|
||||
this.updatedAt = data.updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh from ConnectWise
|
||||
*
|
||||
* Fetches the latest opportunity data from CW and updates
|
||||
* the local database record and controller state.
|
||||
*/
|
||||
public async refreshFromCW(): Promise<OpportunityController> {
|
||||
const cwData = await fetchOpportunity(this.cwOpportunityId);
|
||||
const mapped = OpportunityController.mapCwToDb(cwData);
|
||||
|
||||
const updated = await prisma.opportunity.update({
|
||||
where: { id: this.id },
|
||||
data: mapped,
|
||||
});
|
||||
|
||||
return new OpportunityController(updated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch raw CW data
|
||||
*
|
||||
* Returns the raw ConnectWise opportunity object without updating the DB.
|
||||
*/
|
||||
public async fetchCwData(): Promise<CWOpportunity> {
|
||||
return fetchOpportunity(this.cwOpportunityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map CW Opportunity → Prisma create/update payload
|
||||
*
|
||||
* Static helper used by both the controller and the refresh sync.
|
||||
*/
|
||||
public static mapCwToDb(item: CWOpportunity) {
|
||||
return {
|
||||
name: item.name,
|
||||
notes: item.notes ?? null,
|
||||
|
||||
typeName: item.type?.name ?? null,
|
||||
typeCwId: item.type?.id ?? null,
|
||||
stageName: item.stage?.name ?? null,
|
||||
stageCwId: item.stage?.id ?? null,
|
||||
statusName: item.status?.name ?? null,
|
||||
statusCwId: item.status?.id ?? null,
|
||||
priorityName: item.priority?.name ?? null,
|
||||
priorityCwId: item.priority?.id ?? null,
|
||||
ratingName: item.rating?.name ?? null,
|
||||
ratingCwId: item.rating?.id ?? null,
|
||||
source: item.source ?? null,
|
||||
campaignName: item.campaign?.name ?? null,
|
||||
campaignCwId: item.campaign?.id ?? null,
|
||||
|
||||
primarySalesRepName: item.primarySalesRep?.name ?? null,
|
||||
primarySalesRepIdentifier: item.primarySalesRep?.identifier ?? null,
|
||||
primarySalesRepCwId: item.primarySalesRep?.id ?? null,
|
||||
secondarySalesRepName: item.secondarySalesRep?.name ?? null,
|
||||
secondarySalesRepIdentifier: item.secondarySalesRep?.identifier ?? null,
|
||||
secondarySalesRepCwId: item.secondarySalesRep?.id ?? null,
|
||||
|
||||
companyCwId: item.company?.id ?? null,
|
||||
companyName: item.company?.name ?? null,
|
||||
contactCwId: item.contact?.id ?? null,
|
||||
contactName: item.contact?.name ?? null,
|
||||
siteCwId: item.site?.id ?? null,
|
||||
siteName: item.site?.name ?? null,
|
||||
customerPO: item.customerPO ?? null,
|
||||
|
||||
totalSalesTax: item.totalSalesTax ?? 0,
|
||||
|
||||
locationName: item.location?.name ?? null,
|
||||
locationCwId: item.location?.id ?? null,
|
||||
departmentName: item.department?.name ?? null,
|
||||
departmentCwId: item.department?.id ?? null,
|
||||
|
||||
expectedCloseDate: item.expectedCloseDate
|
||||
? new Date(item.expectedCloseDate)
|
||||
: null,
|
||||
pipelineChangeDate: item.pipelineChangeDate
|
||||
? new Date(item.pipelineChangeDate)
|
||||
: null,
|
||||
dateBecameLead: item.dateBecameLead
|
||||
? new Date(item.dateBecameLead)
|
||||
: null,
|
||||
closedDate: item.closedDate ? new Date(item.closedDate) : null,
|
||||
closedFlag: item.closedFlag ?? false,
|
||||
closedByName: item.closedBy?.name ?? null,
|
||||
closedByCwId: item.closedBy?.id ?? null,
|
||||
|
||||
cwLastUpdated: item._info?.lastUpdated
|
||||
? new Date(item._info.lastUpdated)
|
||||
: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* To JSON
|
||||
*
|
||||
* Serializes the opportunity into a safe, API-friendly object.
|
||||
*/
|
||||
public toJson(): Record<string, any> {
|
||||
return {
|
||||
id: this.id,
|
||||
cwOpportunityId: this.cwOpportunityId,
|
||||
name: this.name,
|
||||
notes: this.notes,
|
||||
type: this.typeCwId ? { id: this.typeCwId, name: this.typeName } : null,
|
||||
stage: this.stageCwId
|
||||
? { id: this.stageCwId, name: this.stageName }
|
||||
: null,
|
||||
status: this.statusCwId
|
||||
? { id: this.statusCwId, name: this.statusName }
|
||||
: null,
|
||||
priority: this.priorityCwId
|
||||
? { id: this.priorityCwId, name: this.priorityName }
|
||||
: null,
|
||||
rating: this.ratingCwId
|
||||
? { id: this.ratingCwId, name: this.ratingName }
|
||||
: null,
|
||||
source: this.source,
|
||||
campaign: this.campaignCwId
|
||||
? { id: this.campaignCwId, name: this.campaignName }
|
||||
: null,
|
||||
primarySalesRep: this.primarySalesRepCwId
|
||||
? {
|
||||
id: this.primarySalesRepCwId,
|
||||
identifier: this.primarySalesRepIdentifier,
|
||||
name: this.primarySalesRepName,
|
||||
}
|
||||
: null,
|
||||
secondarySalesRep: this.secondarySalesRepCwId
|
||||
? {
|
||||
id: this.secondarySalesRepCwId,
|
||||
identifier: this.secondarySalesRepIdentifier,
|
||||
name: this.secondarySalesRepName,
|
||||
}
|
||||
: null,
|
||||
company: this.companyCwId
|
||||
? { id: this.companyCwId, name: this.companyName }
|
||||
: null,
|
||||
contact: this.contactCwId
|
||||
? { id: this.contactCwId, name: this.contactName }
|
||||
: null,
|
||||
site: this.siteCwId ? { id: this.siteCwId, name: this.siteName } : null,
|
||||
customerPO: this.customerPO,
|
||||
totalSalesTax: this.totalSalesTax,
|
||||
location: this.locationCwId
|
||||
? { id: this.locationCwId, name: this.locationName }
|
||||
: null,
|
||||
department: this.departmentCwId
|
||||
? { id: this.departmentCwId, name: this.departmentName }
|
||||
: null,
|
||||
expectedCloseDate: this.expectedCloseDate,
|
||||
pipelineChangeDate: this.pipelineChangeDate,
|
||||
dateBecameLead: this.dateBecameLead,
|
||||
closedDate: this.closedDate,
|
||||
closedFlag: this.closedFlag,
|
||||
closedBy: this.closedByCwId
|
||||
? { id: this.closedByCwId, name: this.closedByName }
|
||||
: null,
|
||||
companyId: this.companyId,
|
||||
cwLastUpdated: this.cwLastUpdated,
|
||||
createdAt: this.createdAt,
|
||||
updatedAt: this.updatedAt,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user