chore(global): remove a bunch of test and temp files
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -381,7 +381,7 @@ export type OpportunityGroupByOutputType = {
|
||||
name: string
|
||||
notes: string | null
|
||||
oppNarrative: string | null
|
||||
typeId: number
|
||||
typeId: number | null
|
||||
stageId: number | null
|
||||
statusId: number | null
|
||||
taxCodeId: number | null
|
||||
@@ -438,7 +438,7 @@ export type OpportunityWhereInput = {
|
||||
name?: Prisma.StringFilter<"Opportunity"> | string
|
||||
notes?: Prisma.StringNullableFilter<"Opportunity"> | string | null
|
||||
oppNarrative?: Prisma.StringNullableFilter<"Opportunity"> | string | null
|
||||
typeId?: Prisma.IntFilter<"Opportunity"> | number
|
||||
typeId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
stageId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
statusId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
taxCodeId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
@@ -465,7 +465,7 @@ export type OpportunityWhereInput = {
|
||||
createdAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string
|
||||
updatedAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesListRelationFilter
|
||||
type?: Prisma.XOR<Prisma.OpportunityTypeScalarRelationFilter, Prisma.OpportunityTypeWhereInput>
|
||||
type?: Prisma.XOR<Prisma.OpportunityTypeNullableScalarRelationFilter, Prisma.OpportunityTypeWhereInput> | null
|
||||
stage?: Prisma.XOR<Prisma.OpportunityStageNullableScalarRelationFilter, Prisma.OpportunityStageWhereInput> | null
|
||||
status?: Prisma.XOR<Prisma.OpportunityStatusNullableScalarRelationFilter, Prisma.OpportunityStatusWhereInput> | null
|
||||
taxCode?: Prisma.XOR<Prisma.TaxCodeNullableScalarRelationFilter, Prisma.TaxCodeWhereInput> | null
|
||||
@@ -485,7 +485,7 @@ export type OpportunityOrderByWithRelationInput = {
|
||||
name?: Prisma.SortOrder
|
||||
notes?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
oppNarrative?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
typeId?: Prisma.SortOrder
|
||||
typeId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
stageId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
statusId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
taxCodeId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
@@ -535,7 +535,7 @@ export type OpportunityWhereUniqueInput = Prisma.AtLeast<{
|
||||
name?: Prisma.StringFilter<"Opportunity"> | string
|
||||
notes?: Prisma.StringNullableFilter<"Opportunity"> | string | null
|
||||
oppNarrative?: Prisma.StringNullableFilter<"Opportunity"> | string | null
|
||||
typeId?: Prisma.IntFilter<"Opportunity"> | number
|
||||
typeId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
stageId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
statusId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
taxCodeId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
@@ -562,7 +562,7 @@ export type OpportunityWhereUniqueInput = Prisma.AtLeast<{
|
||||
createdAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string
|
||||
updatedAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesListRelationFilter
|
||||
type?: Prisma.XOR<Prisma.OpportunityTypeScalarRelationFilter, Prisma.OpportunityTypeWhereInput>
|
||||
type?: Prisma.XOR<Prisma.OpportunityTypeNullableScalarRelationFilter, Prisma.OpportunityTypeWhereInput> | null
|
||||
stage?: Prisma.XOR<Prisma.OpportunityStageNullableScalarRelationFilter, Prisma.OpportunityStageWhereInput> | null
|
||||
status?: Prisma.XOR<Prisma.OpportunityStatusNullableScalarRelationFilter, Prisma.OpportunityStatusWhereInput> | null
|
||||
taxCode?: Prisma.XOR<Prisma.TaxCodeNullableScalarRelationFilter, Prisma.TaxCodeWhereInput> | null
|
||||
@@ -582,7 +582,7 @@ export type OpportunityOrderByWithAggregationInput = {
|
||||
name?: Prisma.SortOrder
|
||||
notes?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
oppNarrative?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
typeId?: Prisma.SortOrder
|
||||
typeId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
stageId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
statusId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
taxCodeId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
@@ -624,7 +624,7 @@ export type OpportunityScalarWhereWithAggregatesInput = {
|
||||
name?: Prisma.StringWithAggregatesFilter<"Opportunity"> | string
|
||||
notes?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null
|
||||
oppNarrative?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null
|
||||
typeId?: Prisma.IntWithAggregatesFilter<"Opportunity"> | number
|
||||
typeId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null
|
||||
stageId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null
|
||||
statusId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null
|
||||
taxCodeId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null
|
||||
@@ -674,7 +674,7 @@ export type OpportunityCreateInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -694,7 +694,7 @@ export type OpportunityUncheckedCreateInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -746,7 +746,7 @@ export type OpportunityUpdateInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -766,7 +766,7 @@ export type OpportunityUncheckedUpdateInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -802,7 +802,7 @@ export type OpportunityCreateManyInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -859,7 +859,7 @@ export type OpportunityUncheckedUpdateManyInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -1572,7 +1572,7 @@ export type OpportunityCreateWithoutPrimarySalesRepInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -1591,7 +1591,7 @@ export type OpportunityUncheckedCreateWithoutPrimarySalesRepInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -1652,7 +1652,7 @@ export type OpportunityCreateWithoutSecondarySalesRepInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -1671,7 +1671,7 @@ export type OpportunityUncheckedCreateWithoutSecondarySalesRepInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -1735,7 +1735,7 @@ export type OpportunityScalarWhereInput = {
|
||||
name?: Prisma.StringFilter<"Opportunity"> | string
|
||||
notes?: Prisma.StringNullableFilter<"Opportunity"> | string | null
|
||||
oppNarrative?: Prisma.StringNullableFilter<"Opportunity"> | string | null
|
||||
typeId?: Prisma.IntFilter<"Opportunity"> | number
|
||||
typeId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
stageId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
statusId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
taxCodeId?: Prisma.IntNullableFilter<"Opportunity"> | number | null
|
||||
@@ -1801,7 +1801,7 @@ export type OpportunityCreateWithoutLocationInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -1820,7 +1820,7 @@ export type OpportunityUncheckedCreateWithoutLocationInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -1897,7 +1897,7 @@ export type OpportunityCreateWithoutDepartmentInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -1916,7 +1916,7 @@ export type OpportunityUncheckedCreateWithoutDepartmentInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -1993,7 +1993,7 @@ export type OpportunityCreateWithoutCompanyInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -2012,7 +2012,7 @@ export type OpportunityUncheckedCreateWithoutCompanyInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -2089,7 +2089,7 @@ export type OpportunityCreateWithoutSiteInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -2108,7 +2108,7 @@ export type OpportunityUncheckedCreateWithoutSiteInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -2185,7 +2185,7 @@ export type OpportunityCreateWithoutContactInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -2204,7 +2204,7 @@ export type OpportunityUncheckedCreateWithoutContactInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -2281,7 +2281,7 @@ export type OpportunityCreateWithoutProductsInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -2300,7 +2300,7 @@ export type OpportunityUncheckedCreateWithoutProductsInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -2367,7 +2367,7 @@ export type OpportunityUpdateWithoutProductsInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -2386,7 +2386,7 @@ export type OpportunityUncheckedUpdateWithoutProductsInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -2437,7 +2437,7 @@ export type OpportunityCreateWithoutStageInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -2456,7 +2456,7 @@ export type OpportunityUncheckedCreateWithoutStageInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
interest?: $Enums.OpportunityInterest | null
|
||||
@@ -2629,7 +2629,7 @@ export type OpportunityCreateWithoutStatusInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -2648,7 +2648,7 @@ export type OpportunityUncheckedCreateWithoutStatusInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
taxCodeId?: number | null
|
||||
interest?: $Enums.OpportunityInterest | null
|
||||
@@ -2724,7 +2724,7 @@ export type OpportunityCreateWithoutGeneratedQuotesInput = {
|
||||
eneteredBy: string
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -2744,7 +2744,7 @@ export type OpportunityUncheckedCreateWithoutGeneratedQuotesInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -2810,7 +2810,7 @@ export type OpportunityUpdateWithoutGeneratedQuotesInput = {
|
||||
eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -2830,7 +2830,7 @@ export type OpportunityUncheckedUpdateWithoutGeneratedQuotesInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -2881,7 +2881,7 @@ export type OpportunityCreateWithoutTaxCodeInput = {
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput
|
||||
type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
type?: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput
|
||||
stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput
|
||||
status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput
|
||||
primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput
|
||||
@@ -2900,7 +2900,7 @@ export type OpportunityUncheckedCreateWithoutTaxCodeInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
interest?: $Enums.OpportunityInterest | null
|
||||
@@ -2961,7 +2961,7 @@ export type OpportunityCreateManyPrimarySalesRepInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -2994,7 +2994,7 @@ export type OpportunityCreateManySecondarySalesRepInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -3043,7 +3043,7 @@ export type OpportunityUpdateWithoutPrimarySalesRepInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -3062,7 +3062,7 @@ export type OpportunityUncheckedUpdateWithoutPrimarySalesRepInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3097,7 +3097,7 @@ export type OpportunityUncheckedUpdateManyWithoutPrimarySalesRepInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3146,7 +3146,7 @@ export type OpportunityUpdateWithoutSecondarySalesRepInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -3165,7 +3165,7 @@ export type OpportunityUncheckedUpdateWithoutSecondarySalesRepInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3200,7 +3200,7 @@ export type OpportunityUncheckedUpdateManyWithoutSecondarySalesRepInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3233,7 +3233,7 @@ export type OpportunityCreateManyLocationInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -3282,7 +3282,7 @@ export type OpportunityUpdateWithoutLocationInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -3301,7 +3301,7 @@ export type OpportunityUncheckedUpdateWithoutLocationInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3336,7 +3336,7 @@ export type OpportunityUncheckedUpdateManyWithoutLocationInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3369,7 +3369,7 @@ export type OpportunityCreateManyDepartmentInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -3418,7 +3418,7 @@ export type OpportunityUpdateWithoutDepartmentInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -3437,7 +3437,7 @@ export type OpportunityUncheckedUpdateWithoutDepartmentInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3472,7 +3472,7 @@ export type OpportunityUncheckedUpdateManyWithoutDepartmentInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3505,7 +3505,7 @@ export type OpportunityCreateManyCompanyInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -3554,7 +3554,7 @@ export type OpportunityUpdateWithoutCompanyInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -3573,7 +3573,7 @@ export type OpportunityUncheckedUpdateWithoutCompanyInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3608,7 +3608,7 @@ export type OpportunityUncheckedUpdateManyWithoutCompanyInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3641,7 +3641,7 @@ export type OpportunityCreateManySiteInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -3690,7 +3690,7 @@ export type OpportunityUpdateWithoutSiteInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -3709,7 +3709,7 @@ export type OpportunityUncheckedUpdateWithoutSiteInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3744,7 +3744,7 @@ export type OpportunityUncheckedUpdateManyWithoutSiteInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3777,7 +3777,7 @@ export type OpportunityCreateManyContactInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
@@ -3826,7 +3826,7 @@ export type OpportunityUpdateWithoutContactInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -3845,7 +3845,7 @@ export type OpportunityUncheckedUpdateWithoutContactInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3880,7 +3880,7 @@ export type OpportunityUncheckedUpdateManyWithoutContactInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
@@ -3913,7 +3913,7 @@ export type OpportunityCreateManyStageInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
statusId?: number | null
|
||||
taxCodeId?: number | null
|
||||
interest?: $Enums.OpportunityInterest | null
|
||||
@@ -3962,7 +3962,7 @@ export type OpportunityUpdateWithoutStageInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -3981,7 +3981,7 @@ export type OpportunityUncheckedUpdateWithoutStageInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null
|
||||
@@ -4016,7 +4016,7 @@ export type OpportunityUncheckedUpdateManyWithoutStageInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null
|
||||
@@ -4185,7 +4185,7 @@ export type OpportunityCreateManyStatusInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
taxCodeId?: number | null
|
||||
interest?: $Enums.OpportunityInterest | null
|
||||
@@ -4234,7 +4234,7 @@ export type OpportunityUpdateWithoutStatusInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput
|
||||
primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -4253,7 +4253,7 @@ export type OpportunityUncheckedUpdateWithoutStatusInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null
|
||||
@@ -4288,7 +4288,7 @@ export type OpportunityUncheckedUpdateManyWithoutStatusInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null
|
||||
@@ -4321,7 +4321,7 @@ export type OpportunityCreateManyTaxCodeInput = {
|
||||
name: string
|
||||
notes?: string | null
|
||||
oppNarrative?: string | null
|
||||
typeId: number
|
||||
typeId?: number | null
|
||||
stageId?: number | null
|
||||
statusId?: number | null
|
||||
interest?: $Enums.OpportunityInterest | null
|
||||
@@ -4370,7 +4370,7 @@ export type OpportunityUpdateWithoutTaxCodeInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput
|
||||
type?: Prisma.OpportunityTypeUpdateOneWithoutOpportunitiesNestedInput
|
||||
stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput
|
||||
status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput
|
||||
primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput
|
||||
@@ -4389,7 +4389,7 @@ export type OpportunityUncheckedUpdateWithoutTaxCodeInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null
|
||||
@@ -4424,7 +4424,7 @@ export type OpportunityUncheckedUpdateManyWithoutTaxCodeInput = {
|
||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
typeId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
typeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||
interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null
|
||||
@@ -4524,7 +4524,7 @@ export type OpportunitySelect<ExtArgs extends runtime.Types.Extensions.InternalA
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
generatedQuotes?: boolean | Prisma.Opportunity$generatedQuotesArgs<ExtArgs>
|
||||
type?: boolean | Prisma.OpportunityTypeDefaultArgs<ExtArgs>
|
||||
type?: boolean | Prisma.Opportunity$typeArgs<ExtArgs>
|
||||
stage?: boolean | Prisma.Opportunity$stageArgs<ExtArgs>
|
||||
status?: boolean | Prisma.Opportunity$statusArgs<ExtArgs>
|
||||
taxCode?: boolean | Prisma.Opportunity$taxCodeArgs<ExtArgs>
|
||||
@@ -4571,7 +4571,7 @@ export type OpportunitySelectCreateManyAndReturn<ExtArgs extends runtime.Types.E
|
||||
eneteredBy?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
type?: boolean | Prisma.OpportunityTypeDefaultArgs<ExtArgs>
|
||||
type?: boolean | Prisma.Opportunity$typeArgs<ExtArgs>
|
||||
stage?: boolean | Prisma.Opportunity$stageArgs<ExtArgs>
|
||||
status?: boolean | Prisma.Opportunity$statusArgs<ExtArgs>
|
||||
taxCode?: boolean | Prisma.Opportunity$taxCodeArgs<ExtArgs>
|
||||
@@ -4616,7 +4616,7 @@ export type OpportunitySelectUpdateManyAndReturn<ExtArgs extends runtime.Types.E
|
||||
eneteredBy?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
type?: boolean | Prisma.OpportunityTypeDefaultArgs<ExtArgs>
|
||||
type?: boolean | Prisma.Opportunity$typeArgs<ExtArgs>
|
||||
stage?: boolean | Prisma.Opportunity$stageArgs<ExtArgs>
|
||||
status?: boolean | Prisma.Opportunity$statusArgs<ExtArgs>
|
||||
taxCode?: boolean | Prisma.Opportunity$taxCodeArgs<ExtArgs>
|
||||
@@ -4666,7 +4666,7 @@ export type OpportunitySelectScalar = {
|
||||
export type OpportunityOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "uid" | "name" | "notes" | "oppNarrative" | "typeId" | "stageId" | "statusId" | "taxCodeId" | "interest" | "probability" | "source" | "primarySalesRepId" | "secondarySalesRepId" | "companyId" | "contactId" | "siteId" | "customerPO" | "locationId" | "departmentId" | "expectedCloseDate" | "pipelineChangeDate" | "dateBecameLead" | "closedDate" | "closedFlag" | "closedById" | "productSequence" | "updatedBy" | "eneteredBy" | "createdAt" | "updatedAt", ExtArgs["result"]["opportunity"]>
|
||||
export type OpportunityInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
generatedQuotes?: boolean | Prisma.Opportunity$generatedQuotesArgs<ExtArgs>
|
||||
type?: boolean | Prisma.OpportunityTypeDefaultArgs<ExtArgs>
|
||||
type?: boolean | Prisma.Opportunity$typeArgs<ExtArgs>
|
||||
stage?: boolean | Prisma.Opportunity$stageArgs<ExtArgs>
|
||||
status?: boolean | Prisma.Opportunity$statusArgs<ExtArgs>
|
||||
taxCode?: boolean | Prisma.Opportunity$taxCodeArgs<ExtArgs>
|
||||
@@ -4681,7 +4681,7 @@ export type OpportunityInclude<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
_count?: boolean | Prisma.OpportunityCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type OpportunityIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
type?: boolean | Prisma.OpportunityTypeDefaultArgs<ExtArgs>
|
||||
type?: boolean | Prisma.Opportunity$typeArgs<ExtArgs>
|
||||
stage?: boolean | Prisma.Opportunity$stageArgs<ExtArgs>
|
||||
status?: boolean | Prisma.Opportunity$statusArgs<ExtArgs>
|
||||
taxCode?: boolean | Prisma.Opportunity$taxCodeArgs<ExtArgs>
|
||||
@@ -4694,7 +4694,7 @@ export type OpportunityIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.
|
||||
department?: boolean | Prisma.Opportunity$departmentArgs<ExtArgs>
|
||||
}
|
||||
export type OpportunityIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
type?: boolean | Prisma.OpportunityTypeDefaultArgs<ExtArgs>
|
||||
type?: boolean | Prisma.Opportunity$typeArgs<ExtArgs>
|
||||
stage?: boolean | Prisma.Opportunity$stageArgs<ExtArgs>
|
||||
status?: boolean | Prisma.Opportunity$statusArgs<ExtArgs>
|
||||
taxCode?: boolean | Prisma.Opportunity$taxCodeArgs<ExtArgs>
|
||||
@@ -4711,7 +4711,7 @@ export type $OpportunityPayload<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
name: "Opportunity"
|
||||
objects: {
|
||||
generatedQuotes: Prisma.$GeneratedQuotesPayload<ExtArgs>[]
|
||||
type: Prisma.$OpportunityTypePayload<ExtArgs>
|
||||
type: Prisma.$OpportunityTypePayload<ExtArgs> | null
|
||||
stage: Prisma.$OpportunityStagePayload<ExtArgs> | null
|
||||
status: Prisma.$OpportunityStatusPayload<ExtArgs> | null
|
||||
taxCode: Prisma.$TaxCodePayload<ExtArgs> | null
|
||||
@@ -4730,7 +4730,7 @@ export type $OpportunityPayload<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
name: string
|
||||
notes: string | null
|
||||
oppNarrative: string | null
|
||||
typeId: number
|
||||
typeId: number | null
|
||||
stageId: number | null
|
||||
statusId: number | null
|
||||
taxCodeId: number | null
|
||||
@@ -5151,7 +5151,7 @@ readonly fields: OpportunityFieldRefs;
|
||||
export interface Prisma__OpportunityClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||
generatedQuotes<T extends Prisma.Opportunity$generatedQuotesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Opportunity$generatedQuotesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$GeneratedQuotesPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||
type<T extends Prisma.OpportunityTypeDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.OpportunityTypeDefaultArgs<ExtArgs>>): Prisma.Prisma__OpportunityTypeClient<runtime.Types.Result.GetResult<Prisma.$OpportunityTypePayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
|
||||
type<T extends Prisma.Opportunity$typeArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Opportunity$typeArgs<ExtArgs>>): Prisma.Prisma__OpportunityTypeClient<runtime.Types.Result.GetResult<Prisma.$OpportunityTypePayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
||||
stage<T extends Prisma.Opportunity$stageArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Opportunity$stageArgs<ExtArgs>>): Prisma.Prisma__OpportunityStageClient<runtime.Types.Result.GetResult<Prisma.$OpportunityStagePayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
||||
status<T extends Prisma.Opportunity$statusArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Opportunity$statusArgs<ExtArgs>>): Prisma.Prisma__OpportunityStatusClient<runtime.Types.Result.GetResult<Prisma.$OpportunityStatusPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
||||
taxCode<T extends Prisma.Opportunity$taxCodeArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Opportunity$taxCodeArgs<ExtArgs>>): Prisma.Prisma__TaxCodeClient<runtime.Types.Result.GetResult<Prisma.$TaxCodePayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
||||
@@ -5647,6 +5647,25 @@ export type Opportunity$generatedQuotesArgs<ExtArgs extends runtime.Types.Extens
|
||||
distinct?: Prisma.GeneratedQuotesScalarFieldEnum | Prisma.GeneratedQuotesScalarFieldEnum[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Opportunity.type
|
||||
*/
|
||||
export type Opportunity$typeArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the OpportunityType
|
||||
*/
|
||||
select?: Prisma.OpportunityTypeSelect<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the OpportunityType
|
||||
*/
|
||||
omit?: Prisma.OpportunityTypeOmit<ExtArgs> | null
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.OpportunityTypeInclude<ExtArgs> | null
|
||||
where?: Prisma.OpportunityTypeWhereInput
|
||||
}
|
||||
|
||||
/**
|
||||
* Opportunity.stage
|
||||
*/
|
||||
|
||||
@@ -1032,8 +1032,8 @@ model Opportunity {
|
||||
|
||||
generatedQuotes GeneratedQuotes[]
|
||||
|
||||
typeId Int
|
||||
type OpportunityType @relation(fields: [typeId], references: [id])
|
||||
typeId Int?
|
||||
type OpportunityType? @relation(fields: [typeId], references: [id])
|
||||
|
||||
stageId Int?
|
||||
stage OpportunityStage? @relation(fields: [stageId], references: [id])
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import { PrismaClient } from './generated/prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// Create the admin role if it doesn't exist
|
||||
const adminRole = await prisma.role.upsert({
|
||||
where: { moniker: 'admin' },
|
||||
update: {},
|
||||
create: {
|
||||
title: 'Administrator',
|
||||
moniker: 'admin',
|
||||
permissions: JSON.stringify({
|
||||
// Full permissions for admin
|
||||
'*': true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✓ Admin role created/verified:', adminRole);
|
||||
|
||||
// Find the user with jackson.roberts@totaltech.net
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { email: 'jackson.roberts@totaltech.net' },
|
||||
include: { roles: true },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
console.error(
|
||||
'✗ User jackson.roberts@totaltech.net not found. Please ensure the user exists.',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('✓ User found:', user.email);
|
||||
|
||||
// Check if user already has admin role
|
||||
const hasAdminRole = user.roles.some((r) => r.moniker === 'admin');
|
||||
|
||||
if (hasAdminRole) {
|
||||
console.log('✓ User already has admin role');
|
||||
} else {
|
||||
// Assign admin role to user
|
||||
const updatedUser = await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: {
|
||||
roles: {
|
||||
connect: { id: adminRole.id },
|
||||
},
|
||||
},
|
||||
include: { roles: true },
|
||||
});
|
||||
|
||||
console.log('✓ Admin role assigned to jackson.roberts@totaltech.net');
|
||||
console.log('✓ User roles:', updatedUser.roles.map((r) => r.moniker));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('✗ Error:', error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -4,6 +4,7 @@ import { companies } from "../../../managers/companies";
|
||||
import { apiResponse } from "../../../modules/api-utils/apiResponse";
|
||||
import { ContentfulStatusCode } from "hono/utils/http-status";
|
||||
import { authMiddleware } from "../../middleware/authorization";
|
||||
import GenericError from "../../../Errors/GenericError";
|
||||
import { processObjectValuePerms } from "../../../modules/permission-utils/processObjectPermissions";
|
||||
|
||||
/* /v1/company/companies/[id] */
|
||||
@@ -12,18 +13,37 @@ export default createRoute(
|
||||
["/companies/:identifier"],
|
||||
|
||||
async (c) => {
|
||||
const company = await companies.fetch(c.req.param("identifier"));
|
||||
const user = c.get("user");
|
||||
const includeAddress =
|
||||
c.req.query("includeAddress") === "true" &&
|
||||
!!user &&
|
||||
(await user.hasPermission("company.fetch.address"));
|
||||
const company = await companies.fetch(c.req.param("identifier") as string);
|
||||
const includeAddress = c.req.query("includeAddress") === "true";
|
||||
const includePrimaryContact =
|
||||
c.req.query("includePrimaryContact") === "true";
|
||||
const includeAllContacts =
|
||||
c.req.query("includeAllContacts") === "true" &&
|
||||
!!user &&
|
||||
(await user.hasPermission("company.fetch.contacts"));
|
||||
const includeAllContacts = c.req.query("includeAllContacts") === "true";
|
||||
|
||||
console.log(company.toJson({ includeAddress, includePrimaryContact, includeAllContacts }));
|
||||
|
||||
// Check for address-specific permission if includeAddress is requested
|
||||
if (includeAddress) {
|
||||
const user = c.get("user");
|
||||
if (!user || !(await user.hasPermission("company.fetch.address"))) {
|
||||
throw new GenericError({
|
||||
name: "InsufficientPermission",
|
||||
message: "You do not have permission to view company addresses.",
|
||||
status: 403,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check for contacts permission if includeAllContacts is requested
|
||||
if (includeAllContacts) {
|
||||
const user = c.get("user");
|
||||
if (!user || !(await user.hasPermission("company.fetch.contacts"))) {
|
||||
throw new GenericError({
|
||||
name: "InsufficientPermission",
|
||||
message: "You do not have permission to view company contacts.",
|
||||
status: 403,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const companyData = company.toJson({
|
||||
includeAddress,
|
||||
@@ -36,13 +56,6 @@ export default createRoute(
|
||||
c.get("user"),
|
||||
);
|
||||
|
||||
// cw_Data fields were already gated by the explicit permission checks above
|
||||
// (company.fetch.contacts / company.fetch.address). Re-attach them so they
|
||||
// are not silently dropped by field-level gating on obj.company.cw_Data.
|
||||
if (companyData.cw_Data && Object.keys(companyData.cw_Data).length > 0) {
|
||||
(gatedData as any).cw_Data = companyData.cw_Data;
|
||||
}
|
||||
|
||||
const response = apiResponse.successful(
|
||||
"Company Fetched Successfully!",
|
||||
gatedData,
|
||||
|
||||
@@ -274,7 +274,7 @@ export class CompanyController {
|
||||
(ci) => ci.type?.name === "Email"
|
||||
);
|
||||
return {
|
||||
id: contact.id,
|
||||
cwId: contact.id,
|
||||
firstName: contact.firstName,
|
||||
lastName: contact.lastName,
|
||||
inactive: contact.inactiveFlag ?? false,
|
||||
@@ -303,16 +303,41 @@ export class CompanyController {
|
||||
};
|
||||
}
|
||||
|
||||
if (opts?.includePrimaryContact && this.cw_Data?.defaultContact) {
|
||||
cw_Data.primaryContact = this._serializeContact(
|
||||
this.cw_Data.defaultContact
|
||||
);
|
||||
if (opts?.includePrimaryContact) {
|
||||
if (this.cw_Data?.defaultContact) {
|
||||
cw_Data.primaryContact = this._serializeContact(
|
||||
this.cw_Data.defaultContact
|
||||
);
|
||||
} else if (this._defaultContact) {
|
||||
const c = this._defaultContact;
|
||||
cw_Data.primaryContact = {
|
||||
cwId: c.id,
|
||||
firstName: c.firstName,
|
||||
lastName: c.lastName,
|
||||
inactive: !c.active,
|
||||
title: c.title ?? null,
|
||||
phone: c.phone ?? null,
|
||||
email: c.email ?? null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (opts?.includeAllContacts && this.cw_Data?.allContacts) {
|
||||
cw_Data.allContacts = this.cw_Data.allContacts.map((c) =>
|
||||
this._serializeContact(c)
|
||||
);
|
||||
if (opts?.includeAllContacts) {
|
||||
if (this.cw_Data?.allContacts) {
|
||||
cw_Data.allContacts = this.cw_Data.allContacts.map((c) =>
|
||||
this._serializeContact(c)
|
||||
);
|
||||
} else if (this._contacts.length > 0) {
|
||||
cw_Data.allContacts = this._contacts.map((c) => ({
|
||||
cwId: c.id,
|
||||
firstName: c.firstName,
|
||||
lastName: c.lastName,
|
||||
inactive: !c.active,
|
||||
title: c.title ?? null,
|
||||
phone: c.phone ?? null,
|
||||
email: c.email ?? null,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -46,7 +46,7 @@ export const opportunities = {
|
||||
|
||||
// Resolve optional local FKs — nullify any that don't exist locally yet
|
||||
// (the sync may be behind; these are all nullable in the schema)
|
||||
const [companyExists, contactExists, siteExists] = await Promise.all([
|
||||
const [companyExists, contactExists, siteExists, typeExists] = await Promise.all([
|
||||
cwData.company?.id
|
||||
? prisma.company.findFirst({ where: { id: cwData.company.id }, select: { id: true } })
|
||||
: null,
|
||||
@@ -56,16 +56,21 @@ export const opportunities = {
|
||||
mapped.siteId != null
|
||||
? prisma.companyAddress.findFirst({ where: { id: mapped.siteId }, select: { id: true } })
|
||||
: null,
|
||||
mapped.typeId != null
|
||||
? prisma.opportunityType.findFirst({ where: { id: mapped.typeId }, select: { id: true } })
|
||||
: null,
|
||||
]);
|
||||
|
||||
const companyId = companyExists?.id ?? null;
|
||||
const contactId = contactExists?.id ?? null;
|
||||
const siteId = siteExists?.id ?? null;
|
||||
const typeId = typeExists?.id ?? null;
|
||||
|
||||
const record = await prisma.opportunity.create({
|
||||
data: {
|
||||
id: cwData.id,
|
||||
...mapped,
|
||||
typeId,
|
||||
companyId,
|
||||
contactId,
|
||||
siteId,
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import axios from "axios";
|
||||
|
||||
const connectWiseApi = axios.create({
|
||||
baseURL: `https://ttscw.totaltech.net/v4_6_release/apis/3.0/`,
|
||||
headers: {
|
||||
Authorization: `Basic ${process.env.CW_BASIC_TOKEN}`,
|
||||
clientId: `${process.env.CW_CLIENT_ID}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
async function main() {
|
||||
// Fetch inactive catalog items
|
||||
const pageSize = 1000;
|
||||
let page = 1;
|
||||
const inactiveItems: any[] = [];
|
||||
|
||||
while (true) {
|
||||
const response = await connectWiseApi.get(
|
||||
`/procurement/catalog?page=${page}&pageSize=${pageSize}&conditions=inactiveFlag=true&fields=id,identifier,description,_info`,
|
||||
);
|
||||
if (response.data.length === 0) break;
|
||||
inactiveItems.push(...response.data);
|
||||
page++;
|
||||
}
|
||||
|
||||
console.log(`Found ${inactiveItems.length} inactive catalog items`);
|
||||
console.log(`Checking inventory for each (batches of 50)...\n`);
|
||||
|
||||
const withStock: any[] = [];
|
||||
const batchSize = 50;
|
||||
|
||||
for (let i = 0; i < inactiveItems.length; i += batchSize) {
|
||||
const batch = inactiveItems.slice(i, i + batchSize);
|
||||
await Promise.all(
|
||||
batch.map(async (item) => {
|
||||
try {
|
||||
const res = await connectWiseApi.get(
|
||||
`/procurement/catalog/${item.id}/inventory?fields=onHand`,
|
||||
);
|
||||
const totalOnHand = (res.data as { onHand: number }[]).reduce(
|
||||
(sum, e) => sum + (e.onHand || 0),
|
||||
0,
|
||||
);
|
||||
if (totalOnHand > 0) {
|
||||
withStock.push({
|
||||
id: item.id,
|
||||
identifier: item.identifier,
|
||||
description: item.description,
|
||||
totalOnHand,
|
||||
});
|
||||
}
|
||||
} catch {}
|
||||
}),
|
||||
);
|
||||
const done = Math.min(i + batchSize, inactiveItems.length);
|
||||
if (done % 500 === 0 || done === inactiveItems.length) {
|
||||
console.log(` ${done}/${inactiveItems.length} checked`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`\nInactive items with inventory: ${withStock.length}/${inactiveItems.length}\n`,
|
||||
);
|
||||
|
||||
if (withStock.length > 0) {
|
||||
console.log(JSON.stringify(withStock, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,600 +0,0 @@
|
||||
/**
|
||||
* Test Script: CW Forecast Item Edit & Partial Cancellation
|
||||
*
|
||||
* This script performs read-write operations against the ConnectWise API:
|
||||
*
|
||||
* 1. Search all open opportunities for a forecast item with description
|
||||
* matching "labor Special Order" (case-insensitive).
|
||||
* 2. Report the current state of that item (price, cost, qty, etc.).
|
||||
* 3. PATCH the item: revenue → 72,000 | cost → 8,500 | quantity → 67
|
||||
* 4. Verify the update by re-fetching the forecast.
|
||||
* 5. Cancel 13 units via the linked procurement product
|
||||
* (partial cancellation: quantityCancelled = 13).
|
||||
* 6. Verify the cancellation by re-fetching procurement data.
|
||||
* 7. Report on every step.
|
||||
*
|
||||
* Usage: bun run test-cw-edit-item.ts
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
|
||||
const cw = axios.create({
|
||||
baseURL: "https://ttscw.totaltech.net/v4_6_release/apis/3.0/",
|
||||
headers: {
|
||||
Authorization: `Basic ${process.env.CW_BASIC_TOKEN}`,
|
||||
clientId: `${process.env.CW_CLIENT_ID}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
timeout: 30_000,
|
||||
});
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
const log = (label: string, ...args: unknown[]) =>
|
||||
console.log(`\n[${label}]`, ...args);
|
||||
|
||||
const divider = () => console.log("─".repeat(72));
|
||||
|
||||
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
const fmt = (n: number) =>
|
||||
n.toLocaleString("en-US", {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
|
||||
// ── Types (minimal, for this script) ──────────────────────────────────────────
|
||||
|
||||
interface ForecastItem {
|
||||
id: number;
|
||||
forecastDescription: string;
|
||||
productDescription: string;
|
||||
quantity: number;
|
||||
revenue: number;
|
||||
cost: number;
|
||||
margin: number;
|
||||
forecastType: string;
|
||||
sequenceNumber: number;
|
||||
catalogItem?: { id: number; identifier: string };
|
||||
status?: { id: number; name: string };
|
||||
opportunity?: { id: number; name: string };
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface Forecast {
|
||||
id: number;
|
||||
forecastItems: ForecastItem[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface ProcurementProduct {
|
||||
id: number;
|
||||
forecastDetailId: number;
|
||||
description: string;
|
||||
quantity: number;
|
||||
price: number;
|
||||
cost: number;
|
||||
cancelledFlag: boolean;
|
||||
quantityCancelled: number;
|
||||
cancelledReason: string | null;
|
||||
cancelledBy: string | null;
|
||||
cancelledDate: string | null;
|
||||
opportunity?: { id: number };
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
// ── Main ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
async function main() {
|
||||
divider();
|
||||
log("START", "CW Forecast Item Edit & Cancellation Test");
|
||||
log("START", `Timestamp: ${new Date().toISOString()}`);
|
||||
divider();
|
||||
|
||||
// ── Step 1: Find the "labor Special Order" forecast item ────────────────
|
||||
|
||||
const OPP_ID = 5150;
|
||||
log(
|
||||
"SEARCH",
|
||||
`Looking for forecast item matching "labor Special Order" on opportunity ${OPP_ID}...`,
|
||||
);
|
||||
|
||||
// Fetch the forecast for opportunity 5150 directly
|
||||
let targetOppId: number = OPP_ID;
|
||||
let targetItem: ForecastItem | null = null;
|
||||
let targetForecast: Forecast | null = null;
|
||||
|
||||
const forecastRes = await cw.get(`/sales/opportunities/${OPP_ID}/forecast`);
|
||||
targetForecast = forecastRes.data as Forecast;
|
||||
const match = (targetForecast.forecastItems ?? []).find(
|
||||
(fi: ForecastItem) =>
|
||||
fi.forecastDescription?.toLowerCase().includes("special order") ||
|
||||
fi.productDescription?.toLowerCase().includes("special order"),
|
||||
);
|
||||
|
||||
if (match) {
|
||||
targetItem = match;
|
||||
log("SEARCH", `✓ FOUND forecast item on opportunity ${OPP_ID}`);
|
||||
}
|
||||
|
||||
if (!targetItem || !targetForecast) {
|
||||
log(
|
||||
"SEARCH",
|
||||
`✗ No "labor Special Order" item found on opportunity ${OPP_ID}.`,
|
||||
);
|
||||
log("SEARCH", "All forecast items on this opportunity:");
|
||||
for (const fi of targetForecast.forecastItems ?? []) {
|
||||
console.log(
|
||||
` id=${fi.id} "${fi.forecastDescription}" / "${fi.productDescription}"`,
|
||||
);
|
||||
}
|
||||
log("SEARCH", "Aborting.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ── Step 2: Report current state ────────────────────────────────────────
|
||||
|
||||
divider();
|
||||
log("CURRENT STATE", "Forecast item details BEFORE edit:");
|
||||
console.log(` Opportunity ID: ${targetOppId}`);
|
||||
console.log(` Forecast Item ID: ${targetItem.id}`);
|
||||
console.log(` Forecast Description: ${targetItem.forecastDescription}`);
|
||||
console.log(` Product Description: ${targetItem.productDescription}`);
|
||||
console.log(
|
||||
` Catalog Item: ${targetItem.catalogItem?.identifier ?? "(none)"} (cwId=${targetItem.catalogItem?.id ?? "N/A"})`,
|
||||
);
|
||||
console.log(` Forecast Type: ${targetItem.forecastType}`);
|
||||
console.log(
|
||||
` Status: ${targetItem.status?.name ?? "?"} (id=${targetItem.status?.id ?? "?"})`,
|
||||
);
|
||||
console.log(` Sequence Number: ${targetItem.sequenceNumber}`);
|
||||
console.log(` ──────────────────────────────────`);
|
||||
console.log(` Quantity: ${targetItem.quantity}`);
|
||||
console.log(` Revenue (Price): $${fmt(targetItem.revenue)}`);
|
||||
console.log(` Cost: $${fmt(targetItem.cost)}`);
|
||||
console.log(` Margin: $${fmt(targetItem.margin)}`);
|
||||
|
||||
// Also report all items on this opportunity for context
|
||||
const allItems = targetForecast.forecastItems ?? [];
|
||||
log(
|
||||
"CONTEXT",
|
||||
`Total forecast items on this opportunity: ${allItems.length}`,
|
||||
);
|
||||
for (const fi of allItems) {
|
||||
const marker = fi.id === targetItem.id ? " ◀ TARGET" : "";
|
||||
console.log(
|
||||
` [${fi.sequenceNumber}] id=${fi.id} "${fi.forecastDescription}" ` +
|
||||
`qty=${fi.quantity} rev=$${fmt(fi.revenue)} cost=$${fmt(fi.cost)}${marker}`,
|
||||
);
|
||||
}
|
||||
|
||||
// ── Step 3: PATCH the forecast item ─────────────────────────────────────
|
||||
|
||||
divider();
|
||||
const UNIT_PRICE = 72_000;
|
||||
const UNIT_COST = 8_500;
|
||||
const QTY = 67;
|
||||
const TOTAL_REVENUE = UNIT_PRICE * QTY; // $4,824,000
|
||||
const TOTAL_COST = UNIT_COST * QTY; // $569,500
|
||||
|
||||
log("EDIT", "Patching forecast item...");
|
||||
log(
|
||||
"EDIT",
|
||||
` Unit price: $${fmt(UNIT_PRICE)} × ${QTY} = $${fmt(TOTAL_REVENUE)} (revenue)`,
|
||||
);
|
||||
log(
|
||||
"EDIT",
|
||||
` Unit cost: $${fmt(UNIT_COST)} × ${QTY} = $${fmt(TOTAL_COST)} (cost)`,
|
||||
);
|
||||
log("EDIT", ` Quantity: ${QTY}`);
|
||||
|
||||
// Find the index of our target item in the forecast array
|
||||
const forecastItems = targetForecast.forecastItems ?? [];
|
||||
const targetIdx = forecastItems.findIndex((fi) => fi.id === targetItem!.id);
|
||||
|
||||
if (targetIdx === -1) {
|
||||
log(
|
||||
"EDIT",
|
||||
"✗ Could not find target item index in forecast array. Aborting.",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
log("EDIT", `Target item is at index ${targetIdx} in forecastItems array.`);
|
||||
|
||||
const patchOps = [
|
||||
{
|
||||
op: "replace",
|
||||
path: `/forecastItems/${targetIdx}/revenue`,
|
||||
value: TOTAL_REVENUE,
|
||||
},
|
||||
{
|
||||
op: "replace",
|
||||
path: `/forecastItems/${targetIdx}/cost`,
|
||||
value: TOTAL_COST,
|
||||
},
|
||||
{ op: "replace", path: `/forecastItems/${targetIdx}/quantity`, value: QTY },
|
||||
];
|
||||
|
||||
log("EDIT", "Patch operations:");
|
||||
for (const op of patchOps) {
|
||||
console.log(` ${op.op} ${op.path} → ${op.value}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const patchRes = await cw.patch(
|
||||
`/sales/opportunities/${targetOppId}/forecast`,
|
||||
patchOps,
|
||||
);
|
||||
const updatedForecast: Forecast = patchRes.data;
|
||||
const updatedItem = (updatedForecast.forecastItems ?? [])[targetIdx];
|
||||
|
||||
if (!updatedItem) {
|
||||
log("EDIT", "✗ Item not found at expected index after PATCH.");
|
||||
} else {
|
||||
log("EDIT", "✓ PATCH successful. Updated item:");
|
||||
console.log(` Forecast Item ID: ${updatedItem.id}`);
|
||||
console.log(` Forecast Description: ${updatedItem.forecastDescription}`);
|
||||
console.log(` Quantity: ${updatedItem.quantity}`);
|
||||
console.log(` Revenue (Price): $${fmt(updatedItem.revenue)}`);
|
||||
console.log(` Cost: $${fmt(updatedItem.cost)}`);
|
||||
console.log(` Margin: $${fmt(updatedItem.margin)}`);
|
||||
|
||||
// Verify values match what we set
|
||||
const checks = [
|
||||
{
|
||||
field: "revenue",
|
||||
expected: TOTAL_REVENUE,
|
||||
actual: updatedItem.revenue,
|
||||
},
|
||||
{ field: "cost", expected: TOTAL_COST, actual: updatedItem.cost },
|
||||
{ field: "quantity", expected: QTY, actual: updatedItem.quantity },
|
||||
];
|
||||
|
||||
log("VERIFY EDIT", "Checking values match requested:");
|
||||
for (const check of checks) {
|
||||
const ok = check.actual === check.expected;
|
||||
console.log(
|
||||
` ${ok ? "✓" : "✗"} ${check.field}: expected=${check.expected}, actual=${check.actual}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Update our reference for the cancellation step
|
||||
targetItem = updatedItem;
|
||||
}
|
||||
} catch (err: any) {
|
||||
log("EDIT", `✗ PATCH failed: ${err.response?.status ?? err.message}`);
|
||||
if (err.response?.data) {
|
||||
console.log(" Response:", JSON.stringify(err.response.data, null, 2));
|
||||
}
|
||||
|
||||
// If quantity PATCH failed (read-only), try without quantity
|
||||
if (err.response?.status === 400 || err.response?.status === 422) {
|
||||
log(
|
||||
"EDIT",
|
||||
"Retrying without quantity (may be read-only on forecast items)...",
|
||||
);
|
||||
const retryOps = patchOps.filter((op) => !op.path.endsWith("/quantity"));
|
||||
try {
|
||||
const retryRes = await cw.patch(
|
||||
`/sales/opportunities/${targetOppId}/forecast`,
|
||||
retryOps,
|
||||
);
|
||||
const retryForecast: Forecast = retryRes.data;
|
||||
const retryItem = (retryForecast.forecastItems ?? [])[targetIdx];
|
||||
|
||||
if (retryItem) {
|
||||
log(
|
||||
"EDIT",
|
||||
"✓ Retry PATCH successful (without quantity). Updated item:",
|
||||
);
|
||||
console.log(
|
||||
` Quantity: ${retryItem.quantity} (unchanged — read-only)`,
|
||||
);
|
||||
console.log(` Revenue (Price): $${fmt(retryItem.revenue)}`);
|
||||
console.log(` Cost: $${fmt(retryItem.cost)}`);
|
||||
console.log(` Margin: $${fmt(retryItem.margin)}`);
|
||||
targetItem = retryItem;
|
||||
}
|
||||
} catch (retryErr: any) {
|
||||
log(
|
||||
"EDIT",
|
||||
`✗ Retry also failed: ${retryErr.response?.status ?? retryErr.message}`,
|
||||
);
|
||||
if (retryErr.response?.data) {
|
||||
console.log(
|
||||
" Response:",
|
||||
JSON.stringify(retryErr.response.data, null, 2),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Step 4: Re-fetch and confirm final forecast state ───────────────────
|
||||
|
||||
divider();
|
||||
log("RE-FETCH", "Fetching forecast to confirm final state...");
|
||||
await sleep(500);
|
||||
|
||||
const confirmRes = await cw.get(
|
||||
`/sales/opportunities/${targetOppId}/forecast`,
|
||||
);
|
||||
const confirmedForecast: Forecast = confirmRes.data;
|
||||
const confirmedItem = (confirmedForecast.forecastItems ?? []).find(
|
||||
(fi) => fi.id === targetItem!.id,
|
||||
);
|
||||
|
||||
if (confirmedItem) {
|
||||
log("CONFIRMED STATE", "Forecast item after edit:");
|
||||
console.log(` Forecast Item ID: ${confirmedItem.id}`);
|
||||
console.log(` Forecast Description: ${confirmedItem.forecastDescription}`);
|
||||
console.log(` Quantity: ${confirmedItem.quantity}`);
|
||||
console.log(` Revenue (Price): $${fmt(confirmedItem.revenue)}`);
|
||||
console.log(` Cost: $${fmt(confirmedItem.cost)}`);
|
||||
console.log(` Margin: $${fmt(confirmedItem.margin)}`);
|
||||
} else {
|
||||
log(
|
||||
"CONFIRMED STATE",
|
||||
"⚠ Could not find item by original ID — it may have been regenerated.",
|
||||
);
|
||||
log("CONFIRMED STATE", "All current forecast items:");
|
||||
for (const fi of confirmedForecast.forecastItems ?? []) {
|
||||
console.log(
|
||||
` id=${fi.id} "${fi.forecastDescription}" qty=${fi.quantity} rev=$${fmt(fi.revenue)} cost=$${fmt(fi.cost)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Step 5: Cancel 13 items via procurement product ─────────────────────
|
||||
|
||||
divider();
|
||||
log("CANCEL", "Cancelling 13 units on this item via procurement product...");
|
||||
|
||||
// First, find existing procurement products linked to this opportunity
|
||||
const procRes = await cw.get(
|
||||
`/procurement/products?conditions=${encodeURIComponent(`opportunity/id=${targetOppId}`)}&pageSize=1000`,
|
||||
);
|
||||
const procProducts: ProcurementProduct[] = procRes.data;
|
||||
|
||||
log(
|
||||
"CANCEL",
|
||||
`Found ${procProducts.length} procurement product(s) on this opportunity.`,
|
||||
);
|
||||
|
||||
if (procProducts.length > 0) {
|
||||
for (const pp of procProducts) {
|
||||
console.log(
|
||||
` Proc id=${pp.id} forecastDetailId=${pp.forecastDetailId} ` +
|
||||
`"${pp.description}" qty=${pp.quantity} price=$${fmt(pp.price ?? 0)} ` +
|
||||
`cancelled=${pp.cancelledFlag} qtyCancelled=${pp.quantityCancelled}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the procurement product linked to our forecast item
|
||||
const linkedProc = procProducts.find(
|
||||
(pp) => pp.forecastDetailId === targetItem!.id,
|
||||
);
|
||||
|
||||
if (linkedProc) {
|
||||
log("CANCEL", `Found linked procurement product: id=${linkedProc.id}`);
|
||||
log(
|
||||
"CANCEL",
|
||||
`Current state: cancelled=${linkedProc.cancelledFlag}, quantityCancelled=${linkedProc.quantityCancelled}`,
|
||||
);
|
||||
log("CANCEL", "Patching: quantityCancelled → 13, cancelledFlag → true");
|
||||
|
||||
try {
|
||||
const cancelRes = await cw.patch(
|
||||
`/procurement/products/${linkedProc.id}`,
|
||||
[
|
||||
{ op: "replace", path: "cancelledFlag", value: true },
|
||||
{ op: "replace", path: "quantityCancelled", value: 13 },
|
||||
{
|
||||
op: "replace",
|
||||
path: "cancelledReason",
|
||||
value: "Test cancellation — 13 units",
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
log("CANCEL", "✓ Cancellation PATCH successful.");
|
||||
console.log(` cancelledFlag: ${cancelRes.data.cancelledFlag}`);
|
||||
console.log(` quantityCancelled: ${cancelRes.data.quantityCancelled}`);
|
||||
console.log(` cancelledReason: ${cancelRes.data.cancelledReason}`);
|
||||
console.log(
|
||||
` cancelledBy: ${cancelRes.data.cancelledBy ?? "N/A"}`,
|
||||
);
|
||||
console.log(
|
||||
` cancelledDate: ${cancelRes.data.cancelledDate ?? "N/A"}`,
|
||||
);
|
||||
} catch (err: any) {
|
||||
log(
|
||||
"CANCEL",
|
||||
`✗ Cancellation PATCH failed: ${err.response?.status ?? err.message}`,
|
||||
);
|
||||
if (err.response?.data) {
|
||||
console.log(" Response:", JSON.stringify(err.response.data, null, 2));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log(
|
||||
"CANCEL",
|
||||
`No procurement product linked to forecast item id=${targetItem!.id}.`,
|
||||
);
|
||||
log(
|
||||
"CANCEL",
|
||||
"Creating a procurement product first, then cancelling 13...",
|
||||
);
|
||||
|
||||
try {
|
||||
// Create a procurement product linked to this forecast item
|
||||
const createProcRes = await cw.post("/procurement/products", {
|
||||
catalogItem: targetItem!.catalogItem?.id
|
||||
? { id: targetItem!.catalogItem.id }
|
||||
: undefined,
|
||||
description:
|
||||
targetItem!.forecastDescription || targetItem!.productDescription,
|
||||
quantity: targetItem!.quantity || 67,
|
||||
price: targetItem!.revenue || 72_000,
|
||||
cost: targetItem!.cost || 8_500,
|
||||
billableOption: "Billable",
|
||||
opportunity: { id: targetOppId },
|
||||
forecastDetailId: targetItem!.id,
|
||||
});
|
||||
|
||||
const newProc = createProcRes.data;
|
||||
log("CANCEL", `✓ Created procurement product id=${newProc.id}`);
|
||||
console.log(` forecastDetailId: ${newProc.forecastDetailId}`);
|
||||
console.log(` description: ${newProc.description}`);
|
||||
console.log(` quantity: ${newProc.quantity}`);
|
||||
console.log(` price: $${fmt(newProc.price ?? 0)}`);
|
||||
console.log(` cost: $${fmt(newProc.cost ?? 0)}`);
|
||||
|
||||
// Now cancel 13 units
|
||||
log("CANCEL", "Patching procurement product: quantityCancelled → 13...");
|
||||
const cancelRes = await cw.patch(`/procurement/products/${newProc.id}`, [
|
||||
{ op: "replace", path: "cancelledFlag", value: true },
|
||||
{ op: "replace", path: "quantityCancelled", value: 13 },
|
||||
{
|
||||
op: "replace",
|
||||
path: "cancelledReason",
|
||||
value: "Test cancellation — 13 units",
|
||||
},
|
||||
]);
|
||||
|
||||
log("CANCEL", "✓ Cancellation PATCH successful.");
|
||||
console.log(` cancelledFlag: ${cancelRes.data.cancelledFlag}`);
|
||||
console.log(` quantityCancelled: ${cancelRes.data.quantityCancelled}`);
|
||||
console.log(` cancelledReason: ${cancelRes.data.cancelledReason}`);
|
||||
console.log(
|
||||
` cancelledBy: ${cancelRes.data.cancelledBy ?? "N/A"}`,
|
||||
);
|
||||
console.log(
|
||||
` cancelledDate: ${cancelRes.data.cancelledDate ?? "N/A"}`,
|
||||
);
|
||||
} catch (err: any) {
|
||||
log("CANCEL", `✗ Failed: ${err.response?.status ?? err.message}`);
|
||||
if (err.response?.data) {
|
||||
console.log(" Response:", JSON.stringify(err.response.data, null, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Step 6: Final verification ──────────────────────────────────────────
|
||||
|
||||
divider();
|
||||
log("FINAL VERIFY", "Re-fetching all data for final report...");
|
||||
await sleep(500);
|
||||
|
||||
// Re-fetch forecast
|
||||
const finalForecastRes = await cw.get(
|
||||
`/sales/opportunities/${targetOppId}/forecast`,
|
||||
);
|
||||
const finalForecast: Forecast = finalForecastRes.data;
|
||||
const finalItem =
|
||||
(finalForecast.forecastItems ?? []).find(
|
||||
(fi) => fi.id === targetItem!.id,
|
||||
) ??
|
||||
(finalForecast.forecastItems ?? []).find(
|
||||
(fi) =>
|
||||
fi.forecastDescription?.toLowerCase().includes("special order") ||
|
||||
fi.productDescription?.toLowerCase().includes("special order"),
|
||||
);
|
||||
|
||||
// Re-fetch procurement
|
||||
const finalProcRes = await cw.get(
|
||||
`/procurement/products?conditions=${encodeURIComponent(`opportunity/id=${targetOppId}`)}&pageSize=1000`,
|
||||
);
|
||||
const finalProcs: ProcurementProduct[] = finalProcRes.data;
|
||||
|
||||
log("FINAL STATE — FORECAST ITEM", "");
|
||||
if (finalItem) {
|
||||
console.log(` Forecast Item ID: ${finalItem.id}`);
|
||||
console.log(` Forecast Description: ${finalItem.forecastDescription}`);
|
||||
console.log(` Quantity: ${finalItem.quantity}`);
|
||||
console.log(` Revenue (Price): $${fmt(finalItem.revenue)}`);
|
||||
console.log(` Cost: $${fmt(finalItem.cost)}`);
|
||||
console.log(` Margin: $${fmt(finalItem.margin)}`);
|
||||
} else {
|
||||
console.log(" ⚠ Target item not found in final forecast.");
|
||||
}
|
||||
|
||||
log("FINAL STATE — PROCUREMENT", `${finalProcs.length} product(s):`);
|
||||
for (const pp of finalProcs) {
|
||||
console.log(
|
||||
` id=${pp.id} forecastDetailId=${pp.forecastDetailId} ` +
|
||||
`"${pp.description}" qty=${pp.quantity} cancelled=${pp.cancelledFlag} ` +
|
||||
`qtyCancelled=${pp.quantityCancelled} reason="${pp.cancelledReason ?? ""}"`,
|
||||
);
|
||||
}
|
||||
|
||||
// ── Summary ─────────────────────────────────────────────────────────────
|
||||
|
||||
divider();
|
||||
log("SUMMARY", "");
|
||||
|
||||
// After cancelling 13 of 67, CW recalculates totals for remaining 54 units
|
||||
const expectedFinalRevenue = Math.round(UNIT_PRICE * (QTY - 13) * 100) / 100;
|
||||
const expectedFinalCost = Math.round(UNIT_COST * (QTY - 13) * 100) / 100;
|
||||
|
||||
const editOk = finalItem
|
||||
? Math.abs(finalItem.revenue - expectedFinalRevenue) < 1 &&
|
||||
Math.abs(finalItem.cost - expectedFinalCost) < 1
|
||||
: false;
|
||||
const qtyOk = finalItem ? finalItem.quantity === QTY : false;
|
||||
|
||||
if (finalItem) {
|
||||
console.log(
|
||||
` Expected final revenue ($${fmt(UNIT_PRICE)} × ${QTY - 13}): $${fmt(expectedFinalRevenue)}`,
|
||||
);
|
||||
console.log(
|
||||
` Actual final revenue: $${fmt(finalItem.revenue)}`,
|
||||
);
|
||||
console.log(
|
||||
` Expected final cost ($${fmt(UNIT_COST)} × ${QTY - 13}): $${fmt(expectedFinalCost)}`,
|
||||
);
|
||||
console.log(
|
||||
` Actual final cost: $${fmt(finalItem.cost)}`,
|
||||
);
|
||||
}
|
||||
const cancelOk = finalProcs.some(
|
||||
(pp) =>
|
||||
pp.forecastDetailId === targetItem!.id &&
|
||||
pp.cancelledFlag === true &&
|
||||
pp.quantityCancelled === 13,
|
||||
);
|
||||
|
||||
console.log(
|
||||
` Unit price $${fmt(UNIT_PRICE)}/ea: `,
|
||||
editOk ? "✓ PASS" : "✗ FAIL",
|
||||
);
|
||||
console.log(
|
||||
` Unit cost $${fmt(UNIT_COST)}/ea: `,
|
||||
editOk ? "✓ PASS" : "✗ FAIL",
|
||||
);
|
||||
console.log(
|
||||
` Quantity set to ${QTY}: `,
|
||||
qtyOk ? "✓ PASS" : "✗ FAIL (may be read-only)",
|
||||
);
|
||||
console.log(
|
||||
" 13 units cancelled: ",
|
||||
cancelOk ? "✓ PASS" : "✗ FAIL",
|
||||
);
|
||||
|
||||
const allPass = editOk && qtyOk && cancelOk;
|
||||
divider();
|
||||
log(
|
||||
"RESULT",
|
||||
allPass
|
||||
? "✓ ALL CHECKS PASSED"
|
||||
: "⚠ SOME CHECKS DID NOT PASS — review output above",
|
||||
);
|
||||
divider();
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("\n[FATAL]", err.response?.data ?? err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,442 +0,0 @@
|
||||
/**
|
||||
* Test Script: Forecast Item Resequencing & Procurement Linkage
|
||||
*
|
||||
* Validates the CW forecast API behaviour discovered via probing:
|
||||
* - `sequenceNumber` is read-only — display order = array position
|
||||
* - PUT always regenerates all forecast item IDs
|
||||
* - Revenue & cost are preserved through PUT
|
||||
* - PATCH on /forecast with `/forecastItems/{idx}/field` paths works
|
||||
* for some fields (e.g. forecastDescription) and preserves IDs
|
||||
*
|
||||
* Test flow:
|
||||
* 1. Create opportunity under XYZ Test Company
|
||||
* 2. Add 4 products via POST
|
||||
* 3. Create procurement products (linked by forecastDetailId)
|
||||
* 4. Cancel one procurement product
|
||||
* 5. Reorder forecast items via PUT (reverse order)
|
||||
* 6. Remap procurement forecastDetailId to new IDs
|
||||
* 7. Verify: order correct, prices preserved, cancellation data intact
|
||||
* 8. Clean up
|
||||
*
|
||||
* Usage: bun run test-forecast-resequence.ts
|
||||
*/
|
||||
import axios from "axios";
|
||||
|
||||
const cw = axios.create({
|
||||
baseURL: "https://ttscw.totaltech.net/v4_6_release/apis/3.0/",
|
||||
headers: {
|
||||
Authorization: `Basic ${process.env.CW_BASIC_TOKEN}`,
|
||||
clientId: `${process.env.CW_CLIENT_ID}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const log = (label: string, ...args: unknown[]) =>
|
||||
console.log(`\n[${label}]`, ...args);
|
||||
|
||||
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
async function main() {
|
||||
// ── 1. Find company ─────────────────────────────────────────────────────
|
||||
log("SETUP", "Finding XYZ Test Company...");
|
||||
const compRes = await cw.get(
|
||||
`/company/companies?conditions=${encodeURIComponent("name like 'XYZ Test%'")}&fields=id,identifier,name`,
|
||||
);
|
||||
if (compRes.data.length === 0) {
|
||||
console.error("ERROR: 'XYZ Test Company' not found.");
|
||||
process.exit(1);
|
||||
}
|
||||
const company = compRes.data[0];
|
||||
log("SETUP", `Company: ${company.name} (id=${company.id})`);
|
||||
|
||||
// ── 2. Create opportunity ───────────────────────────────────────────────
|
||||
log("SETUP", "Creating test opportunity...");
|
||||
const oppRes = await cw.post("/sales/opportunities", {
|
||||
name: `[TEST] Resequence – ${new Date().toISOString().slice(0, 16)}`,
|
||||
company: { id: company.id },
|
||||
contact: { id: 1 },
|
||||
primarySalesRep: { id: 153 },
|
||||
expectedCloseDate: new Date(Date.now() + 30 * 86_400_000)
|
||||
.toISOString()
|
||||
.replace(/\.\d{3}Z$/, "Z"),
|
||||
});
|
||||
const oppId = oppRes.data.id;
|
||||
log("SETUP", `Created opportunity id=${oppId}`);
|
||||
|
||||
const forecastUrl = `/sales/opportunities/${oppId}/forecast`;
|
||||
|
||||
// Track IDs for cleanup
|
||||
const procIdsToClean: number[] = [];
|
||||
|
||||
try {
|
||||
// ── 3. Add 4 products ───────────────────────────────────────────────────
|
||||
log("PRODUCTS", "Adding 4 products...");
|
||||
const postRes = await cw.post(forecastUrl, {
|
||||
forecastItems: [
|
||||
{
|
||||
opportunity: { id: oppId },
|
||||
status: { id: 1 },
|
||||
forecastDescription: "Alpha",
|
||||
revenue: 100,
|
||||
cost: 50,
|
||||
forecastType: "Product",
|
||||
},
|
||||
{
|
||||
opportunity: { id: oppId },
|
||||
status: { id: 1 },
|
||||
forecastDescription: "Bravo",
|
||||
revenue: 250,
|
||||
cost: 125,
|
||||
forecastType: "Product",
|
||||
},
|
||||
{
|
||||
opportunity: { id: oppId },
|
||||
status: { id: 1 },
|
||||
forecastDescription: "Charlie",
|
||||
revenue: 30,
|
||||
cost: 10,
|
||||
forecastType: "Product",
|
||||
},
|
||||
{
|
||||
opportunity: { id: oppId },
|
||||
status: { id: 1 },
|
||||
forecastDescription: "Delta",
|
||||
revenue: 75,
|
||||
cost: 40,
|
||||
forecastType: "Product",
|
||||
},
|
||||
],
|
||||
});
|
||||
const items: any[] = postRes.data.forecastItems ?? [];
|
||||
log("PRODUCTS", `Created ${items.length} items:`);
|
||||
for (const it of items) {
|
||||
console.log(
|
||||
` id=${it.id} desc="${it.forecastDescription}" rev=${it.revenue} cost=${it.cost}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Snapshot prices
|
||||
const priceSnap = new Map<string, { rev: number; cost: number }>(
|
||||
items.map((i) => [
|
||||
i.forecastDescription,
|
||||
{ rev: i.revenue, cost: i.cost },
|
||||
]),
|
||||
);
|
||||
|
||||
// ── 4. Create procurement products ──────────────────────────────────────
|
||||
log("PROCUREMENT", "Creating procurement products...");
|
||||
const procProducts: any[] = [];
|
||||
for (const item of items) {
|
||||
try {
|
||||
const pr = await cw.post("/procurement/products", {
|
||||
catalogItem: { id: 87 },
|
||||
description: item.forecastDescription,
|
||||
quantity: 1,
|
||||
price: item.revenue,
|
||||
cost: item.cost,
|
||||
billableOption: "Billable",
|
||||
opportunity: { id: oppId },
|
||||
forecastDetailId: item.id,
|
||||
});
|
||||
procProducts.push(pr.data);
|
||||
procIdsToClean.push(pr.data.id);
|
||||
console.log(
|
||||
` ✓ Proc ${pr.data.id} → forecastDetailId=${pr.data.forecastDetailId} "${item.forecastDescription}"`,
|
||||
);
|
||||
} catch (e: any) {
|
||||
console.log(
|
||||
` ✗ Failed: ${e.response?.status} ${JSON.stringify(e.response?.data)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (procProducts.length === 0) {
|
||||
log(
|
||||
"PROCUREMENT",
|
||||
"Could not create procurement products (permission issue?).",
|
||||
);
|
||||
log(
|
||||
"PROCUREMENT",
|
||||
"Will run reorder test without cancellation verification.",
|
||||
);
|
||||
}
|
||||
|
||||
// ── 5. Cancel "Bravo" procurement product ───────────────────────────────
|
||||
const bravoProc = procProducts.find((p: any) => p.description === "Bravo");
|
||||
if (bravoProc) {
|
||||
log("CANCEL", `Cancelling Bravo (proc id=${bravoProc.id})...`);
|
||||
try {
|
||||
await cw.patch(`/procurement/products/${bravoProc.id}`, [
|
||||
{ op: "replace", path: "cancelledFlag", value: true },
|
||||
{ op: "replace", path: "quantityCancelled", value: 1 },
|
||||
{
|
||||
op: "replace",
|
||||
path: "cancelledReason",
|
||||
value: "Test cancellation",
|
||||
},
|
||||
]);
|
||||
log("CANCEL", "✓ Cancelled.");
|
||||
} catch (e: any) {
|
||||
log(
|
||||
"CANCEL",
|
||||
`✗ ${e.response?.status} ${JSON.stringify(e.response?.data)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── 5b. Check for auto-created forecast items ─────────────────────────
|
||||
await sleep(300);
|
||||
const midForecast = await cw.get(forecastUrl);
|
||||
const midItems = midForecast.data.forecastItems ?? [];
|
||||
log(
|
||||
"OBSERVE",
|
||||
`Forecast items after procurement creation: ${midItems.length} (was ${items.length})`,
|
||||
);
|
||||
if (midItems.length !== items.length) {
|
||||
log(
|
||||
"OBSERVE",
|
||||
"⚠ Creating procurement products auto-created additional forecast items!",
|
||||
);
|
||||
for (const mi of midItems) {
|
||||
const isOriginal = items.some((i: any) => i.id === mi.id);
|
||||
console.log(
|
||||
` id=${mi.id} desc="${mi.forecastDescription}" ${isOriginal ? "(original)" : "(AUTO-CREATED by procurement)"}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot procurement state before reorder
|
||||
const beforeProc = await cw.get(
|
||||
`/procurement/products?conditions=${encodeURIComponent(`opportunity/id=${oppId}`)}&fields=id,forecastDetailId,cancelledFlag,quantityCancelled,cancelledReason,description`,
|
||||
);
|
||||
// Build map by description for cross-PUT comparison (IDs will change)
|
||||
const beforeByDesc = new Map<string, any>();
|
||||
log(
|
||||
"SNAPSHOT",
|
||||
`${beforeProc.data.length} procurement products before reorder:`,
|
||||
);
|
||||
for (const p of beforeProc.data) {
|
||||
beforeByDesc.set(p.description, p);
|
||||
console.log(
|
||||
` Proc ${p.id}: forecastDetailId=${p.forecastDetailId} cancelled=${p.cancelledFlag} qty=${p.quantityCancelled} reason="${p.cancelledReason ?? ""}" "${p.description}"`,
|
||||
);
|
||||
}
|
||||
|
||||
// Record old procurement IDs for later comparison
|
||||
const oldProcIds = new Set(beforeProc.data.map((p: any) => p.id));
|
||||
|
||||
// ── 6. Reorder: reverse ONLY the original 4 forecast items ──────────────
|
||||
log("REORDER", "Reversing forecast item order via PUT...");
|
||||
|
||||
// Only reorder the original items; keep any auto-created ones in place
|
||||
const originalDescs = new Set(items.map((i: any) => i.forecastDescription));
|
||||
const originals = midItems.filter(
|
||||
(i: any) =>
|
||||
originalDescs.has(i.forecastDescription) &&
|
||||
items.some((o: any) => o.id === i.id),
|
||||
);
|
||||
const extras = midItems.filter(
|
||||
(i: any) => !originals.some((o: any) => o.id === i.id),
|
||||
);
|
||||
|
||||
const reversedOriginals = [...originals].reverse();
|
||||
const reorderedAll = [...reversedOriginals, ...extras];
|
||||
|
||||
const clone = JSON.parse(JSON.stringify(midForecast.data));
|
||||
clone.forecastItems = JSON.parse(JSON.stringify(reorderedAll));
|
||||
|
||||
const putRes = await cw.put(forecastUrl, clone);
|
||||
const newItems: any[] = putRes.data.forecastItems ?? [];
|
||||
|
||||
log("REORDER", `After PUT (${newItems.length} items):`);
|
||||
for (const it of newItems) {
|
||||
console.log(
|
||||
` id=${it.id} desc="${it.forecastDescription}" rev=${it.revenue} cost=${it.cost}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Build old→new ID map by position (for original items only)
|
||||
const idMap = new Map<number, number>();
|
||||
for (let i = 0; i < reversedOriginals.length && i < newItems.length; i++) {
|
||||
idMap.set(reversedOriginals[i].id, newItems[i].id);
|
||||
}
|
||||
log("ID MAP", "Forecast item Old → New:");
|
||||
for (const [oldId, newId] of idMap) {
|
||||
console.log(` ${oldId} → ${newId}`);
|
||||
}
|
||||
|
||||
// ── 7. Check if procurement products survived PUT ───────────────────────
|
||||
await sleep(300);
|
||||
const afterProc = await cw.get(
|
||||
`/procurement/products?conditions=${encodeURIComponent(`opportunity/id=${oppId}`)}&fields=id,forecastDetailId,cancelledFlag,quantityCancelled,cancelledReason,description`,
|
||||
);
|
||||
const newProcIds = new Set(afterProc.data.map((p: any) => p.id));
|
||||
|
||||
log(
|
||||
"PROCUREMENT SURVIVAL",
|
||||
"Checking if procurement product IDs survived PUT...",
|
||||
);
|
||||
const procSurvived = [...oldProcIds].every((id) => newProcIds.has(id));
|
||||
if (procSurvived) {
|
||||
console.log(" ✓ All original procurement product IDs survived PUT.");
|
||||
} else {
|
||||
console.log(" ✗ PUT REGENERATED procurement product IDs!");
|
||||
console.log(` Before: [${[...oldProcIds].join(", ")}]`);
|
||||
console.log(` After: [${[...newProcIds].join(", ")}]`);
|
||||
}
|
||||
|
||||
// Try remap if old IDs still exist
|
||||
let remapOk = true;
|
||||
if (procSurvived) {
|
||||
log("REMAP", "Updating procurement products forecastDetailId...");
|
||||
for (const pp of beforeProc.data) {
|
||||
const oldFdId = pp.forecastDetailId as number;
|
||||
const newFdId = idMap.get(oldFdId);
|
||||
if (!newFdId || newFdId === oldFdId) continue;
|
||||
try {
|
||||
await cw.patch(`/procurement/products/${pp.id}`, [
|
||||
{ op: "replace", path: "forecastDetailId", value: newFdId },
|
||||
]);
|
||||
console.log(
|
||||
` ✓ Proc ${pp.id}: forecastDetailId ${oldFdId} → ${newFdId}`,
|
||||
);
|
||||
} catch (e: any) {
|
||||
remapOk = false;
|
||||
console.log(
|
||||
` ✗ Proc ${pp.id} remap failed: ${e.response?.status} ${JSON.stringify(e.response?.data)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
remapOk = false;
|
||||
log(
|
||||
"REMAP",
|
||||
"⚠ SKIPPED — procurement products were regenerated by PUT; old IDs no longer exist.",
|
||||
);
|
||||
}
|
||||
|
||||
// ── 8. Verify ───────────────────────────────────────────────────────────
|
||||
await sleep(300);
|
||||
|
||||
// 8a. Verify order (first 4 items)
|
||||
log("VERIFY ORDER", "Expected reverse: Delta, Charlie, Bravo, Alpha");
|
||||
const expectedOrder = ["Delta", "Charlie", "Bravo", "Alpha"];
|
||||
let orderOk = true;
|
||||
for (let i = 0; i < expectedOrder.length; i++) {
|
||||
const actual = newItems[i]?.forecastDescription;
|
||||
const ok = actual === expectedOrder[i];
|
||||
if (!ok) orderOk = false;
|
||||
console.log(
|
||||
` Position ${i}: ${ok ? "✓" : "✗"} expected "${expectedOrder[i]}", got "${actual}"`,
|
||||
);
|
||||
}
|
||||
|
||||
// 8b. Verify prices (by description)
|
||||
log("VERIFY PRICES", "");
|
||||
let pricesOk = true;
|
||||
for (const item of newItems) {
|
||||
const orig = priceSnap.get(item.forecastDescription);
|
||||
if (!orig) continue;
|
||||
if (item.revenue !== orig.rev || item.cost !== orig.cost) {
|
||||
pricesOk = false;
|
||||
console.log(
|
||||
` ✗ "${item.forecastDescription}": rev ${orig.rev}→${item.revenue}, cost ${orig.cost}→${item.cost}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (pricesOk) console.log(" ✓ All prices preserved.");
|
||||
|
||||
// 8c. Verify cancellation data — match by description since IDs may have changed
|
||||
let cancelOk = true;
|
||||
if (procProducts.length > 0) {
|
||||
log(
|
||||
"VERIFY CANCELLATION",
|
||||
"Checking cancellation data on procurement products after PUT...",
|
||||
);
|
||||
const finalProc = await cw.get(
|
||||
`/procurement/products?conditions=${encodeURIComponent(`opportunity/id=${oppId}`)}&fields=id,forecastDetailId,cancelledFlag,quantityCancelled,cancelledReason,description`,
|
||||
);
|
||||
|
||||
// Track by procIdsToClean for cleanup
|
||||
for (const p of finalProc.data) {
|
||||
if (!procIdsToClean.includes(p.id)) procIdsToClean.push(p.id);
|
||||
}
|
||||
|
||||
for (const pp of finalProc.data) {
|
||||
const orig = beforeByDesc.get(pp.description);
|
||||
if (!orig) {
|
||||
console.log(
|
||||
` ? Proc ${pp.id} "${pp.description}" — no matching pre-PUT record`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const cancelledMatch =
|
||||
pp.cancelledFlag === orig.cancelledFlag &&
|
||||
pp.quantityCancelled === orig.quantityCancelled &&
|
||||
(pp.cancelledReason ?? "") === (orig.cancelledReason ?? "");
|
||||
|
||||
if (!cancelledMatch) {
|
||||
cancelOk = false;
|
||||
console.log(
|
||||
` ✗ Proc ${pp.id} "${pp.description}": CANCELLATION DATA CHANGED\n` +
|
||||
` Before: cancelled=${orig.cancelledFlag} qty=${orig.quantityCancelled} reason="${orig.cancelledReason ?? ""}"\n` +
|
||||
` After: cancelled=${pp.cancelledFlag} qty=${pp.quantityCancelled} reason="${pp.cancelledReason ?? ""}"`,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
` ✓ Proc ${pp.id} "${pp.description}": cancelled=${pp.cancelledFlag} qty=${pp.quantityCancelled} reason="${pp.cancelledReason ?? ""}"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Summary ─────────────────────────────────────────────────────────────
|
||||
log("SUMMARY", "");
|
||||
console.log(
|
||||
" Order correct: ",
|
||||
orderOk ? "✓ PASS" : "✗ FAIL",
|
||||
);
|
||||
console.log(
|
||||
" Prices preserved: ",
|
||||
pricesOk ? "✓ PASS" : "✗ FAIL",
|
||||
);
|
||||
console.log(
|
||||
" Proc IDs survived PUT: ",
|
||||
procSurvived ? "✓ PASS" : "✗ FAIL",
|
||||
);
|
||||
console.log(
|
||||
" Procurement remap: ",
|
||||
remapOk ? "✓ PASS" : "✗ FAIL (skipped or failed)",
|
||||
);
|
||||
console.log(
|
||||
" Cancellation data preserved:",
|
||||
cancelOk ? "✓ PASS" : "✗ FAIL",
|
||||
);
|
||||
|
||||
const allPass = orderOk && pricesOk && procSurvived && remapOk && cancelOk;
|
||||
log("RESULT", allPass ? "✓ ALL TESTS PASSED" : "✗ SOME TESTS FAILED");
|
||||
} finally {
|
||||
// ── Cleanup ─────────────────────────────────────────────────────────────
|
||||
log("CLEANUP", "Deleting procurement products...");
|
||||
for (const id of procIdsToClean) {
|
||||
try {
|
||||
await cw.delete(`/procurement/products/${id}`);
|
||||
} catch {}
|
||||
}
|
||||
log("CLEANUP", `Deleted ${procIdsToClean.length} procurement products.`);
|
||||
|
||||
log("CLEANUP", `Deleting opportunity ${oppId}...`);
|
||||
try {
|
||||
await cw.delete(`/sales/opportunities/${oppId}`);
|
||||
log("CLEANUP", "✓ Done.");
|
||||
} catch (e: any) {
|
||||
log("CLEANUP", `✗ ${e.response?.status ?? e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("\n[FATAL]", err.response?.data ?? err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,133 +0,0 @@
|
||||
// Test script to probe UniFi API endpoints for response shapes
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||
|
||||
const controllerBaseUrl = "https://unifi.totaltech.net";
|
||||
const site = "km9b1v8i";
|
||||
const username = "admin";
|
||||
const password = "Tt$Un1fiIZth3B3$t26";
|
||||
|
||||
class TestClient {
|
||||
private client: AxiosInstance;
|
||||
|
||||
constructor(baseURL: string) {
|
||||
this.client = axios.create({
|
||||
baseURL,
|
||||
validateStatus: (s) => s >= 200 && s < 400,
|
||||
});
|
||||
}
|
||||
|
||||
private persistSession(res: { headers: Record<string, unknown> }): void {
|
||||
const raw = res.headers["set-cookie"];
|
||||
if (raw) {
|
||||
const cookies = (Array.isArray(raw) ? raw : [raw]) as string[];
|
||||
const cookieString = cookies.map((c) => c.split(";")[0]).join("; ");
|
||||
this.client.defaults.headers.common["Cookie"] = cookieString;
|
||||
}
|
||||
const csrf = res.headers["x-csrf-token"];
|
||||
if (typeof csrf === "string") {
|
||||
this.client.defaults.headers.common["X-CSRF-Token"] = csrf;
|
||||
}
|
||||
}
|
||||
|
||||
async login(): Promise<void> {
|
||||
try {
|
||||
const res = await this.client.post("/api/auth/login", {
|
||||
username,
|
||||
password,
|
||||
});
|
||||
console.log("Login OK (UniFi OS)", res.status);
|
||||
this.persistSession(res);
|
||||
} catch (e) {
|
||||
const res = await this.client.post("/api/login", { username, password });
|
||||
console.log("Login OK (legacy)", res.status);
|
||||
this.persistSession(res);
|
||||
}
|
||||
}
|
||||
|
||||
async tryGet(label: string, paths: string[]): Promise<any> {
|
||||
for (const path of paths) {
|
||||
try {
|
||||
const res = await this.client.get(path);
|
||||
const data = res.data?.data ?? res.data;
|
||||
console.log(`\n=== ${label} (${path}) ===`);
|
||||
console.log(JSON.stringify(data, null, 2));
|
||||
return data;
|
||||
} catch (e: any) {
|
||||
console.log(` Failed ${path}: ${e.response?.status ?? e.message}`);
|
||||
}
|
||||
}
|
||||
console.log(` Could not fetch ${label} from any path`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const client = new TestClient(controllerBaseUrl);
|
||||
await client.login();
|
||||
|
||||
// 1. WLAN Groups (AP groups in UniFi)
|
||||
await client.tryGet("WLAN Groups", [
|
||||
`/proxy/network/api/s/${site}/rest/wlangroup`,
|
||||
`/api/s/${site}/rest/wlangroup`,
|
||||
]);
|
||||
|
||||
// 2. User Groups (bandwidth/speed limit profiles)
|
||||
await client.tryGet("User Groups (Speed Profiles)", [
|
||||
`/proxy/network/api/s/${site}/rest/usergroup`,
|
||||
`/api/s/${site}/rest/usergroup`,
|
||||
]);
|
||||
|
||||
// 3. Devices - APs only (compact)
|
||||
const devices = await client.tryGet("Devices", [
|
||||
`/proxy/network/api/s/${site}/stat/device`,
|
||||
]);
|
||||
if (devices) {
|
||||
const aps = devices.filter((d: any) => d.type === "uap");
|
||||
console.log(`\n=== APs (${aps.length}) - compact ===`);
|
||||
aps.forEach((ap: any) => {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
_id: ap._id,
|
||||
name: ap.name,
|
||||
mac: ap.mac,
|
||||
model: ap.model,
|
||||
radio_table: ap.radio_table?.map((r: any) => ({
|
||||
radio: r.radio,
|
||||
name: r.name,
|
||||
})),
|
||||
wlangroup_id_ng: ap.wlangroup_id_ng,
|
||||
wlangroup_id_na: ap.wlangroup_id_na,
|
||||
vap_table_count: ap.vap_table?.length,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 4. One full WLAN to see private_preshared_keys structure
|
||||
const wlans = await client.tryGet("WLANs", [
|
||||
`/proxy/network/api/s/${site}/rest/wlanconf`,
|
||||
]);
|
||||
if (wlans) {
|
||||
// Log just the PPSK-related fields from each WLAN
|
||||
console.log("\n=== PPSK fields per WLAN ===");
|
||||
wlans.forEach((w: any) => {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
name: w.name,
|
||||
_id: w._id,
|
||||
private_preshared_keys_enabled: w.private_preshared_keys_enabled,
|
||||
private_preshared_keys: w.private_preshared_keys,
|
||||
ap_group_ids: w.ap_group_ids,
|
||||
ap_group_mode: w.ap_group_mode,
|
||||
wlan_band: w.wlan_band,
|
||||
wlan_bands: w.wlan_bands,
|
||||
usergroup_id: w.usergroup_id,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((e) => console.error(e));
|
||||
@@ -1,123 +0,0 @@
|
||||
// unifi-wifi-list.ts
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||
|
||||
const controllerBaseUrl = "https://unifi.totaltech.net";
|
||||
const site = "km9b1v8i";
|
||||
const username = "admin";
|
||||
const password = "Tt$Un1fiIZth3B3$t26";
|
||||
|
||||
interface WlanConfRaw {
|
||||
_id: string;
|
||||
name?: string;
|
||||
ssid?: string;
|
||||
x_passphrase?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface WlanConf {
|
||||
id: string;
|
||||
ssid: string;
|
||||
password: string | null;
|
||||
}
|
||||
|
||||
class UnifiClient {
|
||||
private client: AxiosInstance;
|
||||
|
||||
constructor(baseURL: string) {
|
||||
this.client = axios.create({
|
||||
baseURL,
|
||||
validateStatus: (s) => s >= 200 && s < 400,
|
||||
});
|
||||
}
|
||||
|
||||
private persistSession(res: { headers: Record<string, unknown> }): void {
|
||||
// Cookies
|
||||
const raw = res.headers["set-cookie"];
|
||||
if (raw) {
|
||||
const cookies = (Array.isArray(raw) ? raw : [raw]) as string[];
|
||||
const cookieString = cookies.map((c) => c.split(";")[0]).join("; ");
|
||||
this.client.defaults.headers.common["Cookie"] = cookieString;
|
||||
}
|
||||
// CSRF token (UniFi OS)
|
||||
const csrf = res.headers["x-csrf-token"];
|
||||
if (typeof csrf === "string") {
|
||||
this.client.defaults.headers.common["X-CSRF-Token"] = csrf;
|
||||
}
|
||||
}
|
||||
|
||||
async login(username: string, password: string): Promise<void> {
|
||||
const body = { username, password };
|
||||
|
||||
try {
|
||||
// UniFi OS
|
||||
const res = await this.client.post("/api/auth/login", body);
|
||||
console.log("Login OK (UniFi OS)", res.status);
|
||||
this.persistSession(res);
|
||||
} catch (e) {
|
||||
// Legacy controller
|
||||
console.log("UniFi OS login failed, trying legacy...");
|
||||
const res = await this.client.post("/api/login", body);
|
||||
console.log("Login OK (legacy)", res.status);
|
||||
this.persistSession(res);
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchWlanConfRaw(site: string): Promise<WlanConfRaw[]> {
|
||||
const paths = [
|
||||
`/proxy/network/api/s/${site}/rest/wlanconf`,
|
||||
`/api/s/${site}/rest/wlanconf`,
|
||||
];
|
||||
|
||||
for (const path of paths) {
|
||||
try {
|
||||
const res = await this.client.get(path);
|
||||
const data = (res.data?.data ?? res.data) as WlanConfRaw[];
|
||||
console.log(`Fetched wlan from ${path}`);
|
||||
return data;
|
||||
} catch (e) {
|
||||
console.log(
|
||||
`Failed ${path}:`,
|
||||
axios.isAxiosError(e) ? e.response?.status : e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Could not fetch WLAN config from any known path");
|
||||
}
|
||||
|
||||
async getWlanConf(site: string): Promise<WlanConf[]> {
|
||||
const raw = await this.fetchWlanConfRaw(site);
|
||||
|
||||
return raw.map(
|
||||
(w): WlanConf => ({
|
||||
id: w._id,
|
||||
ssid: (w.name || w.ssid || "").toString(),
|
||||
password: typeof w.x_passphrase === "string" ? w.x_passphrase : null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const unifi = new UnifiClient(controllerBaseUrl);
|
||||
|
||||
try {
|
||||
await unifi.login(username, password);
|
||||
|
||||
const wlans = await unifi.getWlanConf(site);
|
||||
|
||||
wlans.forEach((wlan) => {
|
||||
console.log(`${wlan.ssid}: ${wlan.password ?? "<no password>"}`);
|
||||
});
|
||||
} catch (err) {
|
||||
if (axios.isAxiosError(err)) {
|
||||
console.error("HTTP error", err.response?.status, err.response?.data);
|
||||
} else {
|
||||
console.error("Error", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((e) => console.error(e));
|
||||
@@ -1,123 +0,0 @@
|
||||
// unifi-wifi-list.ts
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||
|
||||
const controllerBaseUrl = "https://unifi.totaltech.net";
|
||||
const site = "km9b1v8i";
|
||||
const username = "admin";
|
||||
const password = "Tt$Un1fiIZth3B3$t26";
|
||||
|
||||
interface WlanConfRaw {
|
||||
_id: string;
|
||||
name?: string;
|
||||
ssid?: string;
|
||||
x_passphrase?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface WlanConf {
|
||||
id: string;
|
||||
ssid: string;
|
||||
password: string | null;
|
||||
}
|
||||
|
||||
class UnifiClient {
|
||||
private client: AxiosInstance;
|
||||
|
||||
constructor(baseURL: string) {
|
||||
this.client = axios.create({
|
||||
baseURL,
|
||||
validateStatus: (s) => s >= 200 && s < 400,
|
||||
});
|
||||
}
|
||||
|
||||
private persistSession(res: { headers: Record<string, unknown> }): void {
|
||||
// Cookies
|
||||
const raw = res.headers["set-cookie"];
|
||||
if (raw) {
|
||||
const cookies = (Array.isArray(raw) ? raw : [raw]) as string[];
|
||||
const cookieString = cookies.map((c) => c.split(";")[0]).join("; ");
|
||||
this.client.defaults.headers.common["Cookie"] = cookieString;
|
||||
}
|
||||
// CSRF token (UniFi OS)
|
||||
const csrf = res.headers["x-csrf-token"];
|
||||
if (typeof csrf === "string") {
|
||||
this.client.defaults.headers.common["X-CSRF-Token"] = csrf;
|
||||
}
|
||||
}
|
||||
|
||||
async login(username: string, password: string): Promise<void> {
|
||||
const body = { username, password };
|
||||
|
||||
try {
|
||||
// UniFi OS
|
||||
const res = await this.client.post("/api/auth/login", body);
|
||||
console.log("Login OK (UniFi OS)", res.status);
|
||||
this.persistSession(res);
|
||||
} catch (e) {
|
||||
// Legacy controller
|
||||
console.log("UniFi OS login failed, trying legacy...");
|
||||
const res = await this.client.post("/api/login", body);
|
||||
console.log("Login OK (legacy)", res.status);
|
||||
this.persistSession(res);
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchWlanConfRaw(site: string): Promise<WlanConfRaw[]> {
|
||||
const paths = [
|
||||
`/proxy/network/api/s/${site}/rest/wlanconf`,
|
||||
`/api/s/${site}/rest/wlanconf`,
|
||||
];
|
||||
|
||||
for (const path of paths) {
|
||||
try {
|
||||
const res = await this.client.get(path);
|
||||
const data = (res.data?.data ?? res.data) as WlanConfRaw[];
|
||||
console.log(`Fetched wlan from ${path}`);
|
||||
return data;
|
||||
} catch (e) {
|
||||
console.log(
|
||||
`Failed ${path}:`,
|
||||
axios.isAxiosError(e) ? e.response?.status : e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Could not fetch WLAN config from any known path");
|
||||
}
|
||||
|
||||
async getWlanConf(site: string): Promise<WlanConf[]> {
|
||||
const raw = await this.fetchWlanConfRaw(site);
|
||||
|
||||
return raw.map(
|
||||
(w): WlanConf => ({
|
||||
id: w._id,
|
||||
ssid: (w.name || w.ssid || "").toString(),
|
||||
password: typeof w.x_passphrase === "string" ? w.x_passphrase : null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const unifi = new UnifiClient(controllerBaseUrl);
|
||||
|
||||
try {
|
||||
await unifi.login(username, password);
|
||||
|
||||
const wlans = await unifi.getWlanConf(site);
|
||||
|
||||
wlans.forEach((wlan) => {
|
||||
console.log(`${wlan.ssid}: ${wlan.password ?? "<no password>"}`);
|
||||
});
|
||||
} catch (err) {
|
||||
if (axios.isAxiosError(err)) {
|
||||
console.error("HTTP error", err.response?.status, err.response?.data);
|
||||
} else {
|
||||
console.error("Error", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((e) => console.error(e));
|
||||
@@ -1,15 +0,0 @@
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import { PrismaClient } from "./generated/prisma/client";
|
||||
import fs from "node:fs";
|
||||
|
||||
const env = fs.readFileSync(".env", "utf8");
|
||||
const line = env.split(/\n/).find((l) => l.startsWith("DATABASE_URL="));
|
||||
const url = line?.slice("DATABASE_URL=".length)?.replace(/^"|"$/g, "");
|
||||
|
||||
const prisma = new PrismaClient({
|
||||
adapter: new PrismaPg({ connectionString: url }),
|
||||
});
|
||||
|
||||
const rows = await prisma.$queryRaw`select tablename from pg_tables where schemaname = 'public' order by tablename`;
|
||||
console.log(rows);
|
||||
await prisma.$disconnect();
|
||||
@@ -1,24 +0,0 @@
|
||||
import { prisma } from "./src/constants";
|
||||
|
||||
const roles = await prisma.role.findMany({ select: { title: true, moniker: true, permissions: true } });
|
||||
|
||||
for (const role of roles) {
|
||||
const token = role.permissions;
|
||||
if (!token) { console.log(role.title, "- NO PERMISSIONS"); continue; }
|
||||
try {
|
||||
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
|
||||
const perms: string[] = payload.permissions || [];
|
||||
const relevant = perms.filter((p: string) =>
|
||||
p.includes("sales.opportunity.product") ||
|
||||
p.includes("sales.opportunity.*") ||
|
||||
p.includes("sales.*") ||
|
||||
p === "*"
|
||||
);
|
||||
if (relevant.length > 0) {
|
||||
console.log("=== " + role.title + " (" + role.moniker + ") ===");
|
||||
relevant.forEach((p: string) => console.log(" " + p));
|
||||
}
|
||||
} catch(e: any) { console.log(role.title, "- parse error:", e.message); }
|
||||
}
|
||||
|
||||
await prisma.$disconnect();
|
||||
@@ -1,24 +0,0 @@
|
||||
import { prisma } from "./src/constants";
|
||||
|
||||
const roles = await prisma.role.findMany({ select: { title: true, moniker: true, permissions: true } });
|
||||
|
||||
for (const role of roles) {
|
||||
const token = role.permissions;
|
||||
if (!token) { console.log(role.title, "- NO PERMISSIONS"); continue; }
|
||||
try {
|
||||
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
|
||||
const perms: string[] = payload.permissions || [];
|
||||
// Check for obj.catalogItem or wildcard that would cover it
|
||||
const relevant = perms.filter((p: string) =>
|
||||
p.includes("obj.catalogItem") ||
|
||||
p.includes("obj.*") ||
|
||||
p === "*"
|
||||
);
|
||||
console.log("=== " + role.title + " (" + role.moniker + ") ===");
|
||||
console.log(" obj.catalogItem-related perms:", JSON.stringify(relevant));
|
||||
// Also show all perms for debugging
|
||||
console.log(" Total perms:", perms.length);
|
||||
} catch(e: any) { console.log(role.title, "- parse error:", e.message); }
|
||||
}
|
||||
|
||||
await prisma.$disconnect();
|
||||
Reference in New Issue
Block a user