diff --git a/api/.docker/docker-compose.yml b/.docker/docker-compose.yml similarity index 97% rename from api/.docker/docker-compose.yml rename to .docker/docker-compose.yml index fe2be7c..3778e82 100644 --- a/api/.docker/docker-compose.yml +++ b/.docker/docker-compose.yml @@ -26,7 +26,7 @@ services: image: adminer restart: unless-stopped ports: - - 8080:8080 + - 8081:8080 depends_on: - pgsql redisinsight: diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index b2465df..0000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -daemon diff --git a/api/.github/workflows/build-and-publish.yaml b/.github/workflows/api-build-and-publish.yaml similarity index 90% rename from api/.github/workflows/build-and-publish.yaml rename to .github/workflows/api-build-and-publish.yaml index 62c6b0a..c35acea 100644 --- a/api/.github/workflows/build-and-publish.yaml +++ b/.github/workflows/api-build-and-publish.yaml @@ -1,4 +1,4 @@ -name: Build and Publish +name: API - Build and Publish on: release: @@ -8,6 +8,9 @@ jobs: test: name: Test runs-on: ubuntu-latest + defaults: + run: + working-directory: api steps: - name: Checkout source code uses: actions/checkout@v4 @@ -47,6 +50,7 @@ jobs: - name: Build and push the Docker image uses: docker/build-push-action@v6 with: + context: ./api push: true target: runtime tags: | @@ -56,6 +60,7 @@ jobs: - name: Build and push the migration image uses: docker/build-push-action@v6 with: + context: ./api push: true target: migration tags: | @@ -66,6 +71,9 @@ jobs: name: Run Migrations needs: [build] runs-on: ubuntu-latest + defaults: + run: + working-directory: api steps: - name: Set the Kubernetes context uses: azure/k8s-set-context@v2 @@ -108,8 +116,8 @@ jobs: with: lintType: dryrun manifests: | - kubernetes/deployment.yaml - kubernetes/ingress.yaml + api/kubernetes/deployment.yaml + api/kubernetes/ingress.yaml namespace: optima - name: Deploy to the Kubernetes cluster @@ -119,7 +127,7 @@ jobs: force: true skip-tls-verify: true manifests: | - kubernetes/deployment.yaml - kubernetes/ingress.yaml + api/kubernetes/deployment.yaml + api/kubernetes/ingress.yaml images: | ghcr.io/project-optima/ttscm-api:${{ github.event.release.tag_name }} diff --git a/api/.github/workflows/tests.yaml b/.github/workflows/api-tests.yaml similarity index 88% rename from api/.github/workflows/tests.yaml rename to .github/workflows/api-tests.yaml index 844952c..1da5db8 100644 --- a/api/.github/workflows/tests.yaml +++ b/.github/workflows/api-tests.yaml @@ -1,4 +1,4 @@ -name: Tests +name: API - Tests on: push: @@ -8,6 +8,9 @@ jobs: test: name: Test runs-on: ubuntu-latest + defaults: + run: + working-directory: api steps: - name: Checkout source code uses: actions/checkout@v4 diff --git a/ui/.github/workflows/build-and-publish.yaml b/.github/workflows/ui-build-and-publish.yaml similarity index 67% rename from ui/.github/workflows/build-and-publish.yaml rename to .github/workflows/ui-build-and-publish.yaml index c0b66f8..51fc45b 100644 --- a/ui/.github/workflows/build-and-publish.yaml +++ b/.github/workflows/ui-build-and-publish.yaml @@ -1,4 +1,4 @@ -name: Build and Publish +name: UI - Build and Publish on: release: @@ -28,7 +28,7 @@ jobs: - name: Build and push the Docker image uses: docker/build-push-action@v6 with: - context: . + context: ./ui push: true build-args: | PUBLIC_API_URL=https://opt-api.osdci.net @@ -41,6 +41,9 @@ jobs: runs-on: macos-latest permissions: contents: write + defaults: + run: + working-directory: ui steps: - name: Checkout source code uses: actions/checkout@v4 @@ -68,14 +71,17 @@ jobs: uses: softprops/action-gh-release@v2 with: files: | - out/make/**/*.dmg - out/make/**/*.zip + ui/out/make/**/*.dmg + ui/out/make/**/*.zip build-desktop-windows: name: Build Desktop (Windows) runs-on: windows-latest permissions: contents: write + defaults: + run: + working-directory: ui steps: - name: Checkout source code uses: actions/checkout@v4 @@ -97,41 +103,4 @@ jobs: uses: softprops/action-gh-release@v2 with: files: | - out/make/**/*.exe - out/make/**/*.nupkg - out/make/**/*.msi - - deploy: - name: Deploy - needs: [build-server] - runs-on: ubuntu-latest - steps: - - name: Set the Kubernetes context - uses: azure/k8s-set-context@v2 - with: - method: kubeconfig - kubeconfig: ${{ secrets.KUBECONFIG }} - - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Lint Kubernetes manifests - uses: azure/k8s-lint@v3 - with: - lintType: dryrun - manifests: | - kubernetes/deployment.yaml - kubernetes/ingress.yaml - namespace: optima - - - name: Deploy to the Kubernetes cluster - uses: azure/k8s-deploy@v5 - with: - namespace: optima - force: true - skip-tls-verify: true - manifests: | - kubernetes/deployment.yaml - kubernetes/ingress.yaml - images: | - ghcr.io/project-optima/ttscm-ui:${{ github.event.release.tag_name }} + ui/out/make/**/*.exe diff --git a/.gitignore b/.gitignore index 9a5aced..96dac53 100644 --- a/.gitignore +++ b/.gitignore @@ -1,139 +1,69 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* +# macOS metadata +__MACOSX/ +.DS_Store +.AppleDouble +.LSOverride -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories +# Dependencies node_modules/ -jspm_packages/ -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files +# Environment variables .env .env.* !.env.example +!.env.test -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache +# Logs +*.log +*.jsonl +logs/ -# Next.js build output -.next -out +# TypeScript +*.tsbuildinfo -# Nuxt.js build / generate output -.nuxt -dist +# Bun +.bun/ -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public +# Build outputs +dist/ +build/ +out/ +.output/ -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Sveltekit cache directory +# SvelteKit .svelte-kit/ -# vitepress build output -**/.vitepress/dist - -# vitepress cache directory -**/.vitepress/cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# Firebase cache directory -.firebase/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v3 -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions - -# Vite logs files +# Vite +.vite/ vite.config.js.timestamp-* vite.config.ts.timestamp-* + +# Test coverage +coverage/ +.nyc_output/ + +# Local Docker data volumes +.docker/postgres/ +.docker/redis/dump.rdb + +# Generated Prisma client +api/generated/ +dalpuri/generated/ + +# Secret key files +api/.permissions.key +api/.refreshToken.key +api/.secureKeys.key +api/.accessToken.key +api/.secureValues.key +api/production-keys/ +api/public-keys/ + +# Temporary / scratch files +git-backup.zip +configurations-top-150-and-stats.json diff --git a/.husky/.gitignore b/.husky/.gitignore deleted file mode 100644 index 31354ec..0000000 --- a/.husky/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_ diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 9e0600d..0000000 --- a/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -daemon diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index c83737b..0000000 --- a/.prettierrc.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - trailingComma: "es5", - tabWidth: 2, - semi: true, - singleQuote: false, - arrowParens: "always", - useTabs: false, -}; diff --git a/.yarnrc.yml b/.yarnrc.yml deleted file mode 100644 index 6032ec5..0000000 --- a/.yarnrc.yml +++ /dev/null @@ -1,9 +0,0 @@ -nodeLinker: node-modules - -plugins: - - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs - spec: "@yarnpkg/plugin-interactive-tools" - - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs - spec: "@yarnpkg/plugin-workspace-tools" - -yarnPath: .yarn/releases/yarn-3.2.0.cjs diff --git a/api/generated/prisma/browser.ts b/api/generated/prisma/browser.ts index dfcba99..b3c8ec0 100644 --- a/api/generated/prisma/browser.ts +++ b/api/generated/prisma/browser.ts @@ -17,6 +17,16 @@ import * as Prisma from './internal/prismaNamespaceBrowser.ts' export { Prisma } export * as $Enums from './enums.ts' export * from './enums.ts'; +/** + * Model SyncJobRun + * + */ +export type SyncJobRun = Prisma.SyncJobRunModel +/** + * Model SyncStepLog + * + */ +export type SyncStepLog = Prisma.SyncStepLogModel /** * Model Session * @@ -32,6 +42,16 @@ export type User = Prisma.UserModel * */ export type Role = Prisma.RoleModel +/** + * Model CorporateLocation + * + */ +export type CorporateLocation = Prisma.CorporateLocationModel +/** + * Model InternalDepartment + * + */ +export type InternalDepartment = Prisma.InternalDepartmentModel /** * Model UnifiSite * @@ -42,16 +62,156 @@ export type UnifiSite = Prisma.UnifiSiteModel * */ export type Company = Prisma.CompanyModel +/** + * Model CompanyAddress + * + */ +export type CompanyAddress = Prisma.CompanyAddressModel +/** + * Model Contact + * + */ +export type Contact = Prisma.ContactModel +/** + * Model CatalogItemType + * + */ +export type CatalogItemType = Prisma.CatalogItemTypeModel +/** + * Model CatalogCategory + * + */ +export type CatalogCategory = Prisma.CatalogCategoryModel +/** + * Model CatalogSubcategory + * + */ +export type CatalogSubcategory = Prisma.CatalogSubcategoryModel +/** + * Model CatalogManufacturer + * + */ +export type CatalogManufacturer = Prisma.CatalogManufacturerModel +/** + * Model WarehouseBin + * + */ +export type WarehouseBin = Prisma.WarehouseBinModel +/** + * Model ProductInventory + * + */ +export type ProductInventory = Prisma.ProductInventoryModel +/** + * Model Warehouse + * + */ +export type Warehouse = Prisma.WarehouseModel +/** + * Model MinimumStockByWarehouse + * + */ +export type MinimumStockByWarehouse = Prisma.MinimumStockByWarehouseModel /** * Model CatalogItem * */ export type CatalogItem = Prisma.CatalogItemModel +/** + * Model ProductData + * + */ +export type ProductData = Prisma.ProductDataModel +/** + * Model ServiceTicket + * + */ +export type ServiceTicket = Prisma.ServiceTicketModel +/** + * Model ServiceTicketNote + * + */ +export type ServiceTicketNote = Prisma.ServiceTicketNoteModel +/** + * Model ServiceTicketType + * + */ +export type ServiceTicketType = Prisma.ServiceTicketTypeModel +/** + * Model ServiceTicketBoard + * + */ +export type ServiceTicketBoard = Prisma.ServiceTicketBoardModel +/** + * Model ServiceTicketLocation + * + */ +export type ServiceTicketLocation = Prisma.ServiceTicketLocationModel +/** + * Model ServiceTicketSource + * + */ +export type ServiceTicketSource = Prisma.ServiceTicketSourceModel +/** + * Model ServiceTicketImpact + * + */ +export type ServiceTicketImpact = Prisma.ServiceTicketImpactModel +/** + * Model ServiceTicketPriority + * + */ +export type ServiceTicketPriority = Prisma.ServiceTicketPriorityModel +/** + * Model ServiceTicketSeverity + * + */ +export type ServiceTicketSeverity = Prisma.ServiceTicketSeverityModel +/** + * Model ServiceTicketFinalData + * + */ +export type ServiceTicketFinalData = Prisma.ServiceTicketFinalDataModel +/** + * Model OpportunityStage + * + */ +export type OpportunityStage = Prisma.OpportunityStageModel +/** + * Model OpportunityType + * + */ +export type OpportunityType = Prisma.OpportunityTypeModel +/** + * Model OpportunityStatus + * + */ +export type OpportunityStatus = Prisma.OpportunityStatusModel /** * Model Opportunity * */ export type Opportunity = Prisma.OpportunityModel +/** + * Model ScheduleStatus + * + */ +export type ScheduleStatus = Prisma.ScheduleStatusModel +/** + * Model ScheduleType + * + */ +export type ScheduleType = Prisma.ScheduleTypeModel +/** + * Model ScheduleSpan + * + */ +export type ScheduleSpan = Prisma.ScheduleSpanModel +/** + * Model Schedule + * + */ +export type Schedule = Prisma.ScheduleModel /** * Model CredentialType * @@ -72,6 +232,11 @@ export type Credential = Prisma.CredentialModel * */ export type GeneratedQuotes = Prisma.GeneratedQuotesModel +/** + * Model TaxCode + * + */ +export type TaxCode = Prisma.TaxCodeModel /** * Model CwMember * diff --git a/api/generated/prisma/client.ts b/api/generated/prisma/client.ts index d95dc7e..f2609c9 100644 --- a/api/generated/prisma/client.ts +++ b/api/generated/prisma/client.ts @@ -28,9 +28,11 @@ export * from "./enums.ts" * Type-safe database client for TypeScript * @example * ``` - * const prisma = new PrismaClient() - * // Fetch zero or more Sessions - * const sessions = await prisma.session.findMany() + * const prisma = new PrismaClient({ + * adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }) + * }) + * // Fetch zero or more SyncJobRuns + * const syncJobRuns = await prisma.syncJobRun.findMany() * ``` * * Read more in our [docs](https://pris.ly/d/client). @@ -39,6 +41,16 @@ export const PrismaClient = $Class.getPrismaClientClass() export type PrismaClient = $Class.PrismaClient export { Prisma } +/** + * Model SyncJobRun + * + */ +export type SyncJobRun = Prisma.SyncJobRunModel +/** + * Model SyncStepLog + * + */ +export type SyncStepLog = Prisma.SyncStepLogModel /** * Model Session * @@ -54,6 +66,16 @@ export type User = Prisma.UserModel * */ export type Role = Prisma.RoleModel +/** + * Model CorporateLocation + * + */ +export type CorporateLocation = Prisma.CorporateLocationModel +/** + * Model InternalDepartment + * + */ +export type InternalDepartment = Prisma.InternalDepartmentModel /** * Model UnifiSite * @@ -64,16 +86,156 @@ export type UnifiSite = Prisma.UnifiSiteModel * */ export type Company = Prisma.CompanyModel +/** + * Model CompanyAddress + * + */ +export type CompanyAddress = Prisma.CompanyAddressModel +/** + * Model Contact + * + */ +export type Contact = Prisma.ContactModel +/** + * Model CatalogItemType + * + */ +export type CatalogItemType = Prisma.CatalogItemTypeModel +/** + * Model CatalogCategory + * + */ +export type CatalogCategory = Prisma.CatalogCategoryModel +/** + * Model CatalogSubcategory + * + */ +export type CatalogSubcategory = Prisma.CatalogSubcategoryModel +/** + * Model CatalogManufacturer + * + */ +export type CatalogManufacturer = Prisma.CatalogManufacturerModel +/** + * Model WarehouseBin + * + */ +export type WarehouseBin = Prisma.WarehouseBinModel +/** + * Model ProductInventory + * + */ +export type ProductInventory = Prisma.ProductInventoryModel +/** + * Model Warehouse + * + */ +export type Warehouse = Prisma.WarehouseModel +/** + * Model MinimumStockByWarehouse + * + */ +export type MinimumStockByWarehouse = Prisma.MinimumStockByWarehouseModel /** * Model CatalogItem * */ export type CatalogItem = Prisma.CatalogItemModel +/** + * Model ProductData + * + */ +export type ProductData = Prisma.ProductDataModel +/** + * Model ServiceTicket + * + */ +export type ServiceTicket = Prisma.ServiceTicketModel +/** + * Model ServiceTicketNote + * + */ +export type ServiceTicketNote = Prisma.ServiceTicketNoteModel +/** + * Model ServiceTicketType + * + */ +export type ServiceTicketType = Prisma.ServiceTicketTypeModel +/** + * Model ServiceTicketBoard + * + */ +export type ServiceTicketBoard = Prisma.ServiceTicketBoardModel +/** + * Model ServiceTicketLocation + * + */ +export type ServiceTicketLocation = Prisma.ServiceTicketLocationModel +/** + * Model ServiceTicketSource + * + */ +export type ServiceTicketSource = Prisma.ServiceTicketSourceModel +/** + * Model ServiceTicketImpact + * + */ +export type ServiceTicketImpact = Prisma.ServiceTicketImpactModel +/** + * Model ServiceTicketPriority + * + */ +export type ServiceTicketPriority = Prisma.ServiceTicketPriorityModel +/** + * Model ServiceTicketSeverity + * + */ +export type ServiceTicketSeverity = Prisma.ServiceTicketSeverityModel +/** + * Model ServiceTicketFinalData + * + */ +export type ServiceTicketFinalData = Prisma.ServiceTicketFinalDataModel +/** + * Model OpportunityStage + * + */ +export type OpportunityStage = Prisma.OpportunityStageModel +/** + * Model OpportunityType + * + */ +export type OpportunityType = Prisma.OpportunityTypeModel +/** + * Model OpportunityStatus + * + */ +export type OpportunityStatus = Prisma.OpportunityStatusModel /** * Model Opportunity * */ export type Opportunity = Prisma.OpportunityModel +/** + * Model ScheduleStatus + * + */ +export type ScheduleStatus = Prisma.ScheduleStatusModel +/** + * Model ScheduleType + * + */ +export type ScheduleType = Prisma.ScheduleTypeModel +/** + * Model ScheduleSpan + * + */ +export type ScheduleSpan = Prisma.ScheduleSpanModel +/** + * Model Schedule + * + */ +export type Schedule = Prisma.ScheduleModel /** * Model CredentialType * @@ -94,6 +256,11 @@ export type Credential = Prisma.CredentialModel * */ export type GeneratedQuotes = Prisma.GeneratedQuotesModel +/** + * Model TaxCode + * + */ +export type TaxCode = Prisma.TaxCodeModel /** * Model CwMember * diff --git a/api/generated/prisma/commonInputTypes.ts b/api/generated/prisma/commonInputTypes.ts index 2427f6b..d683960 100644 --- a/api/generated/prisma/commonInputTypes.ts +++ b/api/generated/prisma/commonInputTypes.ts @@ -29,20 +29,18 @@ export type StringFilter<$PrismaModel = never> = { not?: Prisma.NestedStringFilter<$PrismaModel> | string } -export type DateTimeFilter<$PrismaModel = never> = { - equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> - notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> - lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string +export type EnumSyncJobTypeFilter<$PrismaModel = never> = { + equals?: $Enums.SyncJobType | Prisma.EnumSyncJobTypeFieldRefInput<$PrismaModel> + in?: $Enums.SyncJobType[] | Prisma.ListEnumSyncJobTypeFieldRefInput<$PrismaModel> + notIn?: $Enums.SyncJobType[] | Prisma.ListEnumSyncJobTypeFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumSyncJobTypeFilter<$PrismaModel> | $Enums.SyncJobType } -export type BoolFilter<$PrismaModel = never> = { - equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel> - not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean +export type EnumSyncJobStatusFilter<$PrismaModel = never> = { + equals?: $Enums.SyncJobStatus | Prisma.EnumSyncJobStatusFieldRefInput<$PrismaModel> + in?: $Enums.SyncJobStatus[] | Prisma.ListEnumSyncJobStatusFieldRefInput<$PrismaModel> + notIn?: $Enums.SyncJobStatus[] | Prisma.ListEnumSyncJobStatusFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumSyncJobStatusFilter<$PrismaModel> | $Enums.SyncJobStatus } export type DateTimeNullableFilter<$PrismaModel = never> = { @@ -56,6 +54,32 @@ export type DateTimeNullableFilter<$PrismaModel = never> = { not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null } +export type StringNullableFilter<$PrismaModel = never> = { + equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null + in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null + notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null + lt?: string | Prisma.StringFieldRefInput<$PrismaModel> + lte?: string | Prisma.StringFieldRefInput<$PrismaModel> + gt?: string | Prisma.StringFieldRefInput<$PrismaModel> + gte?: string | Prisma.StringFieldRefInput<$PrismaModel> + contains?: string | Prisma.StringFieldRefInput<$PrismaModel> + startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel> + endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel> + mode?: Prisma.QueryMode + not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null +} + +export type DateTimeFilter<$PrismaModel = never> = { + equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> + notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> + lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string +} + export type SortOrderInput = { sort: Prisma.SortOrder nulls?: Prisma.NullsOrder @@ -79,26 +103,24 @@ export type StringWithAggregatesFilter<$PrismaModel = never> = { _max?: Prisma.NestedStringFilter<$PrismaModel> } -export type DateTimeWithAggregatesFilter<$PrismaModel = never> = { - equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> - notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> - lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string +export type EnumSyncJobTypeWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.SyncJobType | Prisma.EnumSyncJobTypeFieldRefInput<$PrismaModel> + in?: $Enums.SyncJobType[] | Prisma.ListEnumSyncJobTypeFieldRefInput<$PrismaModel> + notIn?: $Enums.SyncJobType[] | Prisma.ListEnumSyncJobTypeFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumSyncJobTypeWithAggregatesFilter<$PrismaModel> | $Enums.SyncJobType _count?: Prisma.NestedIntFilter<$PrismaModel> - _min?: Prisma.NestedDateTimeFilter<$PrismaModel> - _max?: Prisma.NestedDateTimeFilter<$PrismaModel> + _min?: Prisma.NestedEnumSyncJobTypeFilter<$PrismaModel> + _max?: Prisma.NestedEnumSyncJobTypeFilter<$PrismaModel> } -export type BoolWithAggregatesFilter<$PrismaModel = never> = { - equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel> - not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean +export type EnumSyncJobStatusWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.SyncJobStatus | Prisma.EnumSyncJobStatusFieldRefInput<$PrismaModel> + in?: $Enums.SyncJobStatus[] | Prisma.ListEnumSyncJobStatusFieldRefInput<$PrismaModel> + notIn?: $Enums.SyncJobStatus[] | Prisma.ListEnumSyncJobStatusFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumSyncJobStatusWithAggregatesFilter<$PrismaModel> | $Enums.SyncJobStatus _count?: Prisma.NestedIntFilter<$PrismaModel> - _min?: Prisma.NestedBoolFilter<$PrismaModel> - _max?: Prisma.NestedBoolFilter<$PrismaModel> + _min?: Prisma.NestedEnumSyncJobStatusFilter<$PrismaModel> + _max?: Prisma.NestedEnumSyncJobStatusFilter<$PrismaModel> } export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = { @@ -115,21 +137,6 @@ export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = { _max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> } -export type StringNullableFilter<$PrismaModel = never> = { - equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null - in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null - notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null - lt?: string | Prisma.StringFieldRefInput<$PrismaModel> - lte?: string | Prisma.StringFieldRefInput<$PrismaModel> - gt?: string | Prisma.StringFieldRefInput<$PrismaModel> - gte?: string | Prisma.StringFieldRefInput<$PrismaModel> - contains?: string | Prisma.StringFieldRefInput<$PrismaModel> - startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel> - endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel> - mode?: Prisma.QueryMode - not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null -} - export type StringNullableWithAggregatesFilter<$PrismaModel = never> = { equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null @@ -148,6 +155,20 @@ export type StringNullableWithAggregatesFilter<$PrismaModel = never> = { _max?: Prisma.NestedStringNullableFilter<$PrismaModel> } +export type DateTimeWithAggregatesFilter<$PrismaModel = never> = { + equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> + notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> + lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string + _count?: Prisma.NestedIntFilter<$PrismaModel> + _min?: Prisma.NestedDateTimeFilter<$PrismaModel> + _max?: Prisma.NestedDateTimeFilter<$PrismaModel> +} + export type IntFilter<$PrismaModel = never> = { equals?: number | Prisma.IntFieldRefInput<$PrismaModel> in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> @@ -159,76 +180,6 @@ export type IntFilter<$PrismaModel = never> = { not?: Prisma.NestedIntFilter<$PrismaModel> | number } -export type IntWithAggregatesFilter<$PrismaModel = never> = { - equals?: number | Prisma.IntFieldRefInput<$PrismaModel> - in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> - notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> - lt?: number | Prisma.IntFieldRefInput<$PrismaModel> - lte?: number | Prisma.IntFieldRefInput<$PrismaModel> - gt?: number | Prisma.IntFieldRefInput<$PrismaModel> - gte?: number | Prisma.IntFieldRefInput<$PrismaModel> - not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number - _count?: Prisma.NestedIntFilter<$PrismaModel> - _avg?: Prisma.NestedFloatFilter<$PrismaModel> - _sum?: Prisma.NestedIntFilter<$PrismaModel> - _min?: Prisma.NestedIntFilter<$PrismaModel> - _max?: Prisma.NestedIntFilter<$PrismaModel> -} - -export type IntNullableFilter<$PrismaModel = never> = { - equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null - in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null - notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null - lt?: number | Prisma.IntFieldRefInput<$PrismaModel> - lte?: number | Prisma.IntFieldRefInput<$PrismaModel> - gt?: number | Prisma.IntFieldRefInput<$PrismaModel> - gte?: number | Prisma.IntFieldRefInput<$PrismaModel> - not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null -} - -export type FloatFilter<$PrismaModel = never> = { - equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> - in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> - notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> - lt?: number | Prisma.FloatFieldRefInput<$PrismaModel> - lte?: number | Prisma.FloatFieldRefInput<$PrismaModel> - gt?: number | Prisma.FloatFieldRefInput<$PrismaModel> - gte?: number | Prisma.FloatFieldRefInput<$PrismaModel> - not?: Prisma.NestedFloatFilter<$PrismaModel> | number -} - -export type IntNullableWithAggregatesFilter<$PrismaModel = never> = { - equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null - in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null - notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null - lt?: number | Prisma.IntFieldRefInput<$PrismaModel> - lte?: number | Prisma.IntFieldRefInput<$PrismaModel> - gt?: number | Prisma.IntFieldRefInput<$PrismaModel> - gte?: number | Prisma.IntFieldRefInput<$PrismaModel> - not?: Prisma.NestedIntNullableWithAggregatesFilter<$PrismaModel> | number | null - _count?: Prisma.NestedIntNullableFilter<$PrismaModel> - _avg?: Prisma.NestedFloatNullableFilter<$PrismaModel> - _sum?: Prisma.NestedIntNullableFilter<$PrismaModel> - _min?: Prisma.NestedIntNullableFilter<$PrismaModel> - _max?: Prisma.NestedIntNullableFilter<$PrismaModel> -} - -export type FloatWithAggregatesFilter<$PrismaModel = never> = { - equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> - in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> - notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> - lt?: number | Prisma.FloatFieldRefInput<$PrismaModel> - lte?: number | Prisma.FloatFieldRefInput<$PrismaModel> - gt?: number | Prisma.FloatFieldRefInput<$PrismaModel> - gte?: number | Prisma.FloatFieldRefInput<$PrismaModel> - not?: Prisma.NestedFloatWithAggregatesFilter<$PrismaModel> | number - _count?: Prisma.NestedIntFilter<$PrismaModel> - _avg?: Prisma.NestedFloatFilter<$PrismaModel> - _sum?: Prisma.NestedFloatFilter<$PrismaModel> - _min?: Prisma.NestedFloatFilter<$PrismaModel> - _max?: Prisma.NestedFloatFilter<$PrismaModel> -} - export type JsonFilter<$PrismaModel = never> = | Prisma.PatchUndefined< Prisma.Either>, Exclude>, 'path'>>, @@ -253,6 +204,22 @@ export type JsonFilterBase<$PrismaModel = never> = { not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter } +export type IntWithAggregatesFilter<$PrismaModel = never> = { + equals?: number | Prisma.IntFieldRefInput<$PrismaModel> + in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> + notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> + lt?: number | Prisma.IntFieldRefInput<$PrismaModel> + lte?: number | Prisma.IntFieldRefInput<$PrismaModel> + gt?: number | Prisma.IntFieldRefInput<$PrismaModel> + gte?: number | Prisma.IntFieldRefInput<$PrismaModel> + not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number + _count?: Prisma.NestedIntFilter<$PrismaModel> + _avg?: Prisma.NestedFloatFilter<$PrismaModel> + _sum?: Prisma.NestedIntFilter<$PrismaModel> + _min?: Prisma.NestedIntFilter<$PrismaModel> + _max?: Prisma.NestedIntFilter<$PrismaModel> +} + export type JsonWithAggregatesFilter<$PrismaModel = never> = | Prisma.PatchUndefined< Prisma.Either>, Exclude>, 'path'>>, @@ -280,6 +247,219 @@ export type JsonWithAggregatesFilterBase<$PrismaModel = never> = { _max?: Prisma.NestedJsonFilter<$PrismaModel> } +export type BoolFilter<$PrismaModel = never> = { + equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel> + not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean +} + +export type BoolWithAggregatesFilter<$PrismaModel = never> = { + equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel> + not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean + _count?: Prisma.NestedIntFilter<$PrismaModel> + _min?: Prisma.NestedBoolFilter<$PrismaModel> + _max?: Prisma.NestedBoolFilter<$PrismaModel> +} + +export type IntNullableFilter<$PrismaModel = never> = { + equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null + in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null + notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null + lt?: number | Prisma.IntFieldRefInput<$PrismaModel> + lte?: number | Prisma.IntFieldRefInput<$PrismaModel> + gt?: number | Prisma.IntFieldRefInput<$PrismaModel> + gte?: number | Prisma.IntFieldRefInput<$PrismaModel> + not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null +} + +export type IntNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null + in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null + notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null + lt?: number | Prisma.IntFieldRefInput<$PrismaModel> + lte?: number | Prisma.IntFieldRefInput<$PrismaModel> + gt?: number | Prisma.IntFieldRefInput<$PrismaModel> + gte?: number | Prisma.IntFieldRefInput<$PrismaModel> + not?: Prisma.NestedIntNullableWithAggregatesFilter<$PrismaModel> | number | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _avg?: Prisma.NestedFloatNullableFilter<$PrismaModel> + _sum?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedIntNullableFilter<$PrismaModel> + _max?: Prisma.NestedIntNullableFilter<$PrismaModel> +} + +export type EnumUSStateNullableFilter<$PrismaModel = never> = { + equals?: $Enums.USState | Prisma.EnumUSStateFieldRefInput<$PrismaModel> | null + in?: $Enums.USState[] | Prisma.ListEnumUSStateFieldRefInput<$PrismaModel> | null + notIn?: $Enums.USState[] | Prisma.ListEnumUSStateFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumUSStateNullableFilter<$PrismaModel> | $Enums.USState | null +} + +export type EnumCountryNullableFilter<$PrismaModel = never> = { + equals?: $Enums.Country | Prisma.EnumCountryFieldRefInput<$PrismaModel> | null + in?: $Enums.Country[] | Prisma.ListEnumCountryFieldRefInput<$PrismaModel> | null + notIn?: $Enums.Country[] | Prisma.ListEnumCountryFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumCountryNullableFilter<$PrismaModel> | $Enums.Country | null +} + +export type EnumUSStateNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.USState | Prisma.EnumUSStateFieldRefInput<$PrismaModel> | null + in?: $Enums.USState[] | Prisma.ListEnumUSStateFieldRefInput<$PrismaModel> | null + notIn?: $Enums.USState[] | Prisma.ListEnumUSStateFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumUSStateNullableWithAggregatesFilter<$PrismaModel> | $Enums.USState | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedEnumUSStateNullableFilter<$PrismaModel> + _max?: Prisma.NestedEnumUSStateNullableFilter<$PrismaModel> +} + +export type EnumCountryNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.Country | Prisma.EnumCountryFieldRefInput<$PrismaModel> | null + in?: $Enums.Country[] | Prisma.ListEnumCountryFieldRefInput<$PrismaModel> | null + notIn?: $Enums.Country[] | Prisma.ListEnumCountryFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumCountryNullableWithAggregatesFilter<$PrismaModel> | $Enums.Country | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedEnumCountryNullableFilter<$PrismaModel> + _max?: Prisma.NestedEnumCountryNullableFilter<$PrismaModel> +} + +export type EnumGenderTypeNullableFilter<$PrismaModel = never> = { + equals?: $Enums.GenderType | Prisma.EnumGenderTypeFieldRefInput<$PrismaModel> | null + in?: $Enums.GenderType[] | Prisma.ListEnumGenderTypeFieldRefInput<$PrismaModel> | null + notIn?: $Enums.GenderType[] | Prisma.ListEnumGenderTypeFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumGenderTypeNullableFilter<$PrismaModel> | $Enums.GenderType | null +} + +export type EnumPhoneTypeNullableFilter<$PrismaModel = never> = { + equals?: $Enums.PhoneType | Prisma.EnumPhoneTypeFieldRefInput<$PrismaModel> | null + in?: $Enums.PhoneType[] | Prisma.ListEnumPhoneTypeFieldRefInput<$PrismaModel> | null + notIn?: $Enums.PhoneType[] | Prisma.ListEnumPhoneTypeFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumPhoneTypeNullableFilter<$PrismaModel> | $Enums.PhoneType | null +} + +export type EnumGenderTypeNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.GenderType | Prisma.EnumGenderTypeFieldRefInput<$PrismaModel> | null + in?: $Enums.GenderType[] | Prisma.ListEnumGenderTypeFieldRefInput<$PrismaModel> | null + notIn?: $Enums.GenderType[] | Prisma.ListEnumGenderTypeFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumGenderTypeNullableWithAggregatesFilter<$PrismaModel> | $Enums.GenderType | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedEnumGenderTypeNullableFilter<$PrismaModel> + _max?: Prisma.NestedEnumGenderTypeNullableFilter<$PrismaModel> +} + +export type EnumPhoneTypeNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.PhoneType | Prisma.EnumPhoneTypeFieldRefInput<$PrismaModel> | null + in?: $Enums.PhoneType[] | Prisma.ListEnumPhoneTypeFieldRefInput<$PrismaModel> | null + notIn?: $Enums.PhoneType[] | Prisma.ListEnumPhoneTypeFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumPhoneTypeNullableWithAggregatesFilter<$PrismaModel> | $Enums.PhoneType | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedEnumPhoneTypeNullableFilter<$PrismaModel> + _max?: Prisma.NestedEnumPhoneTypeNullableFilter<$PrismaModel> +} + +export type FloatFilter<$PrismaModel = never> = { + equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> + in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> + notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> + lt?: number | Prisma.FloatFieldRefInput<$PrismaModel> + lte?: number | Prisma.FloatFieldRefInput<$PrismaModel> + gt?: number | Prisma.FloatFieldRefInput<$PrismaModel> + gte?: number | Prisma.FloatFieldRefInput<$PrismaModel> + not?: Prisma.NestedFloatFilter<$PrismaModel> | number +} + +export type FloatWithAggregatesFilter<$PrismaModel = never> = { + equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> + in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> + notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> + lt?: number | Prisma.FloatFieldRefInput<$PrismaModel> + lte?: number | Prisma.FloatFieldRefInput<$PrismaModel> + gt?: number | Prisma.FloatFieldRefInput<$PrismaModel> + gte?: number | Prisma.FloatFieldRefInput<$PrismaModel> + not?: Prisma.NestedFloatWithAggregatesFilter<$PrismaModel> | number + _count?: Prisma.NestedIntFilter<$PrismaModel> + _avg?: Prisma.NestedFloatFilter<$PrismaModel> + _sum?: Prisma.NestedFloatFilter<$PrismaModel> + _min?: Prisma.NestedFloatFilter<$PrismaModel> + _max?: Prisma.NestedFloatFilter<$PrismaModel> +} + +export type FloatNullableFilter<$PrismaModel = never> = { + equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null + in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null + notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null + lt?: number | Prisma.FloatFieldRefInput<$PrismaModel> + lte?: number | Prisma.FloatFieldRefInput<$PrismaModel> + gt?: number | Prisma.FloatFieldRefInput<$PrismaModel> + gte?: number | Prisma.FloatFieldRefInput<$PrismaModel> + not?: Prisma.NestedFloatNullableFilter<$PrismaModel> | number | null +} + +export type FloatNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null + in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null + notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null + lt?: number | Prisma.FloatFieldRefInput<$PrismaModel> + lte?: number | Prisma.FloatFieldRefInput<$PrismaModel> + gt?: number | Prisma.FloatFieldRefInput<$PrismaModel> + gte?: number | Prisma.FloatFieldRefInput<$PrismaModel> + not?: Prisma.NestedFloatNullableWithAggregatesFilter<$PrismaModel> | number | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _avg?: Prisma.NestedFloatNullableFilter<$PrismaModel> + _sum?: Prisma.NestedFloatNullableFilter<$PrismaModel> + _min?: Prisma.NestedFloatNullableFilter<$PrismaModel> + _max?: Prisma.NestedFloatNullableFilter<$PrismaModel> +} + +export type EnumBillingMethodFilter<$PrismaModel = never> = { + equals?: $Enums.BillingMethod | Prisma.EnumBillingMethodFieldRefInput<$PrismaModel> + in?: $Enums.BillingMethod[] | Prisma.ListEnumBillingMethodFieldRefInput<$PrismaModel> + notIn?: $Enums.BillingMethod[] | Prisma.ListEnumBillingMethodFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumBillingMethodFilter<$PrismaModel> | $Enums.BillingMethod +} + +export type EnumBillingTypeFilter<$PrismaModel = never> = { + equals?: $Enums.BillingType | Prisma.EnumBillingTypeFieldRefInput<$PrismaModel> + in?: $Enums.BillingType[] | Prisma.ListEnumBillingTypeFieldRefInput<$PrismaModel> + notIn?: $Enums.BillingType[] | Prisma.ListEnumBillingTypeFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumBillingTypeFilter<$PrismaModel> | $Enums.BillingType +} + +export type EnumBillingMethodWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.BillingMethod | Prisma.EnumBillingMethodFieldRefInput<$PrismaModel> + in?: $Enums.BillingMethod[] | Prisma.ListEnumBillingMethodFieldRefInput<$PrismaModel> + notIn?: $Enums.BillingMethod[] | Prisma.ListEnumBillingMethodFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumBillingMethodWithAggregatesFilter<$PrismaModel> | $Enums.BillingMethod + _count?: Prisma.NestedIntFilter<$PrismaModel> + _min?: Prisma.NestedEnumBillingMethodFilter<$PrismaModel> + _max?: Prisma.NestedEnumBillingMethodFilter<$PrismaModel> +} + +export type EnumBillingTypeWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.BillingType | Prisma.EnumBillingTypeFieldRefInput<$PrismaModel> + in?: $Enums.BillingType[] | Prisma.ListEnumBillingTypeFieldRefInput<$PrismaModel> + notIn?: $Enums.BillingType[] | Prisma.ListEnumBillingTypeFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumBillingTypeWithAggregatesFilter<$PrismaModel> | $Enums.BillingType + _count?: Prisma.NestedIntFilter<$PrismaModel> + _min?: Prisma.NestedEnumBillingTypeFilter<$PrismaModel> + _max?: Prisma.NestedEnumBillingTypeFilter<$PrismaModel> +} + +export type EnumOpportunityInterestNullableFilter<$PrismaModel = never> = { + equals?: $Enums.OpportunityInterest | Prisma.EnumOpportunityInterestFieldRefInput<$PrismaModel> | null + in?: $Enums.OpportunityInterest[] | Prisma.ListEnumOpportunityInterestFieldRefInput<$PrismaModel> | null + notIn?: $Enums.OpportunityInterest[] | Prisma.ListEnumOpportunityInterestFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumOpportunityInterestNullableFilter<$PrismaModel> | $Enums.OpportunityInterest | null +} + +export type EnumOpportunityInterestNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.OpportunityInterest | Prisma.EnumOpportunityInterestFieldRefInput<$PrismaModel> | null + in?: $Enums.OpportunityInterest[] | Prisma.ListEnumOpportunityInterestFieldRefInput<$PrismaModel> | null + notIn?: $Enums.OpportunityInterest[] | Prisma.ListEnumOpportunityInterestFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumOpportunityInterestNullableWithAggregatesFilter<$PrismaModel> | $Enums.OpportunityInterest | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedEnumOpportunityInterestNullableFilter<$PrismaModel> + _max?: Prisma.NestedEnumOpportunityInterestNullableFilter<$PrismaModel> +} + export type BytesFilter<$PrismaModel = never> = { equals?: runtime.Bytes | Prisma.BytesFieldRefInput<$PrismaModel> in?: runtime.Bytes[] | Prisma.ListBytesFieldRefInput<$PrismaModel> @@ -311,20 +491,18 @@ export type NestedStringFilter<$PrismaModel = never> = { not?: Prisma.NestedStringFilter<$PrismaModel> | string } -export type NestedDateTimeFilter<$PrismaModel = never> = { - equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> - notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> - lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string +export type NestedEnumSyncJobTypeFilter<$PrismaModel = never> = { + equals?: $Enums.SyncJobType | Prisma.EnumSyncJobTypeFieldRefInput<$PrismaModel> + in?: $Enums.SyncJobType[] | Prisma.ListEnumSyncJobTypeFieldRefInput<$PrismaModel> + notIn?: $Enums.SyncJobType[] | Prisma.ListEnumSyncJobTypeFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumSyncJobTypeFilter<$PrismaModel> | $Enums.SyncJobType } -export type NestedBoolFilter<$PrismaModel = never> = { - equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel> - not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean +export type NestedEnumSyncJobStatusFilter<$PrismaModel = never> = { + equals?: $Enums.SyncJobStatus | Prisma.EnumSyncJobStatusFieldRefInput<$PrismaModel> + in?: $Enums.SyncJobStatus[] | Prisma.ListEnumSyncJobStatusFieldRefInput<$PrismaModel> + notIn?: $Enums.SyncJobStatus[] | Prisma.ListEnumSyncJobStatusFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumSyncJobStatusFilter<$PrismaModel> | $Enums.SyncJobStatus } export type NestedDateTimeNullableFilter<$PrismaModel = never> = { @@ -338,6 +516,31 @@ export type NestedDateTimeNullableFilter<$PrismaModel = never> = { not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null } +export type NestedStringNullableFilter<$PrismaModel = never> = { + equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null + in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null + notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null + lt?: string | Prisma.StringFieldRefInput<$PrismaModel> + lte?: string | Prisma.StringFieldRefInput<$PrismaModel> + gt?: string | Prisma.StringFieldRefInput<$PrismaModel> + gte?: string | Prisma.StringFieldRefInput<$PrismaModel> + contains?: string | Prisma.StringFieldRefInput<$PrismaModel> + startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel> + endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel> + not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null +} + +export type NestedDateTimeFilter<$PrismaModel = never> = { + equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> + notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> + lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string +} + export type NestedStringWithAggregatesFilter<$PrismaModel = never> = { equals?: string | Prisma.StringFieldRefInput<$PrismaModel> in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> @@ -366,26 +569,24 @@ export type NestedIntFilter<$PrismaModel = never> = { not?: Prisma.NestedIntFilter<$PrismaModel> | number } -export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = { - equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> - notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> - lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> - not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string +export type NestedEnumSyncJobTypeWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.SyncJobType | Prisma.EnumSyncJobTypeFieldRefInput<$PrismaModel> + in?: $Enums.SyncJobType[] | Prisma.ListEnumSyncJobTypeFieldRefInput<$PrismaModel> + notIn?: $Enums.SyncJobType[] | Prisma.ListEnumSyncJobTypeFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumSyncJobTypeWithAggregatesFilter<$PrismaModel> | $Enums.SyncJobType _count?: Prisma.NestedIntFilter<$PrismaModel> - _min?: Prisma.NestedDateTimeFilter<$PrismaModel> - _max?: Prisma.NestedDateTimeFilter<$PrismaModel> + _min?: Prisma.NestedEnumSyncJobTypeFilter<$PrismaModel> + _max?: Prisma.NestedEnumSyncJobTypeFilter<$PrismaModel> } -export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = { - equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel> - not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean +export type NestedEnumSyncJobStatusWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.SyncJobStatus | Prisma.EnumSyncJobStatusFieldRefInput<$PrismaModel> + in?: $Enums.SyncJobStatus[] | Prisma.ListEnumSyncJobStatusFieldRefInput<$PrismaModel> + notIn?: $Enums.SyncJobStatus[] | Prisma.ListEnumSyncJobStatusFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumSyncJobStatusWithAggregatesFilter<$PrismaModel> | $Enums.SyncJobStatus _count?: Prisma.NestedIntFilter<$PrismaModel> - _min?: Prisma.NestedBoolFilter<$PrismaModel> - _max?: Prisma.NestedBoolFilter<$PrismaModel> + _min?: Prisma.NestedEnumSyncJobStatusFilter<$PrismaModel> + _max?: Prisma.NestedEnumSyncJobStatusFilter<$PrismaModel> } export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = { @@ -413,20 +614,6 @@ export type NestedIntNullableFilter<$PrismaModel = never> = { not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null } -export type NestedStringNullableFilter<$PrismaModel = never> = { - equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null - in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null - notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null - lt?: string | Prisma.StringFieldRefInput<$PrismaModel> - lte?: string | Prisma.StringFieldRefInput<$PrismaModel> - gt?: string | Prisma.StringFieldRefInput<$PrismaModel> - gte?: string | Prisma.StringFieldRefInput<$PrismaModel> - contains?: string | Prisma.StringFieldRefInput<$PrismaModel> - startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel> - endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel> - not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null -} - export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = { equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null @@ -444,6 +631,20 @@ export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = { _max?: Prisma.NestedStringNullableFilter<$PrismaModel> } +export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = { + equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> + notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> + lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> + not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string + _count?: Prisma.NestedIntFilter<$PrismaModel> + _min?: Prisma.NestedDateTimeFilter<$PrismaModel> + _max?: Prisma.NestedDateTimeFilter<$PrismaModel> +} + export type NestedIntWithAggregatesFilter<$PrismaModel = never> = { equals?: number | Prisma.IntFieldRefInput<$PrismaModel> in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> @@ -471,6 +672,43 @@ export type NestedFloatFilter<$PrismaModel = never> = { not?: Prisma.NestedFloatFilter<$PrismaModel> | number } +export type NestedJsonFilter<$PrismaModel = never> = +| Prisma.PatchUndefined< + Prisma.Either>, Exclude>, 'path'>>, + Required> + > +| Prisma.OptionalFlat>, 'path'>> + +export type NestedJsonFilterBase<$PrismaModel = never> = { + equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter + path?: string[] + mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel> + string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel> + string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel> + string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel> + array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null + array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null + array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null + lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> + lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> + gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> + gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> + not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter +} + +export type NestedBoolFilter<$PrismaModel = never> = { + equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel> + not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean +} + +export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = { + equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel> + not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean + _count?: Prisma.NestedIntFilter<$PrismaModel> + _min?: Prisma.NestedBoolFilter<$PrismaModel> + _max?: Prisma.NestedBoolFilter<$PrismaModel> +} + export type NestedIntNullableWithAggregatesFilter<$PrismaModel = never> = { equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null @@ -498,6 +736,74 @@ export type NestedFloatNullableFilter<$PrismaModel = never> = { not?: Prisma.NestedFloatNullableFilter<$PrismaModel> | number | null } +export type NestedEnumUSStateNullableFilter<$PrismaModel = never> = { + equals?: $Enums.USState | Prisma.EnumUSStateFieldRefInput<$PrismaModel> | null + in?: $Enums.USState[] | Prisma.ListEnumUSStateFieldRefInput<$PrismaModel> | null + notIn?: $Enums.USState[] | Prisma.ListEnumUSStateFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumUSStateNullableFilter<$PrismaModel> | $Enums.USState | null +} + +export type NestedEnumCountryNullableFilter<$PrismaModel = never> = { + equals?: $Enums.Country | Prisma.EnumCountryFieldRefInput<$PrismaModel> | null + in?: $Enums.Country[] | Prisma.ListEnumCountryFieldRefInput<$PrismaModel> | null + notIn?: $Enums.Country[] | Prisma.ListEnumCountryFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumCountryNullableFilter<$PrismaModel> | $Enums.Country | null +} + +export type NestedEnumUSStateNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.USState | Prisma.EnumUSStateFieldRefInput<$PrismaModel> | null + in?: $Enums.USState[] | Prisma.ListEnumUSStateFieldRefInput<$PrismaModel> | null + notIn?: $Enums.USState[] | Prisma.ListEnumUSStateFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumUSStateNullableWithAggregatesFilter<$PrismaModel> | $Enums.USState | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedEnumUSStateNullableFilter<$PrismaModel> + _max?: Prisma.NestedEnumUSStateNullableFilter<$PrismaModel> +} + +export type NestedEnumCountryNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.Country | Prisma.EnumCountryFieldRefInput<$PrismaModel> | null + in?: $Enums.Country[] | Prisma.ListEnumCountryFieldRefInput<$PrismaModel> | null + notIn?: $Enums.Country[] | Prisma.ListEnumCountryFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumCountryNullableWithAggregatesFilter<$PrismaModel> | $Enums.Country | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedEnumCountryNullableFilter<$PrismaModel> + _max?: Prisma.NestedEnumCountryNullableFilter<$PrismaModel> +} + +export type NestedEnumGenderTypeNullableFilter<$PrismaModel = never> = { + equals?: $Enums.GenderType | Prisma.EnumGenderTypeFieldRefInput<$PrismaModel> | null + in?: $Enums.GenderType[] | Prisma.ListEnumGenderTypeFieldRefInput<$PrismaModel> | null + notIn?: $Enums.GenderType[] | Prisma.ListEnumGenderTypeFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumGenderTypeNullableFilter<$PrismaModel> | $Enums.GenderType | null +} + +export type NestedEnumPhoneTypeNullableFilter<$PrismaModel = never> = { + equals?: $Enums.PhoneType | Prisma.EnumPhoneTypeFieldRefInput<$PrismaModel> | null + in?: $Enums.PhoneType[] | Prisma.ListEnumPhoneTypeFieldRefInput<$PrismaModel> | null + notIn?: $Enums.PhoneType[] | Prisma.ListEnumPhoneTypeFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumPhoneTypeNullableFilter<$PrismaModel> | $Enums.PhoneType | null +} + +export type NestedEnumGenderTypeNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.GenderType | Prisma.EnumGenderTypeFieldRefInput<$PrismaModel> | null + in?: $Enums.GenderType[] | Prisma.ListEnumGenderTypeFieldRefInput<$PrismaModel> | null + notIn?: $Enums.GenderType[] | Prisma.ListEnumGenderTypeFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumGenderTypeNullableWithAggregatesFilter<$PrismaModel> | $Enums.GenderType | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedEnumGenderTypeNullableFilter<$PrismaModel> + _max?: Prisma.NestedEnumGenderTypeNullableFilter<$PrismaModel> +} + +export type NestedEnumPhoneTypeNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.PhoneType | Prisma.EnumPhoneTypeFieldRefInput<$PrismaModel> | null + in?: $Enums.PhoneType[] | Prisma.ListEnumPhoneTypeFieldRefInput<$PrismaModel> | null + notIn?: $Enums.PhoneType[] | Prisma.ListEnumPhoneTypeFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumPhoneTypeNullableWithAggregatesFilter<$PrismaModel> | $Enums.PhoneType | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedEnumPhoneTypeNullableFilter<$PrismaModel> + _max?: Prisma.NestedEnumPhoneTypeNullableFilter<$PrismaModel> +} + export type NestedFloatWithAggregatesFilter<$PrismaModel = never> = { equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> @@ -514,28 +820,71 @@ export type NestedFloatWithAggregatesFilter<$PrismaModel = never> = { _max?: Prisma.NestedFloatFilter<$PrismaModel> } -export type NestedJsonFilter<$PrismaModel = never> = -| Prisma.PatchUndefined< - Prisma.Either>, Exclude>, 'path'>>, - Required> - > -| Prisma.OptionalFlat>, 'path'>> +export type NestedFloatNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null + in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null + notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null + lt?: number | Prisma.FloatFieldRefInput<$PrismaModel> + lte?: number | Prisma.FloatFieldRefInput<$PrismaModel> + gt?: number | Prisma.FloatFieldRefInput<$PrismaModel> + gte?: number | Prisma.FloatFieldRefInput<$PrismaModel> + not?: Prisma.NestedFloatNullableWithAggregatesFilter<$PrismaModel> | number | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _avg?: Prisma.NestedFloatNullableFilter<$PrismaModel> + _sum?: Prisma.NestedFloatNullableFilter<$PrismaModel> + _min?: Prisma.NestedFloatNullableFilter<$PrismaModel> + _max?: Prisma.NestedFloatNullableFilter<$PrismaModel> +} -export type NestedJsonFilterBase<$PrismaModel = never> = { - equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter - path?: string[] - mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel> - string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel> - string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel> - string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel> - array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null - array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null - array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null - lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> - lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> - gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> - gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> - not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter +export type NestedEnumBillingMethodFilter<$PrismaModel = never> = { + equals?: $Enums.BillingMethod | Prisma.EnumBillingMethodFieldRefInput<$PrismaModel> + in?: $Enums.BillingMethod[] | Prisma.ListEnumBillingMethodFieldRefInput<$PrismaModel> + notIn?: $Enums.BillingMethod[] | Prisma.ListEnumBillingMethodFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumBillingMethodFilter<$PrismaModel> | $Enums.BillingMethod +} + +export type NestedEnumBillingTypeFilter<$PrismaModel = never> = { + equals?: $Enums.BillingType | Prisma.EnumBillingTypeFieldRefInput<$PrismaModel> + in?: $Enums.BillingType[] | Prisma.ListEnumBillingTypeFieldRefInput<$PrismaModel> + notIn?: $Enums.BillingType[] | Prisma.ListEnumBillingTypeFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumBillingTypeFilter<$PrismaModel> | $Enums.BillingType +} + +export type NestedEnumBillingMethodWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.BillingMethod | Prisma.EnumBillingMethodFieldRefInput<$PrismaModel> + in?: $Enums.BillingMethod[] | Prisma.ListEnumBillingMethodFieldRefInput<$PrismaModel> + notIn?: $Enums.BillingMethod[] | Prisma.ListEnumBillingMethodFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumBillingMethodWithAggregatesFilter<$PrismaModel> | $Enums.BillingMethod + _count?: Prisma.NestedIntFilter<$PrismaModel> + _min?: Prisma.NestedEnumBillingMethodFilter<$PrismaModel> + _max?: Prisma.NestedEnumBillingMethodFilter<$PrismaModel> +} + +export type NestedEnumBillingTypeWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.BillingType | Prisma.EnumBillingTypeFieldRefInput<$PrismaModel> + in?: $Enums.BillingType[] | Prisma.ListEnumBillingTypeFieldRefInput<$PrismaModel> + notIn?: $Enums.BillingType[] | Prisma.ListEnumBillingTypeFieldRefInput<$PrismaModel> + not?: Prisma.NestedEnumBillingTypeWithAggregatesFilter<$PrismaModel> | $Enums.BillingType + _count?: Prisma.NestedIntFilter<$PrismaModel> + _min?: Prisma.NestedEnumBillingTypeFilter<$PrismaModel> + _max?: Prisma.NestedEnumBillingTypeFilter<$PrismaModel> +} + +export type NestedEnumOpportunityInterestNullableFilter<$PrismaModel = never> = { + equals?: $Enums.OpportunityInterest | Prisma.EnumOpportunityInterestFieldRefInput<$PrismaModel> | null + in?: $Enums.OpportunityInterest[] | Prisma.ListEnumOpportunityInterestFieldRefInput<$PrismaModel> | null + notIn?: $Enums.OpportunityInterest[] | Prisma.ListEnumOpportunityInterestFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumOpportunityInterestNullableFilter<$PrismaModel> | $Enums.OpportunityInterest | null +} + +export type NestedEnumOpportunityInterestNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: $Enums.OpportunityInterest | Prisma.EnumOpportunityInterestFieldRefInput<$PrismaModel> | null + in?: $Enums.OpportunityInterest[] | Prisma.ListEnumOpportunityInterestFieldRefInput<$PrismaModel> | null + notIn?: $Enums.OpportunityInterest[] | Prisma.ListEnumOpportunityInterestFieldRefInput<$PrismaModel> | null + not?: Prisma.NestedEnumOpportunityInterestNullableWithAggregatesFilter<$PrismaModel> | $Enums.OpportunityInterest | null + _count?: Prisma.NestedIntNullableFilter<$PrismaModel> + _min?: Prisma.NestedEnumOpportunityInterestNullableFilter<$PrismaModel> + _max?: Prisma.NestedEnumOpportunityInterestNullableFilter<$PrismaModel> } export type NestedBytesFilter<$PrismaModel = never> = { diff --git a/api/generated/prisma/enums.ts b/api/generated/prisma/enums.ts index 043572d..37e878a 100644 --- a/api/generated/prisma/enums.ts +++ b/api/generated/prisma/enums.ts @@ -9,7 +9,138 @@ * 🟢 You can import this file directly. */ +export const PhoneType = { + DIRECT: 'DIRECT', + MOBILE: 'MOBILE', + HOME: 'HOME', + COMPANY: 'COMPANY', + SITE: 'SITE' +} as const + +export type PhoneType = (typeof PhoneType)[keyof typeof PhoneType] -// This file is empty because there are no enums in the schema. -export {} +export const FaxType = { + FAX: 'FAX', + COMPANY: 'COMPANY', + SITE: 'SITE' +} as const + +export type FaxType = (typeof FaxType)[keyof typeof FaxType] + + +export const BillingMethod = { + ACTUAL_RATES: 'ACTUAL_RATES', + FIXED_FEE: 'FIXED_FEE', + NOT_TO_EXCEED: 'NOT_TO_EXCEED', + OVERRIDE_RATE: 'OVERRIDE_RATE' +} as const + +export type BillingMethod = (typeof BillingMethod)[keyof typeof BillingMethod] + + +export const BillingType = { + STANDARD: 'STANDARD', + PROJECT: 'PROJECT' +} as const + +export type BillingType = (typeof BillingType)[keyof typeof BillingType] + + +export const GenderType = { + MALE: 'MALE', + FEMALE: 'FEMALE' +} as const + +export type GenderType = (typeof GenderType)[keyof typeof GenderType] + + +export const USState = { + AL: 'AL', + AK: 'AK', + AZ: 'AZ', + AR: 'AR', + CA: 'CA', + CO: 'CO', + CT: 'CT', + DE: 'DE', + FL: 'FL', + GA: 'GA', + HI: 'HI', + ID: 'ID', + IL: 'IL', + IN: 'IN', + IA: 'IA', + KS: 'KS', + KY: 'KY', + LA: 'LA', + ME: 'ME', + MD: 'MD', + MA: 'MA', + MI: 'MI', + MN: 'MN', + MS: 'MS', + MO: 'MO', + MT: 'MT', + NE: 'NE', + NV: 'NV', + NH: 'NH', + NJ: 'NJ', + NM: 'NM', + NY: 'NY', + NC: 'NC', + ND: 'ND', + OH: 'OH', + OK: 'OK', + OR: 'OR', + PA: 'PA', + RI: 'RI', + SC: 'SC', + SD: 'SD', + TN: 'TN', + TX: 'TX', + UT: 'UT', + VT: 'VT', + VA: 'VA', + WA: 'WA', + WV: 'WV', + WI: 'WI', + WY: 'WY' +} as const + +export type USState = (typeof USState)[keyof typeof USState] + + +export const Country = { + US: 'US' +} as const + +export type Country = (typeof Country)[keyof typeof Country] + + +export const OpportunityInterest = { + HOT: 'HOT', + WARM: 'WARM', + COLD: 'COLD' +} as const + +export type OpportunityInterest = (typeof OpportunityInterest)[keyof typeof OpportunityInterest] + + +export const SyncJobType = { + FULL_SYNC: 'FULL_SYNC', + INCREMENTAL_SYNC: 'INCREMENTAL_SYNC' +} as const + +export type SyncJobType = (typeof SyncJobType)[keyof typeof SyncJobType] + + +export const SyncJobStatus = { + QUEUED: 'QUEUED', + RUNNING: 'RUNNING', + COMPLETED: 'COMPLETED', + FAILED: 'FAILED', + TIMED_OUT: 'TIMED_OUT' +} as const + +export type SyncJobStatus = (typeof SyncJobStatus)[keyof typeof SyncJobStatus] diff --git a/api/generated/prisma/internal/class.ts b/api/generated/prisma/internal/class.ts index aa277ad..e3842f0 100644 --- a/api/generated/prisma/internal/class.ts +++ b/api/generated/prisma/internal/class.ts @@ -17,18 +17,26 @@ import type * as Prisma from "./prismaNamespace.ts" const config: runtime.GetPrismaClientConfig = { "previewFeatures": [], - "clientVersion": "7.3.0", - "engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735", + "clientVersion": "7.5.0", + "engineVersion": "280c870be64f457428992c43c1f6d557fab6e29e", "activeProvider": "postgresql", - "inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel Session {\n id String @id @default(uuid())\n sessionKey String @unique @default(cuid())\n userId String\n expires DateTime\n refreshTokenGenerated Boolean @default(false)\n refreshedAt DateTime?\n invalidatedAt DateTime?\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel User {\n id String @id @default(cuid())\n roles Role[]\n permissions String?\n login String @unique\n name String?\n email String @unique\n emailVerified DateTime?\n image String?\n\n cwIdentifier String?\n\n userId String @unique\n token String?\n\n sessions Session[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n generatedQuotes GeneratedQuotes[]\n}\n\nmodel Role {\n id String @id @default(uuid())\n title String\n moniker String @unique // e.g. admin, super_admin, moderator\n\n permissions String\n users User[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel UnifiSite {\n id String @id @default(cuid())\n name String\n\n siteId String @unique\n\n companyId String?\n company Company? @relation(fields: [companyId], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Company {\n id String @id @default(cuid())\n name String\n\n cw_CompanyId Int @unique\n cw_Identifier String @unique\n\n credentials Credential[]\n unifiSites UnifiSite[]\n opportunities Opportunity[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CatalogItem {\n id String @id @default(cuid())\n cwCatalogId Int @unique\n identifier String? @unique\n name String\n description String?\n customerDescription String?\n internalNotes String?\n\n linkedItems CatalogItem[] @relation(\"LinkedItems\")\n linkedTo CatalogItem[] @relation(\"LinkedItems\")\n\n category String?\n categoryCwId Int?\n subcategory String?\n subcategoryCwId Int?\n\n manufacturer String?\n manufactureCwId Int?\n\n partNumber String?\n\n vendorName String?\n vendorSku String?\n vendorCwId Int?\n\n price Float\n cost Float\n\n inactive Boolean @default(false)\n salesTaxable Boolean @default(true)\n\n onHand Int @default(0)\n cwLastUpdated DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Opportunity {\n id String @id @default(cuid())\n cwOpportunityId Int @unique\n name String\n notes String?\n\n generatedQuotes GeneratedQuotes[]\n\n // Stage / status / priority / type / rating stored as JSON references\n // so we don't need separate lookup tables for CW enums\n typeName String?\n typeCwId Int?\n stageName String?\n stageCwId Int?\n statusName String?\n statusCwId Int?\n priorityName String?\n priorityCwId Int?\n ratingName String?\n ratingCwId Int?\n source String?\n campaignName String?\n campaignCwId Int?\n\n // Sales rep references\n primarySalesRepName String?\n primarySalesRepIdentifier String?\n primarySalesRepCwId Int?\n secondarySalesRepName String?\n secondarySalesRepIdentifier String?\n secondarySalesRepCwId Int?\n\n // Company / contact / site\n companyCwId Int?\n companyName String?\n contactCwId Int?\n contactName String?\n siteCwId Int?\n siteName String?\n customerPO String?\n\n // Financials\n totalSalesTax Float @default(0)\n probability Float @default(0)\n\n // Location / department\n locationName String?\n locationCwId Int?\n departmentName String?\n departmentCwId Int?\n\n // Dates\n expectedCloseDate DateTime?\n pipelineChangeDate DateTime?\n dateBecameLead DateTime?\n closedDate DateTime?\n closedFlag Boolean @default(false)\n closedByName String?\n closedByCwId Int?\n\n // Internal relation to Company (optional, linked by cwCompanyId)\n companyId String?\n company Company? @relation(fields: [companyId], references: [id])\n\n // Local product sequence — array of CW forecast item IDs in display order.\n // When present, fetchProducts() uses this order instead of CW sequenceNumber.\n productSequence Int[] @default([])\n\n cwLastUpdated DateTime?\n cwDateEntered DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CredentialType {\n id String @id @default(cuid())\n name String @unique\n\n permissionScope String\n icon String?\n fields Json\n\n credentials Credential[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel SecureValue {\n id String @id @default(cuid())\n name String\n\n content String // Encrypted content\n hash String // Hash of the original content for integrity verification and Search\n\n credentialId String\n credential Credential @relation(fields: [credentialId], references: [id], onDelete: Cascade)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Credential {\n id String @id @default(cuid())\n name String\n notes String?\n subCredentialOfId String?\n subCredentialOf Credential? @relation(\"SubCredentials\", fields: [subCredentialOfId], references: [id], onDelete: Cascade)\n subCredentials Credential[] @relation(\"SubCredentials\")\n\n typeId String\n type CredentialType @relation(fields: [typeId], references: [id], onDelete: Cascade)\n\n fields Json\n\n companyId String\n company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)\n\n securevalues SecureValue[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel GeneratedQuotes {\n id String @id @default(uuid())\n\n quoteRegenData Json @default(\"{}\") // Store any additional data needed for quote regeneration, such as product details, pricing, etc.\n quoteRegenParams Json @default(\"{}\") // Store parameters used for quote regeneration, such as template ID, formatting options, etc.\n quoteRegenHash String @unique @default(\"\")\n\n downloads Json @default(\"[]\") // Array of download records with timestamp and user info\n\n quoteFile Bytes\n quoteFileName String\n\n opportunityId String\n opportunity Opportunity @relation(fields: [opportunityId], references: [id], onDelete: Cascade)\n\n createdById String?\n createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CwMember {\n id String @id @default(cuid())\n\n cwMemberId Int @unique\n identifier String @unique\n firstName String\n lastName String\n officeEmail String?\n inactiveFlag Boolean @default(false)\n\n apiKey String?\n\n cwLastUpdated DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n", + "inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nenum PhoneType {\n DIRECT\n MOBILE\n HOME\n COMPANY // Main company line, not direct to contact\n SITE // Main site line, not direct to contact\n}\n\nenum FaxType {\n FAX // Direct fax line to contact\n COMPANY // Main company fax line, not direct to contact\n SITE // Main site fax line, not direct to contact\n}\n\nenum BillingMethod {\n ACTUAL_RATES\n FIXED_FEE\n NOT_TO_EXCEED // Requires \"Bill Ticket Seperately\"\n OVERRIDE_RATE // Shows hourly rate field\n}\n\nenum BillingType {\n STANDARD\n PROJECT\n}\n\n// By human nature, there are only two genders.\nenum GenderType {\n MALE\n FEMALE\n}\n\nenum USState {\n AL\n AK\n AZ\n AR\n CA\n CO\n CT\n DE\n FL\n GA\n HI\n ID\n IL\n IN\n IA\n KS\n KY\n LA\n ME\n MD\n MA\n MI\n MN\n MS\n MO\n MT\n NE\n NV\n NH\n NJ\n NM\n NY\n NC\n ND\n OH\n OK\n OR\n PA\n RI\n SC\n SD\n TN\n TX\n UT\n VT\n VA\n WA\n WV\n WI\n WY\n}\n\nenum Country {\n US\n}\n\nenum OpportunityInterest {\n HOT\n WARM\n COLD\n}\n\n// ---- Sync Job Tracking ----\n\nenum SyncJobType {\n FULL_SYNC\n INCREMENTAL_SYNC\n}\n\nenum SyncJobStatus {\n QUEUED\n RUNNING\n COMPLETED\n FAILED\n TIMED_OUT\n}\n\nmodel SyncJobRun {\n id String @id @default(uuid())\n jobType SyncJobType\n status SyncJobStatus @default(QUEUED)\n triggeredBy String @default(\"system\")\n\n startedAt DateTime?\n completedAt DateTime?\n\n errorSummary String?\n\n steps SyncStepLog[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel SyncStepLog {\n id String @id @default(uuid())\n\n syncJobRunId String\n syncJobRun SyncJobRun @relation(fields: [syncJobRunId], references: [id], onDelete: Cascade)\n\n tableName String\n syncMode String // \"full\" or \"incremental\"\n\n recordsProcessed Int @default(0)\n recordsInserted Int @default(0)\n recordsSkipped Int @default(0)\n recordsFailed Int @default(0)\n recordsDeleted Int @default(0)\n\n sampleErrors Json @default(\"[]\")\n durationMs Int @default(0)\n\n createdAt DateTime @default(now())\n}\n\nmodel Session {\n id String @id @default(uuid())\n sessionKey String @unique @default(cuid())\n userId String\n expires DateTime\n refreshTokenGenerated Boolean @default(false)\n refreshedAt DateTime?\n invalidatedAt DateTime?\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel User {\n id String @id @default(uuid())\n roles Role[]\n permissions String?\n login String @unique\n firstName String?\n lastName String?\n email String @unique\n image String?\n\n title String?\n\n active Boolean @default(true)\n hidden Boolean @default(false)\n\n cwIdentifier String? @unique\n cwMemberId Int? @unique\n cwMember CwMember? @relation(fields: [cwMemberId], references: [cwMemberId])\n\n userId String? @unique\n token String?\n\n sessions Session[]\n\n companiesDeleted Company[] @relation(\"DeletedBy\")\n companiesEntered Company[] @relation(\"EnteredBy\")\n\n opportunities Opportunity[] @relation(\"PrimarySalesRep\")\n opportunitiesSecondary Opportunity[] @relation(\"SecondarySalesRep\")\n\n generatedQuotes GeneratedQuotes[]\n companyAddresses CompanyAddress[]\n\n updatedBy String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n serviceTicketsOwned ServiceTicket[] @relation(\"ServiceTicketOwner\")\n serviceTicketsClosed ServiceTicket[] @relation(\"ServiceTicketClosedBy\")\n serviceTicketsCreated ServiceTicket[] @relation(\"ServiceTicketCreatedBy\")\n serviceTicketsUpdated ServiceTicket[] @relation(\"ServiceTicketUpdatedBy\")\n serviceTicketNotes ServiceTicketNote[] @relation(\"ServiceTicketNoteAuthor\")\n}\n\nmodel Role {\n id String @id @default(uuid())\n title String\n moniker String @unique // e.g. admin, super_admin, moderator\n\n permissions String\n users User[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CorporateLocation {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String?\n\n updatedById String?\n\n addressLine1 String?\n addressLine2 String?\n city String?\n state USState?\n zipCode String?\n country Country?\n\n inactiveFlag Boolean @default(false) // Optima Only field, not synced to CW\n\n opportunities Opportunity[]\n serviceBoards ServiceTicketBoard[]\n productDataRecords ProductData[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel InternalDepartment {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n createdById String?\n updatedById String?\n\n opportunities Opportunity[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel UnifiSite {\n id String @id @default(cuid())\n name String\n\n siteId String @unique\n\n companyId Int?\n company Company? @relation(fields: [companyId], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Company {\n id Int @unique\n uid String @id @default(uuid())\n name String\n\n phone String?\n website String?\n\n deleteFlag Boolean @default(false)\n dateDeleted DateTime?\n\n taxId String?\n taxExempt Boolean @default(false) // Optima Only field, not synced to CW\n\n enteredById String?\n deletedById String?\n\n contacts Contact[]\n companyAddresses CompanyAddress[]\n\n credentials Credential[]\n unifiSites UnifiSite[]\n opportunities Opportunity[]\n\n deletedBy User? @relation(\"DeletedBy\", fields: [deletedById], references: [cwIdentifier])\n enteredBy User? @relation(\"EnteredBy\", fields: [enteredById], references: [cwIdentifier])\n\n deletedAt DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n serviceTickets ServiceTicket[]\n billingServiceTickets ServiceTicket[] @relation(\"BillingCompany\")\n}\n\nmodel CompanyAddress {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String?\n\n addressLine1 String?\n addressLine2 String?\n city String?\n state USState?\n zipCode String?\n country Country?\n\n phone String?\n fax String?\n\n inactiveFlag Boolean @default(false)\n defaultFlag Boolean @default(false)\n defaultMailFlag Boolean @default(false)\n defaultBillFlag Boolean @default(false)\n defaultShipFlag Boolean @default(false)\n\n updatedById String?\n updatedBy User? @relation(fields: [updatedById], references: [cwIdentifier])\n\n companyId Int\n company Company @relation(fields: [companyId], references: [id])\n\n contacts Contact[]\n oppportunities Opportunity[]\n serviceTickets ServiceTicket[]\n billingTickets ServiceTicket[] @relation(\"BillingAddress\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Contact {\n id Int @unique\n uid String @id @default(uuid())\n\n active Boolean @default(true)\n default Boolean @default(false)\n\n firstName String\n lastName String\n nickname String?\n\n title String?\n gender GenderType?\n birthday DateTime?\n email String?\n\n phone String?\n phoneExtension String?\n phoneType PhoneType?\n\n companyAddressId Int?\n companyAddress CompanyAddress? @relation(fields: [companyAddressId], references: [id])\n\n memberId Int?\n companyId Int?\n\n company Company? @relation(fields: [companyId], references: [id])\n opportunities Opportunity[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n serviceTickets ServiceTicket[]\n}\n\nmodel CatalogItemType {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n inactiveFlag Boolean @default(false)\n defaultFlag Boolean @default(false)\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CatalogCategory {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n inactiveFlag Boolean @default(false)\n\n catalogSubcategories CatalogSubcategory[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CatalogSubcategory {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n categoryId Int\n category CatalogCategory @relation(fields: [categoryId], references: [id])\n\n items CatalogItem[]\n\n inactiveFlag Boolean @default(false)\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CatalogManufacturer {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n inactiveFlag Boolean @default(false)\n\n items CatalogItem[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel WarehouseBin {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n minQuantity Int\n maxQuantity Int\n\n inactiveFlag Boolean @default(false)\n defaultFlag Boolean @default(false)\n\n proeductInventories ProductInventory[]\n\n warehouse Warehouse? @relation(fields: [warehouseId], references: [id])\n warehouseId Int?\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ProductInventory {\n id Int @unique\n uid String @id @default(uuid())\n\n qtyOnHand Int @default(0)\n\n warehouseBinId Int\n warehouseBin WarehouseBin @relation(fields: [warehouseBinId], references: [id])\n\n itemId Int?\n item CatalogItem? @relation(fields: [itemId], references: [id])\n\n warehouse Warehouse? @relation(fields: [warehouseId], references: [id])\n warehouseId Int?\n\n updatedById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Warehouse {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n inactiveFlag Boolean @default(false)\n lockedFlag Boolean @default(false)\n\n minimumStock MinimumStockByWarehouse[]\n inventory ProductInventory[]\n bins WarehouseBin[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel MinimumStockByWarehouse {\n id Int @unique\n uid String @id @default(uuid())\n\n minQty Int @default(0)\n\n warehouseId Int\n warehouse Warehouse @relation(fields: [warehouseId], references: [id])\n\n itemId Int?\n item CatalogItem? @relation(fields: [itemId], references: [id])\n\n updatedById String?\n enteredById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CatalogItem {\n id Int @unique\n uid String @id @default(uuid())\n\n identifier String? @unique\n name String\n description String?\n\n customerDescription String?\n internalNotes String?\n\n linkedItems CatalogItem[] @relation(\"LinkedItems\")\n linkedTo CatalogItem[] @relation(\"LinkedItems\")\n\n subcategoryId Int\n subcategory CatalogSubcategory @relation(fields: [subcategoryId], references: [id])\n\n manufacturerId Int?\n manufacturer CatalogManufacturer? @relation(fields: [manufacturerId], references: [id])\n\n partNumber String?\n\n vendorName String?\n vendorSku String?\n vendorCwId Int?\n\n price Float\n cost Float\n\n inventory ProductInventory[]\n minimumStockByWarehouses MinimumStockByWarehouse[]\n\n productDataRecords ProductData[]\n\n inactive Boolean @default(false)\n salesTaxable Boolean @default(true)\n\n onHand Int @default(0)\n cwLastUpdated DateTime?\n\n // IV_Class_ID from CW: 'S' = Service/Labor, 'I' = Inventory, 'N' = Non-Inventory\n classId String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ProductData {\n id Int @unique\n uid String @id @default(uuid())\n\n qty Float @default(1)\n internalNote String?\n shortDescription String?\n description String?\n sequenceNumber Int? // This is the sequence number of the product on the ticket, which may be different from the sequence number of the product in the catalog, and is important for maintaining the order of products on the ticket as they were added.\n\n procurementNotes String?\n productNarrative String?\n\n unitPrice Float @default(0)\n unitCost Float @default(0)\n listPrice Float @default(0) // The original price of the product before any discounts or promotions are applied, which may be different from the unit price if there are any overrides or promotions applied at the ticket level.\n\n discount Float @default(0) // The discount amount applied to this product, which may be different from the discount on the catalog item if there are any overrides or promotions applied at the ticket level.\n\n recurringRevenue Float @default(0) // For subscription products, the recurring revenue amount, which may be different from the unit price if there are any discounts or promotions applied.\n recurringCost Float @default(0) // For subscription products, the recurring cost amount, which may be different from the unit cost if there are any discounts or promotions applied.\n\n qtyPicked Int @default(0) // How many of this product have been taken out of inventory\n qtyShipped Int @default(0) // How many of this product have been recieved by the customer\n\n cancelReason String? // If this product was canceled or removed from the ticket after being added, what was the reason for that?\n cancelQty Float? // If this product was canceled or removed from the ticket after being added, how many were canceled or removed?\n\n // ------ Flag Fields ------\n billableFlag Boolean @default(true)\n taxableFlag Boolean @default(true)\n invoiceFlag Boolean @default(true)\n recurringFlag Boolean @default(false) // Is this product a subscription or recurring revenue product?\n poApprovedFlag Boolean @default(false) // Was the purchase of this product approved as part of a purchase order process?\n\n calcPriceFlag Boolean @default(true) // Should the price of this product be calculated based on the catalog item price and any overrides, or should it use the unitPrice as is? \n calcCostFlag Boolean @default(true) // Should the cost of this product be calculated based on the catalog item cost and any overrides, or should it use the unitCost as is?\n\n cancelFlag Boolean @default(false) // Has this product been canceled or removed from the ticket after being added, but we want to keep the record of it for historical and reporting purposes?\n\n // ------ Relational Fields ------\n catalogItemId Int\n corporateLocationId Int\n\n serviceTicketId Int?\n opportunityId Int?\n\n serviceTicket ServiceTicket? @relation(fields: [serviceTicketId], references: [id])\n opportunity Opportunity? @relation(fields: [opportunityId], references: [id])\n\n catalogItem CatalogItem @relation(fields: [catalogItemId], references: [id])\n corporateLocation CorporateLocation @relation(fields: [corporateLocationId], references: [id])\n\n updatedById String?\n createdById String?\n closedById String?\n cancelById String\n\n closedAt DateTime?\n cancelledAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\n//\n// SERVICE TICKETS\n//\n\nmodel ServiceTicket {\n id Int @unique\n uid String @id @default(uuid())\n\n // ------ Core ticket fields ------\n\n summary String\n\n notes ServiceTicketNote[]\n\n addressLine1 String?\n addressLine2 String?\n city String?\n state USState?\n zipCode String?\n country Country?\n\n contactName String?\n phone String?\n phoneExtension String?\n phoneType PhoneType?\n email String?\n\n // ------ Billing and invoicing fields ------\n\n products ProductData[] // The products used on this ticket, which may be important for billing and invoicing, as well as reporting and analytics.\n\n poNumber String?\n billCompleteFlag Boolean @default(false) // Bill after ticket is closed, not allowing any billing while open.\n billUnapprovedFlag Boolean @default(false) // Can this ticket bill unapproved work or expenses?\n billingAmount Float @default(0.00)\n billingMethod BillingMethod @default(ACTUAL_RATES)\n\n timeBillableFlag Boolean @default(true) // Is the time spent on this ticket billable?\n expenseBillableFlag Boolean @default(true) // Are the expenses incurred on this ticket billable?\n productBillableFlag Boolean @default(true) // Are the products used on this ticket billable?\n\n timeInvoiceableFlag Boolean @default(true) // Should the billable time on this ticket be included on invoices?\n expenseInvoiceableFlag Boolean @default(true) // Should the billable expenses on this ticket be included on invoices?\n productInvoiceableFlag Boolean @default(true) // Should the billable products on this ticket be included on invoices?\n\n dateRequested DateTime? // The date the customer requested service, which may be different from the date the ticket was created in the system.\n\n billingType BillingType @default(STANDARD) // (CUSTOM FIELD) Standard billing or project billing, which may have different rules for how the ticket is billed.\n billingInstructions String? // (CUSTOM FIELD) Any special instructions for billing this ticket, which may be important for project billing or non-standard billing arrangements.\n\n // ------ Flag Fields ------\n\n rejectedFlag Boolean @default(false) // More used for denoting if a ticket was rejected by some automation.\n closedFlag Boolean @default(false)\n redFlag Boolean @default(false) // Carry over from CW, used for visibility and filtering.\n publishFlag Boolean @default(false) // Should this ticket be visible to the customer in the portal or any other means?\n\n // ------ Relational Fields ------\n\n ticketOwnerId String?\n serviceTicketBoardId Int?\n severityId Int\n impactId Int\n priorityId Int\n sourceId Int\n locationId Int\n parentId Int?\n\n companyId Int?\n contactId Int?\n companyAddressId Int?\n\n billingCompanyId Int?\n billingAddressId Int?\n\n severity ServiceTicketSeverity @relation(fields: [severityId], references: [id])\n impact ServiceTicketImpact @relation(fields: [impactId], references: [id])\n priority ServiceTicketPriority @relation(fields: [priorityId], references: [id])\n source ServiceTicketSource @relation(fields: [sourceId], references: [id])\n location ServiceTicketLocation @relation(fields: [locationId], references: [id])\n serviceTicketBoard ServiceTicketBoard? @relation(fields: [serviceTicketBoardId], references: [id])\n ticketOwner User? @relation(\"ServiceTicketOwner\", fields: [ticketOwnerId], references: [cwIdentifier])\n\n company Company? @relation(fields: [companyId], references: [id])\n contact Contact? @relation(fields: [contactId], references: [id])\n companyAddress CompanyAddress? @relation(fields: [companyAddressId], references: [id])\n\n billingCompany Company? @relation(\"BillingCompany\", fields: [billingCompanyId], references: [id])\n billingAddress CompanyAddress? @relation(\"BillingAddress\", fields: [billingAddressId], references: [id])\n\n // ------ Audit Fields ------\n\n createdById String?\n updatedById String?\n closedById String?\n\n closedBy User? @relation(\"ServiceTicketClosedBy\", fields: [closedById], references: [id])\n createdBy User? @relation(\"ServiceTicketCreatedBy\", fields: [createdById], references: [id])\n updatedBy User? @relation(\"ServiceTicketUpdatedBy\", fields: [updatedById], references: [id])\n\n rejectedAt DateTime?\n closedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ServiceTicketNote {\n id Int @unique\n uid String @id @default(uuid())\n\n notes String\n notesMd String\n\n authorId String\n author User @relation(\"ServiceTicketNoteAuthor\", fields: [authorId], references: [id])\n\n problemFlag Boolean @default(false) // Is this note describing the problem?\n resolutionFlag Boolean @default(false) // Is this note describing the resolution?\n internalAnalysisFlag Boolean @default(false) // Is this note describing the internal analysis of the issue, such as root cause analysis or technical details that may not be relevant to the customer?\n internalMemberFlag Boolean @default(false) // Is this note meant to be seen by internal team members only, not visible to the customer?\n createdByParentFlag Boolean @default(false) // Is this note created by the parent entity.\n mergedFlag Boolean @default(false)\n bundledFlag Boolean @default(false)\n\n serviceTicketId Int\n serviceTicket ServiceTicket @relation(fields: [serviceTicketId], references: [id])\n\n createdById String?\n updatedById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ServiceTicketType {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n inactiveFlag Boolean @default(false)\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ServiceTicketBoard {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n\n // Does this generate revenue for the company?\n timeBillableFlag Boolean @default(false)\n expenseBillableFlag Boolean @default(false)\n productBillableFlag Boolean @default(false)\n\n // Can the customer see this on their invoice?\n timeInvoiceableFlag Boolean @default(false)\n expenseInvoiceableFlag Boolean @default(false)\n productInvoiceableFlag Boolean @default(false)\n\n // These are auto assignment rule flags\n // If/When I implement a system for auto assignement, \n // these flags will determine which fields are considered \n // for auto assignment when a ticket is created without an assignee.\n\n // These are a carry over from CW.\n autoAssignNewFlag Boolean @default(false)\n autoAssignEmailCreatedFlag Boolean @default(false)\n autoAssignPortalCreatedFlag Boolean @default(false)\n\n projectFlag Boolean @default(false)\n lockDescriptionFlag Boolean @default(false) // Should we lock the description field on tickets in this board after creation?\n\n emailContactFlag Boolean @default(false) // Should we email the contact when a ticket is updated?\n emailResourceFlag Boolean @default(false) // Should we email the assigned resource(s) when a ticket is updated?\n\n resolutionSortOrder String @default(\"D\") @db.Char(1)\n internalAnalysisSortOrder String @default(\"D\") @db.Char(1)\n\n locationId Int\n location CorporateLocation @relation(fields: [locationId], references: [id])\n\n serviceTickets ServiceTicket[]\n\n createdById String?\n updatedById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ServiceTicketLocation {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n defaultFlag Boolean @default(false)\n\n serviceTickets ServiceTicket[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ServiceTicketSource {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n inactiveFlag Boolean @default(false)\n defaultFlag Boolean @default(false)\n\n serviceTickets ServiceTicket[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\n// How much does the issue affect the customer or their business operations\nmodel ServiceTicketImpact {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String?\n\n defaultFlag Boolean @default(false)\n\n serviceTickets ServiceTicket[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\n// How soon does the issue need to be addressed/resolved\nmodel ServiceTicketPriority {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n color String? // Hex color code for priority, e.g. \"#FF0000\" for red\n description String? // Optima Only field, not synced to CW\n\n defaultFlag Boolean @default(false)\n\n serviceTickets ServiceTicket[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\n// How bad is the Technical Issue\nmodel ServiceTicketSeverity {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String?\n\n defaultFlag Boolean @default(false)\n\n serviceTickets ServiceTicket[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@map(\"ServiceTicketServerity\")\n}\n\n// This data is populated asynchronously after a ticket is closed, \n// so we can keep the critical path of closing a ticket fast and not \n// dependent on any additional data processing. This will also allow \n// us to be able to store the data AS IT was at the time of closing, \n// without worrying about any additional updates that may come in after the fact.\nmodel ServiceTicketFinalData {\n id String @id @default(uuid())\n}\n\nmodel OpportunityStage {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n seqNbr Int?\n funnelColor String?\n\n updatedById String?\n\n opportunities Opportunity[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel OpportunityType {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n inactiveFlag Boolean @default(false)\n opportunities Opportunity[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel OpportunityStatus {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n\n inactiveFlag Boolean @default(false)\n defaultFlag Boolean @default(false)\n wonFlag Boolean @default(false)\n lostFlag Boolean @default(false)\n closeFlag Boolean @default(false)\n\n updatedById String?\n createdById String?\n\n opportunities Opportunity[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Opportunity {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n notes String?\n\n oppNarrative String? // A long form text field for the story of the opportunity, which may include details about the customer's needs, the proposed solution, and any other relevant information that doesn't fit into the structured fields.\n\n generatedQuotes GeneratedQuotes[]\n\n typeId Int\n type OpportunityType @relation(fields: [typeId], references: [id])\n\n stageId Int?\n stage OpportunityStage? @relation(fields: [stageId], references: [id])\n\n statusId Int?\n status OpportunityStatus? @relation(fields: [statusId], references: [id])\n\n taxCodeId Int?\n taxCode TaxCode? @relation(fields: [taxCodeId], references: [id])\n\n interest OpportunityInterest?\n probability Float @default(0)\n\n source String?\n\n // Sales rep references\n primarySalesRepId String?\n primarySalesRep User? @relation(\"PrimarySalesRep\", fields: [primarySalesRepId], references: [cwIdentifier])\n\n secondarySalesRepId String?\n secondarySalesRep User? @relation(\"SecondarySalesRep\", fields: [secondarySalesRepId], references: [cwIdentifier])\n\n // Company / contact / site\n companyId Int?\n company Company? @relation(fields: [companyId], references: [id])\n\n contactId Int?\n contact Contact? @relation(fields: [contactId], references: [id])\n\n siteId Int?\n site CompanyAddress? @relation(fields: [siteId], references: [id])\n\n customerPO String?\n\n // Location / department\n locationId Int?\n location CorporateLocation? @relation(fields: [locationId], references: [id])\n\n departmentId Int?\n department InternalDepartment? @relation(fields: [departmentId], references: [id])\n\n // Dates\n expectedCloseDate DateTime?\n pipelineChangeDate DateTime?\n dateBecameLead DateTime?\n closedDate DateTime?\n closedFlag Boolean @default(false)\n closedById String?\n\n // Local product sequence — array of CW forecast item IDs in display order.\n // When present, fetchProducts() uses this order instead of CW sequenceNumber.\n productSequence Int[] @default([])\n\n products ProductData[]\n\n updatedBy String\n eneteredBy String\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\n// ------ Schedule / Calendar --------\n\nmodel ScheduleStatus {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n color String?\n\n softFlag Boolean @default(false)\n defaultFlag Boolean @default(false)\n\n schedules Schedule[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ScheduleType {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String? // Optima Only field, not synced to CW\n displayColor String?\n\n tableReference String?\n moduleId String? @db.Char(2)\n scheduleTypeId String? @db.Char(1)\n\n systemFlag Boolean @default(false)\n displayFlag Boolean @default(false)\n\n schedules Schedule[]\n\n updatedById String?\n createdById String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel ScheduleSpan {\n id Int @id @default(autoincrement())\n scheduleSpanId String? @db.Char(1)\n spanDesc String? @db.Char(20)\n\n schedules Schedule[]\n}\n\nmodel Schedule {\n id Int @unique\n uid String @id @default(uuid())\n\n name String\n description String?\n\n memberId String?\n\n closedFlag Boolean @default(false)\n reminderFlag Boolean @default(false)\n allDayFlag Boolean @default(false)\n acknowledgementFlag Boolean @default(false)\n meetingFlag Boolean @default(false)\n recurringFlag Boolean @default(false)\n\n billableFlag Boolean @default(false)\n\n acknowledgedById String?\n acknowledgedAt DateTime?\n\n startDate DateTime?\n endDate DateTime?\n hoursScheduled Float?\n duration Int? // The number of days in between the start and end date.\n hoursPerDay Float?\n reminderMinutes Int? @default(15)\n\n statusId Int?\n status ScheduleStatus? @relation(fields: [statusId], references: [id])\n\n typeId Int?\n type ScheduleType? @relation(fields: [typeId], references: [id])\n\n scheduleSpanId Int?\n scheduleSpan ScheduleSpan? @relation(fields: [scheduleSpanId], references: [id])\n\n updatedById String?\n createdById String?\n closedById String?\n\n closedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CredentialType {\n id String @id @default(cuid())\n name String @unique\n\n permissionScope String\n icon String?\n fields Json\n\n credentials Credential[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel SecureValue {\n id String @id @default(cuid())\n name String\n\n content String // Encrypted content\n hash String // Hash of the original content for integrity verification and Search\n\n credentialId String\n credential Credential @relation(fields: [credentialId], references: [id], onDelete: Cascade)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Credential {\n id String @id @default(cuid())\n name String\n notes String?\n subCredentialOfId String?\n subCredentialOf Credential? @relation(\"SubCredentials\", fields: [subCredentialOfId], references: [id], onDelete: Cascade)\n subCredentials Credential[] @relation(\"SubCredentials\")\n\n typeId String\n type CredentialType @relation(fields: [typeId], references: [id], onDelete: Cascade)\n\n fields Json\n\n companyId String\n company Company @relation(fields: [companyId], references: [uid], onDelete: Cascade)\n\n securevalues SecureValue[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel GeneratedQuotes {\n id String @id @default(uuid())\n\n quoteRegenData Json @default(\"{}\") // Store any additional data needed for quote regeneration, such as product details, pricing, etc.\n quoteRegenParams Json @default(\"{}\") // Store parameters used for quote regeneration, such as template ID, formatting options, etc.\n quoteRegenHash String @unique @default(\"\")\n\n downloads Json @default(\"[]\") // Array of download records with timestamp and user info\n\n quoteFile Bytes\n quoteFileName String\n\n opportunityId String\n opportunity Opportunity @relation(fields: [opportunityId], references: [uid], onDelete: Cascade)\n\n createdById String?\n createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel TaxCode {\n id Int @unique\n uid String @id @default(uuid())\n\n opportunities Opportunity[]\n\n code String @unique\n codeCaption String\n description String?\n\n rate Float?\n\n defaultFlag Boolean @default(false)\n\n createdBy String\n updatedBy String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CwMember {\n id String @id @default(cuid())\n\n cwMemberId Int @unique\n identifier String @unique\n firstName String\n lastName String\n officeEmail String?\n inactiveFlag Boolean @default(false)\n\n apiKey String?\n\n user User?\n\n cwLastUpdated DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n", "runtimeDataModel": { "models": {}, "enums": {}, "types": {} + }, + "parameterizationSchema": { + "strings": [], + "graph": "" } } -config.runtimeDataModel = JSON.parse("{\"models\":{\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessionKey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expires\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"refreshTokenGenerated\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"refreshedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"invalidatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"}],\"dbName\":null},\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"roles\",\"kind\":\"object\",\"type\":\"Role\",\"relationName\":\"RoleToUser\"},{\"name\":\"permissions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"login\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"emailVerified\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"image\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cwIdentifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessions\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"generatedQuotes\",\"kind\":\"object\",\"type\":\"GeneratedQuotes\",\"relationName\":\"GeneratedQuotesToUser\"}],\"dbName\":null},\"Role\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"moniker\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"permissions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"users\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"RoleToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"UnifiSite\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"siteId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToUnifiSite\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Company\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cw_CompanyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"cw_Identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CompanyToCredential\"},{\"name\":\"unifiSites\",\"kind\":\"object\",\"type\":\"UnifiSite\",\"relationName\":\"CompanyToUnifiSite\"},{\"name\":\"opportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"CompanyToOpportunity\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CatalogItem\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cwCatalogId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"customerDescription\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"internalNotes\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"linkedItems\",\"kind\":\"object\",\"type\":\"CatalogItem\",\"relationName\":\"LinkedItems\"},{\"name\":\"linkedTo\",\"kind\":\"object\",\"type\":\"CatalogItem\",\"relationName\":\"LinkedItems\"},{\"name\":\"category\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"categoryCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"subcategory\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"subcategoryCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"manufacturer\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"manufactureCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"partNumber\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"vendorName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"vendorSku\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"vendorCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"price\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"cost\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"inactive\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"salesTaxable\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"onHand\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"cwLastUpdated\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Opportunity\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cwOpportunityId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"notes\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"generatedQuotes\",\"kind\":\"object\",\"type\":\"GeneratedQuotes\",\"relationName\":\"GeneratedQuotesToOpportunity\"},{\"name\":\"typeName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"typeCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"stageName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"stageCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"statusName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"statusCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"priorityName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"priorityCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"ratingName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"ratingCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"source\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"campaignName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"campaignCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"primarySalesRepName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"primarySalesRepIdentifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"primarySalesRepCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"secondarySalesRepName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"secondarySalesRepIdentifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"secondarySalesRepCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"companyCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"companyName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"contactCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"contactName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"siteCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"siteName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"customerPO\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"totalSalesTax\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"probability\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"locationName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"locationCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"departmentName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"departmentCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"expectedCloseDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"pipelineChangeDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"dateBecameLead\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"closedDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"closedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"closedByName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"closedByCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToOpportunity\"},{\"name\":\"productSequence\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"cwLastUpdated\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"cwDateEntered\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CredentialType\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"permissionScope\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"icon\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fields\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToCredentialType\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"SecureValue\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credentialId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credential\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToSecureValue\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Credential\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"notes\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"subCredentialOfId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"subCredentialOf\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"SubCredentials\"},{\"name\":\"subCredentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"SubCredentials\"},{\"name\":\"typeId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"object\",\"type\":\"CredentialType\",\"relationName\":\"CredentialToCredentialType\"},{\"name\":\"fields\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToCredential\"},{\"name\":\"securevalues\",\"kind\":\"object\",\"type\":\"SecureValue\",\"relationName\":\"CredentialToSecureValue\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"GeneratedQuotes\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"quoteRegenData\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"quoteRegenParams\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"quoteRegenHash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"downloads\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"quoteFile\",\"kind\":\"scalar\",\"type\":\"Bytes\"},{\"name\":\"quoteFileName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"opportunityId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"opportunity\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"GeneratedQuotesToOpportunity\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdBy\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"GeneratedQuotesToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CwMember\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cwMemberId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"officeEmail\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"apiKey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cwLastUpdated\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}") +config.runtimeDataModel = JSON.parse("{\"models\":{\"SyncJobRun\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"jobType\",\"kind\":\"enum\",\"type\":\"SyncJobType\"},{\"name\":\"status\",\"kind\":\"enum\",\"type\":\"SyncJobStatus\"},{\"name\":\"triggeredBy\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"startedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"completedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"errorSummary\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"steps\",\"kind\":\"object\",\"type\":\"SyncStepLog\",\"relationName\":\"SyncJobRunToSyncStepLog\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"SyncStepLog\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"syncJobRunId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"syncJobRun\",\"kind\":\"object\",\"type\":\"SyncJobRun\",\"relationName\":\"SyncJobRunToSyncStepLog\"},{\"name\":\"tableName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"syncMode\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"recordsProcessed\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"recordsInserted\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"recordsSkipped\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"recordsFailed\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"recordsDeleted\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"sampleErrors\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"durationMs\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessionKey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expires\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"refreshTokenGenerated\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"refreshedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"invalidatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"}],\"dbName\":null},\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"roles\",\"kind\":\"object\",\"type\":\"Role\",\"relationName\":\"RoleToUser\"},{\"name\":\"permissions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"login\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"image\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"active\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"hidden\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"cwIdentifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cwMemberId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"cwMember\",\"kind\":\"object\",\"type\":\"CwMember\",\"relationName\":\"CwMemberToUser\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessions\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"companiesDeleted\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"DeletedBy\"},{\"name\":\"companiesEntered\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"EnteredBy\"},{\"name\":\"opportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"PrimarySalesRep\"},{\"name\":\"opportunitiesSecondary\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"SecondarySalesRep\"},{\"name\":\"generatedQuotes\",\"kind\":\"object\",\"type\":\"GeneratedQuotes\",\"relationName\":\"GeneratedQuotesToUser\"},{\"name\":\"companyAddresses\",\"kind\":\"object\",\"type\":\"CompanyAddress\",\"relationName\":\"CompanyAddressToUser\"},{\"name\":\"updatedBy\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"serviceTicketsOwned\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketOwner\"},{\"name\":\"serviceTicketsClosed\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketClosedBy\"},{\"name\":\"serviceTicketsCreated\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketCreatedBy\"},{\"name\":\"serviceTicketsUpdated\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketUpdatedBy\"},{\"name\":\"serviceTicketNotes\",\"kind\":\"object\",\"type\":\"ServiceTicketNote\",\"relationName\":\"ServiceTicketNoteAuthor\"}],\"dbName\":null},\"Role\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"moniker\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"permissions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"users\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"RoleToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CorporateLocation\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"addressLine1\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"addressLine2\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"city\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"state\",\"kind\":\"enum\",\"type\":\"USState\"},{\"name\":\"zipCode\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"country\",\"kind\":\"enum\",\"type\":\"Country\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"opportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"CorporateLocationToOpportunity\"},{\"name\":\"serviceBoards\",\"kind\":\"object\",\"type\":\"ServiceTicketBoard\",\"relationName\":\"CorporateLocationToServiceTicketBoard\"},{\"name\":\"productDataRecords\",\"kind\":\"object\",\"type\":\"ProductData\",\"relationName\":\"CorporateLocationToProductData\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"InternalDepartment\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"opportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"InternalDepartmentToOpportunity\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"UnifiSite\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"siteId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToUnifiSite\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Company\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"website\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"deleteFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"dateDeleted\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"taxId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"taxExempt\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"enteredById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"deletedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"contacts\",\"kind\":\"object\",\"type\":\"Contact\",\"relationName\":\"CompanyToContact\"},{\"name\":\"companyAddresses\",\"kind\":\"object\",\"type\":\"CompanyAddress\",\"relationName\":\"CompanyToCompanyAddress\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CompanyToCredential\"},{\"name\":\"unifiSites\",\"kind\":\"object\",\"type\":\"UnifiSite\",\"relationName\":\"CompanyToUnifiSite\"},{\"name\":\"opportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"CompanyToOpportunity\"},{\"name\":\"deletedBy\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"DeletedBy\"},{\"name\":\"enteredBy\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"EnteredBy\"},{\"name\":\"deletedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"serviceTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"CompanyToServiceTicket\"},{\"name\":\"billingServiceTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"BillingCompany\"}],\"dbName\":null},\"CompanyAddress\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"addressLine1\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"addressLine2\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"city\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"state\",\"kind\":\"enum\",\"type\":\"USState\"},{\"name\":\"zipCode\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"country\",\"kind\":\"enum\",\"type\":\"Country\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fax\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"defaultMailFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"defaultBillFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"defaultShipFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"updatedBy\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"CompanyAddressToUser\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToCompanyAddress\"},{\"name\":\"contacts\",\"kind\":\"object\",\"type\":\"Contact\",\"relationName\":\"CompanyAddressToContact\"},{\"name\":\"oppportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"CompanyAddressToOpportunity\"},{\"name\":\"serviceTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"CompanyAddressToServiceTicket\"},{\"name\":\"billingTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"BillingAddress\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Contact\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"active\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"default\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"nickname\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"gender\",\"kind\":\"enum\",\"type\":\"GenderType\"},{\"name\":\"birthday\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"phoneExtension\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"phoneType\",\"kind\":\"enum\",\"type\":\"PhoneType\"},{\"name\":\"companyAddressId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"companyAddress\",\"kind\":\"object\",\"type\":\"CompanyAddress\",\"relationName\":\"CompanyAddressToContact\"},{\"name\":\"memberId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToContact\"},{\"name\":\"opportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"ContactToOpportunity\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"serviceTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ContactToServiceTicket\"}],\"dbName\":null},\"CatalogItemType\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CatalogCategory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"catalogSubcategories\",\"kind\":\"object\",\"type\":\"CatalogSubcategory\",\"relationName\":\"CatalogCategoryToCatalogSubcategory\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CatalogSubcategory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"category\",\"kind\":\"object\",\"type\":\"CatalogCategory\",\"relationName\":\"CatalogCategoryToCatalogSubcategory\"},{\"name\":\"items\",\"kind\":\"object\",\"type\":\"CatalogItem\",\"relationName\":\"CatalogItemToCatalogSubcategory\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CatalogManufacturer\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"items\",\"kind\":\"object\",\"type\":\"CatalogItem\",\"relationName\":\"CatalogItemToCatalogManufacturer\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"WarehouseBin\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"minQuantity\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"maxQuantity\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"proeductInventories\",\"kind\":\"object\",\"type\":\"ProductInventory\",\"relationName\":\"ProductInventoryToWarehouseBin\"},{\"name\":\"warehouse\",\"kind\":\"object\",\"type\":\"Warehouse\",\"relationName\":\"WarehouseToWarehouseBin\"},{\"name\":\"warehouseId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ProductInventory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"qtyOnHand\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"warehouseBinId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"warehouseBin\",\"kind\":\"object\",\"type\":\"WarehouseBin\",\"relationName\":\"ProductInventoryToWarehouseBin\"},{\"name\":\"itemId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"item\",\"kind\":\"object\",\"type\":\"CatalogItem\",\"relationName\":\"CatalogItemToProductInventory\"},{\"name\":\"warehouse\",\"kind\":\"object\",\"type\":\"Warehouse\",\"relationName\":\"ProductInventoryToWarehouse\"},{\"name\":\"warehouseId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Warehouse\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"lockedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"minimumStock\",\"kind\":\"object\",\"type\":\"MinimumStockByWarehouse\",\"relationName\":\"MinimumStockByWarehouseToWarehouse\"},{\"name\":\"inventory\",\"kind\":\"object\",\"type\":\"ProductInventory\",\"relationName\":\"ProductInventoryToWarehouse\"},{\"name\":\"bins\",\"kind\":\"object\",\"type\":\"WarehouseBin\",\"relationName\":\"WarehouseToWarehouseBin\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"MinimumStockByWarehouse\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"minQty\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"warehouseId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"warehouse\",\"kind\":\"object\",\"type\":\"Warehouse\",\"relationName\":\"MinimumStockByWarehouseToWarehouse\"},{\"name\":\"itemId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"item\",\"kind\":\"object\",\"type\":\"CatalogItem\",\"relationName\":\"CatalogItemToMinimumStockByWarehouse\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"enteredById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CatalogItem\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"customerDescription\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"internalNotes\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"linkedItems\",\"kind\":\"object\",\"type\":\"CatalogItem\",\"relationName\":\"LinkedItems\"},{\"name\":\"linkedTo\",\"kind\":\"object\",\"type\":\"CatalogItem\",\"relationName\":\"LinkedItems\"},{\"name\":\"subcategoryId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"subcategory\",\"kind\":\"object\",\"type\":\"CatalogSubcategory\",\"relationName\":\"CatalogItemToCatalogSubcategory\"},{\"name\":\"manufacturerId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"manufacturer\",\"kind\":\"object\",\"type\":\"CatalogManufacturer\",\"relationName\":\"CatalogItemToCatalogManufacturer\"},{\"name\":\"partNumber\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"vendorName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"vendorSku\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"vendorCwId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"price\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"cost\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"inventory\",\"kind\":\"object\",\"type\":\"ProductInventory\",\"relationName\":\"CatalogItemToProductInventory\"},{\"name\":\"minimumStockByWarehouses\",\"kind\":\"object\",\"type\":\"MinimumStockByWarehouse\",\"relationName\":\"CatalogItemToMinimumStockByWarehouse\"},{\"name\":\"productDataRecords\",\"kind\":\"object\",\"type\":\"ProductData\",\"relationName\":\"CatalogItemToProductData\"},{\"name\":\"inactive\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"salesTaxable\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"onHand\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"cwLastUpdated\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"classId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ProductData\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"qty\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"internalNote\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"shortDescription\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sequenceNumber\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"procurementNotes\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"productNarrative\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"unitPrice\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"unitCost\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"listPrice\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"discount\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"recurringRevenue\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"recurringCost\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"qtyPicked\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"qtyShipped\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"cancelReason\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cancelQty\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"billableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"taxableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"invoiceFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"recurringFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"poApprovedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"calcPriceFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"calcCostFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"cancelFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"catalogItemId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"corporateLocationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"serviceTicketId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"opportunityId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"serviceTicket\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ProductDataToServiceTicket\"},{\"name\":\"opportunity\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"OpportunityToProductData\"},{\"name\":\"catalogItem\",\"kind\":\"object\",\"type\":\"CatalogItem\",\"relationName\":\"CatalogItemToProductData\"},{\"name\":\"corporateLocation\",\"kind\":\"object\",\"type\":\"CorporateLocation\",\"relationName\":\"CorporateLocationToProductData\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"closedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cancelById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"closedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"cancelledAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceTicket\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"summary\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"notes\",\"kind\":\"object\",\"type\":\"ServiceTicketNote\",\"relationName\":\"ServiceTicketToServiceTicketNote\"},{\"name\":\"addressLine1\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"addressLine2\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"city\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"state\",\"kind\":\"enum\",\"type\":\"USState\"},{\"name\":\"zipCode\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"country\",\"kind\":\"enum\",\"type\":\"Country\"},{\"name\":\"contactName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"phoneExtension\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"phoneType\",\"kind\":\"enum\",\"type\":\"PhoneType\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"products\",\"kind\":\"object\",\"type\":\"ProductData\",\"relationName\":\"ProductDataToServiceTicket\"},{\"name\":\"poNumber\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"billCompleteFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"billUnapprovedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"billingAmount\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"billingMethod\",\"kind\":\"enum\",\"type\":\"BillingMethod\"},{\"name\":\"timeBillableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"expenseBillableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"productBillableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"timeInvoiceableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"expenseInvoiceableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"productInvoiceableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"dateRequested\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"billingType\",\"kind\":\"enum\",\"type\":\"BillingType\"},{\"name\":\"billingInstructions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"rejectedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"closedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"redFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"publishFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"ticketOwnerId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"serviceTicketBoardId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"severityId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"impactId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"priorityId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"sourceId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"locationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"parentId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"contactId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"companyAddressId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"billingCompanyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"billingAddressId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"severity\",\"kind\":\"object\",\"type\":\"ServiceTicketSeverity\",\"relationName\":\"ServiceTicketToServiceTicketSeverity\"},{\"name\":\"impact\",\"kind\":\"object\",\"type\":\"ServiceTicketImpact\",\"relationName\":\"ServiceTicketToServiceTicketImpact\"},{\"name\":\"priority\",\"kind\":\"object\",\"type\":\"ServiceTicketPriority\",\"relationName\":\"ServiceTicketToServiceTicketPriority\"},{\"name\":\"source\",\"kind\":\"object\",\"type\":\"ServiceTicketSource\",\"relationName\":\"ServiceTicketToServiceTicketSource\"},{\"name\":\"location\",\"kind\":\"object\",\"type\":\"ServiceTicketLocation\",\"relationName\":\"ServiceTicketToServiceTicketLocation\"},{\"name\":\"serviceTicketBoard\",\"kind\":\"object\",\"type\":\"ServiceTicketBoard\",\"relationName\":\"ServiceTicketToServiceTicketBoard\"},{\"name\":\"ticketOwner\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ServiceTicketOwner\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToServiceTicket\"},{\"name\":\"contact\",\"kind\":\"object\",\"type\":\"Contact\",\"relationName\":\"ContactToServiceTicket\"},{\"name\":\"companyAddress\",\"kind\":\"object\",\"type\":\"CompanyAddress\",\"relationName\":\"CompanyAddressToServiceTicket\"},{\"name\":\"billingCompany\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"BillingCompany\"},{\"name\":\"billingAddress\",\"kind\":\"object\",\"type\":\"CompanyAddress\",\"relationName\":\"BillingAddress\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"closedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"closedBy\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ServiceTicketClosedBy\"},{\"name\":\"createdBy\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ServiceTicketCreatedBy\"},{\"name\":\"updatedBy\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ServiceTicketUpdatedBy\"},{\"name\":\"rejectedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"closedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceTicketNote\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"notes\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"notesMd\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"authorId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"author\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ServiceTicketNoteAuthor\"},{\"name\":\"problemFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"resolutionFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"internalAnalysisFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"internalMemberFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"createdByParentFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"mergedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"bundledFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"serviceTicketId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"serviceTicket\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketToServiceTicketNote\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceTicketType\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceTicketBoard\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"timeBillableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"expenseBillableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"productBillableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"timeInvoiceableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"expenseInvoiceableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"productInvoiceableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"autoAssignNewFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"autoAssignEmailCreatedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"autoAssignPortalCreatedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"projectFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"lockDescriptionFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"emailContactFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"emailResourceFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"resolutionSortOrder\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"internalAnalysisSortOrder\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"locationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"location\",\"kind\":\"object\",\"type\":\"CorporateLocation\",\"relationName\":\"CorporateLocationToServiceTicketBoard\"},{\"name\":\"serviceTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketToServiceTicketBoard\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceTicketLocation\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"serviceTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketToServiceTicketLocation\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceTicketSource\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"serviceTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketToServiceTicketSource\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceTicketImpact\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"serviceTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketToServiceTicketImpact\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceTicketPriority\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"color\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"serviceTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketToServiceTicketPriority\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ServiceTicketSeverity\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"serviceTickets\",\"kind\":\"object\",\"type\":\"ServiceTicket\",\"relationName\":\"ServiceTicketToServiceTicketSeverity\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"ServiceTicketServerity\"},\"ServiceTicketFinalData\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null},\"OpportunityStage\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"seqNbr\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"funnelColor\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"opportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"OpportunityToOpportunityStage\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"OpportunityType\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"opportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"OpportunityToOpportunityType\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"OpportunityStatus\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"wonFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"lostFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"closeFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"opportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"OpportunityToOpportunityStatus\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Opportunity\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"notes\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"oppNarrative\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"generatedQuotes\",\"kind\":\"object\",\"type\":\"GeneratedQuotes\",\"relationName\":\"GeneratedQuotesToOpportunity\"},{\"name\":\"typeId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"type\",\"kind\":\"object\",\"type\":\"OpportunityType\",\"relationName\":\"OpportunityToOpportunityType\"},{\"name\":\"stageId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"stage\",\"kind\":\"object\",\"type\":\"OpportunityStage\",\"relationName\":\"OpportunityToOpportunityStage\"},{\"name\":\"statusId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"status\",\"kind\":\"object\",\"type\":\"OpportunityStatus\",\"relationName\":\"OpportunityToOpportunityStatus\"},{\"name\":\"taxCodeId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"taxCode\",\"kind\":\"object\",\"type\":\"TaxCode\",\"relationName\":\"OpportunityToTaxCode\"},{\"name\":\"interest\",\"kind\":\"enum\",\"type\":\"OpportunityInterest\"},{\"name\":\"probability\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"source\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"primarySalesRepId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"primarySalesRep\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"PrimarySalesRep\"},{\"name\":\"secondarySalesRepId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"secondarySalesRep\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SecondarySalesRep\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToOpportunity\"},{\"name\":\"contactId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"contact\",\"kind\":\"object\",\"type\":\"Contact\",\"relationName\":\"ContactToOpportunity\"},{\"name\":\"siteId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"site\",\"kind\":\"object\",\"type\":\"CompanyAddress\",\"relationName\":\"CompanyAddressToOpportunity\"},{\"name\":\"customerPO\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"locationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"location\",\"kind\":\"object\",\"type\":\"CorporateLocation\",\"relationName\":\"CorporateLocationToOpportunity\"},{\"name\":\"departmentId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"department\",\"kind\":\"object\",\"type\":\"InternalDepartment\",\"relationName\":\"InternalDepartmentToOpportunity\"},{\"name\":\"expectedCloseDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"pipelineChangeDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"dateBecameLead\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"closedDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"closedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"closedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"productSequence\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"products\",\"kind\":\"object\",\"type\":\"ProductData\",\"relationName\":\"OpportunityToProductData\"},{\"name\":\"updatedBy\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"eneteredBy\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ScheduleStatus\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"color\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"softFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"schedules\",\"kind\":\"object\",\"type\":\"Schedule\",\"relationName\":\"ScheduleToScheduleStatus\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ScheduleType\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"displayColor\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tableReference\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"moduleId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"scheduleTypeId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"systemFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"displayFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"schedules\",\"kind\":\"object\",\"type\":\"Schedule\",\"relationName\":\"ScheduleToScheduleType\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ScheduleSpan\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"scheduleSpanId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"spanDesc\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"schedules\",\"kind\":\"object\",\"type\":\"Schedule\",\"relationName\":\"ScheduleToScheduleSpan\"}],\"dbName\":null},\"Schedule\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"memberId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"closedFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"reminderFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"allDayFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"acknowledgementFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"meetingFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"recurringFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"billableFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"acknowledgedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"acknowledgedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"startDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"endDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"hoursScheduled\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"duration\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"hoursPerDay\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"reminderMinutes\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"statusId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"status\",\"kind\":\"object\",\"type\":\"ScheduleStatus\",\"relationName\":\"ScheduleToScheduleStatus\"},{\"name\":\"typeId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"type\",\"kind\":\"object\",\"type\":\"ScheduleType\",\"relationName\":\"ScheduleToScheduleType\"},{\"name\":\"scheduleSpanId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"scheduleSpan\",\"kind\":\"object\",\"type\":\"ScheduleSpan\",\"relationName\":\"ScheduleToScheduleSpan\"},{\"name\":\"updatedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"closedById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"closedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CredentialType\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"permissionScope\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"icon\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fields\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToCredentialType\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"SecureValue\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credentialId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credential\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToSecureValue\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Credential\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"notes\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"subCredentialOfId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"subCredentialOf\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"SubCredentials\"},{\"name\":\"subCredentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"SubCredentials\"},{\"name\":\"typeId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"object\",\"type\":\"CredentialType\",\"relationName\":\"CredentialToCredentialType\"},{\"name\":\"fields\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToCredential\"},{\"name\":\"securevalues\",\"kind\":\"object\",\"type\":\"SecureValue\",\"relationName\":\"CredentialToSecureValue\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"GeneratedQuotes\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"quoteRegenData\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"quoteRegenParams\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"quoteRegenHash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"downloads\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"quoteFile\",\"kind\":\"scalar\",\"type\":\"Bytes\"},{\"name\":\"quoteFileName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"opportunityId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"opportunity\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"GeneratedQuotesToOpportunity\"},{\"name\":\"createdById\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdBy\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"GeneratedQuotesToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"TaxCode\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"opportunities\",\"kind\":\"object\",\"type\":\"Opportunity\",\"relationName\":\"OpportunityToTaxCode\"},{\"name\":\"code\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"codeCaption\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"rate\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"defaultFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"createdBy\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"updatedBy\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CwMember\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cwMemberId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"officeEmail\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"inactiveFlag\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"apiKey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"CwMemberToUser\"},{\"name\":\"cwLastUpdated\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}") +config.parameterizationSchema = { + strings: JSON.parse("[\"where\",\"orderBy\",\"cursor\",\"syncJobRun\",\"steps\",\"_count\",\"SyncJobRun.findUnique\",\"SyncJobRun.findUniqueOrThrow\",\"SyncJobRun.findFirst\",\"SyncJobRun.findFirstOrThrow\",\"SyncJobRun.findMany\",\"data\",\"SyncJobRun.createOne\",\"SyncJobRun.createMany\",\"SyncJobRun.createManyAndReturn\",\"SyncJobRun.updateOne\",\"SyncJobRun.updateMany\",\"SyncJobRun.updateManyAndReturn\",\"create\",\"update\",\"SyncJobRun.upsertOne\",\"SyncJobRun.deleteOne\",\"SyncJobRun.deleteMany\",\"having\",\"_min\",\"_max\",\"SyncJobRun.groupBy\",\"SyncJobRun.aggregate\",\"SyncStepLog.findUnique\",\"SyncStepLog.findUniqueOrThrow\",\"SyncStepLog.findFirst\",\"SyncStepLog.findFirstOrThrow\",\"SyncStepLog.findMany\",\"SyncStepLog.createOne\",\"SyncStepLog.createMany\",\"SyncStepLog.createManyAndReturn\",\"SyncStepLog.updateOne\",\"SyncStepLog.updateMany\",\"SyncStepLog.updateManyAndReturn\",\"SyncStepLog.upsertOne\",\"SyncStepLog.deleteOne\",\"SyncStepLog.deleteMany\",\"_avg\",\"_sum\",\"SyncStepLog.groupBy\",\"SyncStepLog.aggregate\",\"users\",\"roles\",\"user\",\"cwMember\",\"sessions\",\"updatedBy\",\"company\",\"contacts\",\"opportunity\",\"createdBy\",\"generatedQuotes\",\"opportunities\",\"type\",\"stage\",\"status\",\"taxCode\",\"primarySalesRep\",\"secondarySalesRep\",\"contact\",\"site\",\"location\",\"author\",\"serviceTicket\",\"notes\",\"linkedItems\",\"linkedTo\",\"catalogSubcategories\",\"category\",\"items\",\"subcategory\",\"manufacturer\",\"proeductInventories\",\"warehouse\",\"item\",\"minimumStock\",\"inventory\",\"bins\",\"warehouseBin\",\"minimumStockByWarehouses\",\"productDataRecords\",\"catalogItem\",\"corporateLocation\",\"products\",\"serviceTickets\",\"severity\",\"impact\",\"priority\",\"source\",\"serviceTicketBoard\",\"ticketOwner\",\"companyAddress\",\"billingCompany\",\"billingAddress\",\"closedBy\",\"serviceBoards\",\"department\",\"oppportunities\",\"billingTickets\",\"companyAddresses\",\"subCredentialOf\",\"subCredentials\",\"credentials\",\"credential\",\"securevalues\",\"unifiSites\",\"deletedBy\",\"enteredBy\",\"billingServiceTickets\",\"companiesDeleted\",\"companiesEntered\",\"opportunitiesSecondary\",\"serviceTicketsOwned\",\"serviceTicketsClosed\",\"serviceTicketsCreated\",\"serviceTicketsUpdated\",\"serviceTicketNotes\",\"Session.findUnique\",\"Session.findUniqueOrThrow\",\"Session.findFirst\",\"Session.findFirstOrThrow\",\"Session.findMany\",\"Session.createOne\",\"Session.createMany\",\"Session.createManyAndReturn\",\"Session.updateOne\",\"Session.updateMany\",\"Session.updateManyAndReturn\",\"Session.upsertOne\",\"Session.deleteOne\",\"Session.deleteMany\",\"Session.groupBy\",\"Session.aggregate\",\"User.findUnique\",\"User.findUniqueOrThrow\",\"User.findFirst\",\"User.findFirstOrThrow\",\"User.findMany\",\"User.createOne\",\"User.createMany\",\"User.createManyAndReturn\",\"User.updateOne\",\"User.updateMany\",\"User.updateManyAndReturn\",\"User.upsertOne\",\"User.deleteOne\",\"User.deleteMany\",\"User.groupBy\",\"User.aggregate\",\"Role.findUnique\",\"Role.findUniqueOrThrow\",\"Role.findFirst\",\"Role.findFirstOrThrow\",\"Role.findMany\",\"Role.createOne\",\"Role.createMany\",\"Role.createManyAndReturn\",\"Role.updateOne\",\"Role.updateMany\",\"Role.updateManyAndReturn\",\"Role.upsertOne\",\"Role.deleteOne\",\"Role.deleteMany\",\"Role.groupBy\",\"Role.aggregate\",\"CorporateLocation.findUnique\",\"CorporateLocation.findUniqueOrThrow\",\"CorporateLocation.findFirst\",\"CorporateLocation.findFirstOrThrow\",\"CorporateLocation.findMany\",\"CorporateLocation.createOne\",\"CorporateLocation.createMany\",\"CorporateLocation.createManyAndReturn\",\"CorporateLocation.updateOne\",\"CorporateLocation.updateMany\",\"CorporateLocation.updateManyAndReturn\",\"CorporateLocation.upsertOne\",\"CorporateLocation.deleteOne\",\"CorporateLocation.deleteMany\",\"CorporateLocation.groupBy\",\"CorporateLocation.aggregate\",\"InternalDepartment.findUnique\",\"InternalDepartment.findUniqueOrThrow\",\"InternalDepartment.findFirst\",\"InternalDepartment.findFirstOrThrow\",\"InternalDepartment.findMany\",\"InternalDepartment.createOne\",\"InternalDepartment.createMany\",\"InternalDepartment.createManyAndReturn\",\"InternalDepartment.updateOne\",\"InternalDepartment.updateMany\",\"InternalDepartment.updateManyAndReturn\",\"InternalDepartment.upsertOne\",\"InternalDepartment.deleteOne\",\"InternalDepartment.deleteMany\",\"InternalDepartment.groupBy\",\"InternalDepartment.aggregate\",\"UnifiSite.findUnique\",\"UnifiSite.findUniqueOrThrow\",\"UnifiSite.findFirst\",\"UnifiSite.findFirstOrThrow\",\"UnifiSite.findMany\",\"UnifiSite.createOne\",\"UnifiSite.createMany\",\"UnifiSite.createManyAndReturn\",\"UnifiSite.updateOne\",\"UnifiSite.updateMany\",\"UnifiSite.updateManyAndReturn\",\"UnifiSite.upsertOne\",\"UnifiSite.deleteOne\",\"UnifiSite.deleteMany\",\"UnifiSite.groupBy\",\"UnifiSite.aggregate\",\"Company.findUnique\",\"Company.findUniqueOrThrow\",\"Company.findFirst\",\"Company.findFirstOrThrow\",\"Company.findMany\",\"Company.createOne\",\"Company.createMany\",\"Company.createManyAndReturn\",\"Company.updateOne\",\"Company.updateMany\",\"Company.updateManyAndReturn\",\"Company.upsertOne\",\"Company.deleteOne\",\"Company.deleteMany\",\"Company.groupBy\",\"Company.aggregate\",\"CompanyAddress.findUnique\",\"CompanyAddress.findUniqueOrThrow\",\"CompanyAddress.findFirst\",\"CompanyAddress.findFirstOrThrow\",\"CompanyAddress.findMany\",\"CompanyAddress.createOne\",\"CompanyAddress.createMany\",\"CompanyAddress.createManyAndReturn\",\"CompanyAddress.updateOne\",\"CompanyAddress.updateMany\",\"CompanyAddress.updateManyAndReturn\",\"CompanyAddress.upsertOne\",\"CompanyAddress.deleteOne\",\"CompanyAddress.deleteMany\",\"CompanyAddress.groupBy\",\"CompanyAddress.aggregate\",\"Contact.findUnique\",\"Contact.findUniqueOrThrow\",\"Contact.findFirst\",\"Contact.findFirstOrThrow\",\"Contact.findMany\",\"Contact.createOne\",\"Contact.createMany\",\"Contact.createManyAndReturn\",\"Contact.updateOne\",\"Contact.updateMany\",\"Contact.updateManyAndReturn\",\"Contact.upsertOne\",\"Contact.deleteOne\",\"Contact.deleteMany\",\"Contact.groupBy\",\"Contact.aggregate\",\"CatalogItemType.findUnique\",\"CatalogItemType.findUniqueOrThrow\",\"CatalogItemType.findFirst\",\"CatalogItemType.findFirstOrThrow\",\"CatalogItemType.findMany\",\"CatalogItemType.createOne\",\"CatalogItemType.createMany\",\"CatalogItemType.createManyAndReturn\",\"CatalogItemType.updateOne\",\"CatalogItemType.updateMany\",\"CatalogItemType.updateManyAndReturn\",\"CatalogItemType.upsertOne\",\"CatalogItemType.deleteOne\",\"CatalogItemType.deleteMany\",\"CatalogItemType.groupBy\",\"CatalogItemType.aggregate\",\"CatalogCategory.findUnique\",\"CatalogCategory.findUniqueOrThrow\",\"CatalogCategory.findFirst\",\"CatalogCategory.findFirstOrThrow\",\"CatalogCategory.findMany\",\"CatalogCategory.createOne\",\"CatalogCategory.createMany\",\"CatalogCategory.createManyAndReturn\",\"CatalogCategory.updateOne\",\"CatalogCategory.updateMany\",\"CatalogCategory.updateManyAndReturn\",\"CatalogCategory.upsertOne\",\"CatalogCategory.deleteOne\",\"CatalogCategory.deleteMany\",\"CatalogCategory.groupBy\",\"CatalogCategory.aggregate\",\"CatalogSubcategory.findUnique\",\"CatalogSubcategory.findUniqueOrThrow\",\"CatalogSubcategory.findFirst\",\"CatalogSubcategory.findFirstOrThrow\",\"CatalogSubcategory.findMany\",\"CatalogSubcategory.createOne\",\"CatalogSubcategory.createMany\",\"CatalogSubcategory.createManyAndReturn\",\"CatalogSubcategory.updateOne\",\"CatalogSubcategory.updateMany\",\"CatalogSubcategory.updateManyAndReturn\",\"CatalogSubcategory.upsertOne\",\"CatalogSubcategory.deleteOne\",\"CatalogSubcategory.deleteMany\",\"CatalogSubcategory.groupBy\",\"CatalogSubcategory.aggregate\",\"CatalogManufacturer.findUnique\",\"CatalogManufacturer.findUniqueOrThrow\",\"CatalogManufacturer.findFirst\",\"CatalogManufacturer.findFirstOrThrow\",\"CatalogManufacturer.findMany\",\"CatalogManufacturer.createOne\",\"CatalogManufacturer.createMany\",\"CatalogManufacturer.createManyAndReturn\",\"CatalogManufacturer.updateOne\",\"CatalogManufacturer.updateMany\",\"CatalogManufacturer.updateManyAndReturn\",\"CatalogManufacturer.upsertOne\",\"CatalogManufacturer.deleteOne\",\"CatalogManufacturer.deleteMany\",\"CatalogManufacturer.groupBy\",\"CatalogManufacturer.aggregate\",\"WarehouseBin.findUnique\",\"WarehouseBin.findUniqueOrThrow\",\"WarehouseBin.findFirst\",\"WarehouseBin.findFirstOrThrow\",\"WarehouseBin.findMany\",\"WarehouseBin.createOne\",\"WarehouseBin.createMany\",\"WarehouseBin.createManyAndReturn\",\"WarehouseBin.updateOne\",\"WarehouseBin.updateMany\",\"WarehouseBin.updateManyAndReturn\",\"WarehouseBin.upsertOne\",\"WarehouseBin.deleteOne\",\"WarehouseBin.deleteMany\",\"WarehouseBin.groupBy\",\"WarehouseBin.aggregate\",\"ProductInventory.findUnique\",\"ProductInventory.findUniqueOrThrow\",\"ProductInventory.findFirst\",\"ProductInventory.findFirstOrThrow\",\"ProductInventory.findMany\",\"ProductInventory.createOne\",\"ProductInventory.createMany\",\"ProductInventory.createManyAndReturn\",\"ProductInventory.updateOne\",\"ProductInventory.updateMany\",\"ProductInventory.updateManyAndReturn\",\"ProductInventory.upsertOne\",\"ProductInventory.deleteOne\",\"ProductInventory.deleteMany\",\"ProductInventory.groupBy\",\"ProductInventory.aggregate\",\"Warehouse.findUnique\",\"Warehouse.findUniqueOrThrow\",\"Warehouse.findFirst\",\"Warehouse.findFirstOrThrow\",\"Warehouse.findMany\",\"Warehouse.createOne\",\"Warehouse.createMany\",\"Warehouse.createManyAndReturn\",\"Warehouse.updateOne\",\"Warehouse.updateMany\",\"Warehouse.updateManyAndReturn\",\"Warehouse.upsertOne\",\"Warehouse.deleteOne\",\"Warehouse.deleteMany\",\"Warehouse.groupBy\",\"Warehouse.aggregate\",\"MinimumStockByWarehouse.findUnique\",\"MinimumStockByWarehouse.findUniqueOrThrow\",\"MinimumStockByWarehouse.findFirst\",\"MinimumStockByWarehouse.findFirstOrThrow\",\"MinimumStockByWarehouse.findMany\",\"MinimumStockByWarehouse.createOne\",\"MinimumStockByWarehouse.createMany\",\"MinimumStockByWarehouse.createManyAndReturn\",\"MinimumStockByWarehouse.updateOne\",\"MinimumStockByWarehouse.updateMany\",\"MinimumStockByWarehouse.updateManyAndReturn\",\"MinimumStockByWarehouse.upsertOne\",\"MinimumStockByWarehouse.deleteOne\",\"MinimumStockByWarehouse.deleteMany\",\"MinimumStockByWarehouse.groupBy\",\"MinimumStockByWarehouse.aggregate\",\"CatalogItem.findUnique\",\"CatalogItem.findUniqueOrThrow\",\"CatalogItem.findFirst\",\"CatalogItem.findFirstOrThrow\",\"CatalogItem.findMany\",\"CatalogItem.createOne\",\"CatalogItem.createMany\",\"CatalogItem.createManyAndReturn\",\"CatalogItem.updateOne\",\"CatalogItem.updateMany\",\"CatalogItem.updateManyAndReturn\",\"CatalogItem.upsertOne\",\"CatalogItem.deleteOne\",\"CatalogItem.deleteMany\",\"CatalogItem.groupBy\",\"CatalogItem.aggregate\",\"ProductData.findUnique\",\"ProductData.findUniqueOrThrow\",\"ProductData.findFirst\",\"ProductData.findFirstOrThrow\",\"ProductData.findMany\",\"ProductData.createOne\",\"ProductData.createMany\",\"ProductData.createManyAndReturn\",\"ProductData.updateOne\",\"ProductData.updateMany\",\"ProductData.updateManyAndReturn\",\"ProductData.upsertOne\",\"ProductData.deleteOne\",\"ProductData.deleteMany\",\"ProductData.groupBy\",\"ProductData.aggregate\",\"ServiceTicket.findUnique\",\"ServiceTicket.findUniqueOrThrow\",\"ServiceTicket.findFirst\",\"ServiceTicket.findFirstOrThrow\",\"ServiceTicket.findMany\",\"ServiceTicket.createOne\",\"ServiceTicket.createMany\",\"ServiceTicket.createManyAndReturn\",\"ServiceTicket.updateOne\",\"ServiceTicket.updateMany\",\"ServiceTicket.updateManyAndReturn\",\"ServiceTicket.upsertOne\",\"ServiceTicket.deleteOne\",\"ServiceTicket.deleteMany\",\"ServiceTicket.groupBy\",\"ServiceTicket.aggregate\",\"ServiceTicketNote.findUnique\",\"ServiceTicketNote.findUniqueOrThrow\",\"ServiceTicketNote.findFirst\",\"ServiceTicketNote.findFirstOrThrow\",\"ServiceTicketNote.findMany\",\"ServiceTicketNote.createOne\",\"ServiceTicketNote.createMany\",\"ServiceTicketNote.createManyAndReturn\",\"ServiceTicketNote.updateOne\",\"ServiceTicketNote.updateMany\",\"ServiceTicketNote.updateManyAndReturn\",\"ServiceTicketNote.upsertOne\",\"ServiceTicketNote.deleteOne\",\"ServiceTicketNote.deleteMany\",\"ServiceTicketNote.groupBy\",\"ServiceTicketNote.aggregate\",\"ServiceTicketType.findUnique\",\"ServiceTicketType.findUniqueOrThrow\",\"ServiceTicketType.findFirst\",\"ServiceTicketType.findFirstOrThrow\",\"ServiceTicketType.findMany\",\"ServiceTicketType.createOne\",\"ServiceTicketType.createMany\",\"ServiceTicketType.createManyAndReturn\",\"ServiceTicketType.updateOne\",\"ServiceTicketType.updateMany\",\"ServiceTicketType.updateManyAndReturn\",\"ServiceTicketType.upsertOne\",\"ServiceTicketType.deleteOne\",\"ServiceTicketType.deleteMany\",\"ServiceTicketType.groupBy\",\"ServiceTicketType.aggregate\",\"ServiceTicketBoard.findUnique\",\"ServiceTicketBoard.findUniqueOrThrow\",\"ServiceTicketBoard.findFirst\",\"ServiceTicketBoard.findFirstOrThrow\",\"ServiceTicketBoard.findMany\",\"ServiceTicketBoard.createOne\",\"ServiceTicketBoard.createMany\",\"ServiceTicketBoard.createManyAndReturn\",\"ServiceTicketBoard.updateOne\",\"ServiceTicketBoard.updateMany\",\"ServiceTicketBoard.updateManyAndReturn\",\"ServiceTicketBoard.upsertOne\",\"ServiceTicketBoard.deleteOne\",\"ServiceTicketBoard.deleteMany\",\"ServiceTicketBoard.groupBy\",\"ServiceTicketBoard.aggregate\",\"ServiceTicketLocation.findUnique\",\"ServiceTicketLocation.findUniqueOrThrow\",\"ServiceTicketLocation.findFirst\",\"ServiceTicketLocation.findFirstOrThrow\",\"ServiceTicketLocation.findMany\",\"ServiceTicketLocation.createOne\",\"ServiceTicketLocation.createMany\",\"ServiceTicketLocation.createManyAndReturn\",\"ServiceTicketLocation.updateOne\",\"ServiceTicketLocation.updateMany\",\"ServiceTicketLocation.updateManyAndReturn\",\"ServiceTicketLocation.upsertOne\",\"ServiceTicketLocation.deleteOne\",\"ServiceTicketLocation.deleteMany\",\"ServiceTicketLocation.groupBy\",\"ServiceTicketLocation.aggregate\",\"ServiceTicketSource.findUnique\",\"ServiceTicketSource.findUniqueOrThrow\",\"ServiceTicketSource.findFirst\",\"ServiceTicketSource.findFirstOrThrow\",\"ServiceTicketSource.findMany\",\"ServiceTicketSource.createOne\",\"ServiceTicketSource.createMany\",\"ServiceTicketSource.createManyAndReturn\",\"ServiceTicketSource.updateOne\",\"ServiceTicketSource.updateMany\",\"ServiceTicketSource.updateManyAndReturn\",\"ServiceTicketSource.upsertOne\",\"ServiceTicketSource.deleteOne\",\"ServiceTicketSource.deleteMany\",\"ServiceTicketSource.groupBy\",\"ServiceTicketSource.aggregate\",\"ServiceTicketImpact.findUnique\",\"ServiceTicketImpact.findUniqueOrThrow\",\"ServiceTicketImpact.findFirst\",\"ServiceTicketImpact.findFirstOrThrow\",\"ServiceTicketImpact.findMany\",\"ServiceTicketImpact.createOne\",\"ServiceTicketImpact.createMany\",\"ServiceTicketImpact.createManyAndReturn\",\"ServiceTicketImpact.updateOne\",\"ServiceTicketImpact.updateMany\",\"ServiceTicketImpact.updateManyAndReturn\",\"ServiceTicketImpact.upsertOne\",\"ServiceTicketImpact.deleteOne\",\"ServiceTicketImpact.deleteMany\",\"ServiceTicketImpact.groupBy\",\"ServiceTicketImpact.aggregate\",\"ServiceTicketPriority.findUnique\",\"ServiceTicketPriority.findUniqueOrThrow\",\"ServiceTicketPriority.findFirst\",\"ServiceTicketPriority.findFirstOrThrow\",\"ServiceTicketPriority.findMany\",\"ServiceTicketPriority.createOne\",\"ServiceTicketPriority.createMany\",\"ServiceTicketPriority.createManyAndReturn\",\"ServiceTicketPriority.updateOne\",\"ServiceTicketPriority.updateMany\",\"ServiceTicketPriority.updateManyAndReturn\",\"ServiceTicketPriority.upsertOne\",\"ServiceTicketPriority.deleteOne\",\"ServiceTicketPriority.deleteMany\",\"ServiceTicketPriority.groupBy\",\"ServiceTicketPriority.aggregate\",\"ServiceTicketSeverity.findUnique\",\"ServiceTicketSeverity.findUniqueOrThrow\",\"ServiceTicketSeverity.findFirst\",\"ServiceTicketSeverity.findFirstOrThrow\",\"ServiceTicketSeverity.findMany\",\"ServiceTicketSeverity.createOne\",\"ServiceTicketSeverity.createMany\",\"ServiceTicketSeverity.createManyAndReturn\",\"ServiceTicketSeverity.updateOne\",\"ServiceTicketSeverity.updateMany\",\"ServiceTicketSeverity.updateManyAndReturn\",\"ServiceTicketSeverity.upsertOne\",\"ServiceTicketSeverity.deleteOne\",\"ServiceTicketSeverity.deleteMany\",\"ServiceTicketSeverity.groupBy\",\"ServiceTicketSeverity.aggregate\",\"ServiceTicketFinalData.findUnique\",\"ServiceTicketFinalData.findUniqueOrThrow\",\"ServiceTicketFinalData.findFirst\",\"ServiceTicketFinalData.findFirstOrThrow\",\"ServiceTicketFinalData.findMany\",\"ServiceTicketFinalData.createOne\",\"ServiceTicketFinalData.createMany\",\"ServiceTicketFinalData.createManyAndReturn\",\"ServiceTicketFinalData.updateOne\",\"ServiceTicketFinalData.updateMany\",\"ServiceTicketFinalData.updateManyAndReturn\",\"ServiceTicketFinalData.upsertOne\",\"ServiceTicketFinalData.deleteOne\",\"ServiceTicketFinalData.deleteMany\",\"ServiceTicketFinalData.groupBy\",\"ServiceTicketFinalData.aggregate\",\"OpportunityStage.findUnique\",\"OpportunityStage.findUniqueOrThrow\",\"OpportunityStage.findFirst\",\"OpportunityStage.findFirstOrThrow\",\"OpportunityStage.findMany\",\"OpportunityStage.createOne\",\"OpportunityStage.createMany\",\"OpportunityStage.createManyAndReturn\",\"OpportunityStage.updateOne\",\"OpportunityStage.updateMany\",\"OpportunityStage.updateManyAndReturn\",\"OpportunityStage.upsertOne\",\"OpportunityStage.deleteOne\",\"OpportunityStage.deleteMany\",\"OpportunityStage.groupBy\",\"OpportunityStage.aggregate\",\"OpportunityType.findUnique\",\"OpportunityType.findUniqueOrThrow\",\"OpportunityType.findFirst\",\"OpportunityType.findFirstOrThrow\",\"OpportunityType.findMany\",\"OpportunityType.createOne\",\"OpportunityType.createMany\",\"OpportunityType.createManyAndReturn\",\"OpportunityType.updateOne\",\"OpportunityType.updateMany\",\"OpportunityType.updateManyAndReturn\",\"OpportunityType.upsertOne\",\"OpportunityType.deleteOne\",\"OpportunityType.deleteMany\",\"OpportunityType.groupBy\",\"OpportunityType.aggregate\",\"OpportunityStatus.findUnique\",\"OpportunityStatus.findUniqueOrThrow\",\"OpportunityStatus.findFirst\",\"OpportunityStatus.findFirstOrThrow\",\"OpportunityStatus.findMany\",\"OpportunityStatus.createOne\",\"OpportunityStatus.createMany\",\"OpportunityStatus.createManyAndReturn\",\"OpportunityStatus.updateOne\",\"OpportunityStatus.updateMany\",\"OpportunityStatus.updateManyAndReturn\",\"OpportunityStatus.upsertOne\",\"OpportunityStatus.deleteOne\",\"OpportunityStatus.deleteMany\",\"OpportunityStatus.groupBy\",\"OpportunityStatus.aggregate\",\"Opportunity.findUnique\",\"Opportunity.findUniqueOrThrow\",\"Opportunity.findFirst\",\"Opportunity.findFirstOrThrow\",\"Opportunity.findMany\",\"Opportunity.createOne\",\"Opportunity.createMany\",\"Opportunity.createManyAndReturn\",\"Opportunity.updateOne\",\"Opportunity.updateMany\",\"Opportunity.updateManyAndReturn\",\"Opportunity.upsertOne\",\"Opportunity.deleteOne\",\"Opportunity.deleteMany\",\"Opportunity.groupBy\",\"Opportunity.aggregate\",\"schedules\",\"scheduleSpan\",\"ScheduleStatus.findUnique\",\"ScheduleStatus.findUniqueOrThrow\",\"ScheduleStatus.findFirst\",\"ScheduleStatus.findFirstOrThrow\",\"ScheduleStatus.findMany\",\"ScheduleStatus.createOne\",\"ScheduleStatus.createMany\",\"ScheduleStatus.createManyAndReturn\",\"ScheduleStatus.updateOne\",\"ScheduleStatus.updateMany\",\"ScheduleStatus.updateManyAndReturn\",\"ScheduleStatus.upsertOne\",\"ScheduleStatus.deleteOne\",\"ScheduleStatus.deleteMany\",\"ScheduleStatus.groupBy\",\"ScheduleStatus.aggregate\",\"ScheduleType.findUnique\",\"ScheduleType.findUniqueOrThrow\",\"ScheduleType.findFirst\",\"ScheduleType.findFirstOrThrow\",\"ScheduleType.findMany\",\"ScheduleType.createOne\",\"ScheduleType.createMany\",\"ScheduleType.createManyAndReturn\",\"ScheduleType.updateOne\",\"ScheduleType.updateMany\",\"ScheduleType.updateManyAndReturn\",\"ScheduleType.upsertOne\",\"ScheduleType.deleteOne\",\"ScheduleType.deleteMany\",\"ScheduleType.groupBy\",\"ScheduleType.aggregate\",\"ScheduleSpan.findUnique\",\"ScheduleSpan.findUniqueOrThrow\",\"ScheduleSpan.findFirst\",\"ScheduleSpan.findFirstOrThrow\",\"ScheduleSpan.findMany\",\"ScheduleSpan.createOne\",\"ScheduleSpan.createMany\",\"ScheduleSpan.createManyAndReturn\",\"ScheduleSpan.updateOne\",\"ScheduleSpan.updateMany\",\"ScheduleSpan.updateManyAndReturn\",\"ScheduleSpan.upsertOne\",\"ScheduleSpan.deleteOne\",\"ScheduleSpan.deleteMany\",\"ScheduleSpan.groupBy\",\"ScheduleSpan.aggregate\",\"Schedule.findUnique\",\"Schedule.findUniqueOrThrow\",\"Schedule.findFirst\",\"Schedule.findFirstOrThrow\",\"Schedule.findMany\",\"Schedule.createOne\",\"Schedule.createMany\",\"Schedule.createManyAndReturn\",\"Schedule.updateOne\",\"Schedule.updateMany\",\"Schedule.updateManyAndReturn\",\"Schedule.upsertOne\",\"Schedule.deleteOne\",\"Schedule.deleteMany\",\"Schedule.groupBy\",\"Schedule.aggregate\",\"CredentialType.findUnique\",\"CredentialType.findUniqueOrThrow\",\"CredentialType.findFirst\",\"CredentialType.findFirstOrThrow\",\"CredentialType.findMany\",\"CredentialType.createOne\",\"CredentialType.createMany\",\"CredentialType.createManyAndReturn\",\"CredentialType.updateOne\",\"CredentialType.updateMany\",\"CredentialType.updateManyAndReturn\",\"CredentialType.upsertOne\",\"CredentialType.deleteOne\",\"CredentialType.deleteMany\",\"CredentialType.groupBy\",\"CredentialType.aggregate\",\"SecureValue.findUnique\",\"SecureValue.findUniqueOrThrow\",\"SecureValue.findFirst\",\"SecureValue.findFirstOrThrow\",\"SecureValue.findMany\",\"SecureValue.createOne\",\"SecureValue.createMany\",\"SecureValue.createManyAndReturn\",\"SecureValue.updateOne\",\"SecureValue.updateMany\",\"SecureValue.updateManyAndReturn\",\"SecureValue.upsertOne\",\"SecureValue.deleteOne\",\"SecureValue.deleteMany\",\"SecureValue.groupBy\",\"SecureValue.aggregate\",\"Credential.findUnique\",\"Credential.findUniqueOrThrow\",\"Credential.findFirst\",\"Credential.findFirstOrThrow\",\"Credential.findMany\",\"Credential.createOne\",\"Credential.createMany\",\"Credential.createManyAndReturn\",\"Credential.updateOne\",\"Credential.updateMany\",\"Credential.updateManyAndReturn\",\"Credential.upsertOne\",\"Credential.deleteOne\",\"Credential.deleteMany\",\"Credential.groupBy\",\"Credential.aggregate\",\"GeneratedQuotes.findUnique\",\"GeneratedQuotes.findUniqueOrThrow\",\"GeneratedQuotes.findFirst\",\"GeneratedQuotes.findFirstOrThrow\",\"GeneratedQuotes.findMany\",\"GeneratedQuotes.createOne\",\"GeneratedQuotes.createMany\",\"GeneratedQuotes.createManyAndReturn\",\"GeneratedQuotes.updateOne\",\"GeneratedQuotes.updateMany\",\"GeneratedQuotes.updateManyAndReturn\",\"GeneratedQuotes.upsertOne\",\"GeneratedQuotes.deleteOne\",\"GeneratedQuotes.deleteMany\",\"GeneratedQuotes.groupBy\",\"GeneratedQuotes.aggregate\",\"TaxCode.findUnique\",\"TaxCode.findUniqueOrThrow\",\"TaxCode.findFirst\",\"TaxCode.findFirstOrThrow\",\"TaxCode.findMany\",\"TaxCode.createOne\",\"TaxCode.createMany\",\"TaxCode.createManyAndReturn\",\"TaxCode.updateOne\",\"TaxCode.updateMany\",\"TaxCode.updateManyAndReturn\",\"TaxCode.upsertOne\",\"TaxCode.deleteOne\",\"TaxCode.deleteMany\",\"TaxCode.groupBy\",\"TaxCode.aggregate\",\"CwMember.findUnique\",\"CwMember.findUniqueOrThrow\",\"CwMember.findFirst\",\"CwMember.findFirstOrThrow\",\"CwMember.findMany\",\"CwMember.createOne\",\"CwMember.createMany\",\"CwMember.createManyAndReturn\",\"CwMember.updateOne\",\"CwMember.updateMany\",\"CwMember.updateManyAndReturn\",\"CwMember.upsertOne\",\"CwMember.deleteOne\",\"CwMember.deleteMany\",\"CwMember.groupBy\",\"CwMember.aggregate\",\"AND\",\"OR\",\"NOT\",\"id\",\"cwMemberId\",\"identifier\",\"firstName\",\"lastName\",\"officeEmail\",\"inactiveFlag\",\"apiKey\",\"cwLastUpdated\",\"createdAt\",\"updatedAt\",\"equals\",\"in\",\"notIn\",\"lt\",\"lte\",\"gt\",\"gte\",\"not\",\"contains\",\"startsWith\",\"endsWith\",\"uid\",\"code\",\"codeCaption\",\"description\",\"rate\",\"defaultFlag\",\"every\",\"some\",\"none\",\"quoteRegenData\",\"quoteRegenParams\",\"quoteRegenHash\",\"downloads\",\"quoteFile\",\"quoteFileName\",\"opportunityId\",\"createdById\",\"string_contains\",\"string_starts_with\",\"string_ends_with\",\"array_starts_with\",\"array_ends_with\",\"array_contains\",\"name\",\"subCredentialOfId\",\"typeId\",\"fields\",\"companyId\",\"content\",\"hash\",\"credentialId\",\"permissionScope\",\"icon\",\"memberId\",\"closedFlag\",\"reminderFlag\",\"allDayFlag\",\"acknowledgementFlag\",\"meetingFlag\",\"recurringFlag\",\"billableFlag\",\"acknowledgedById\",\"acknowledgedAt\",\"startDate\",\"endDate\",\"hoursScheduled\",\"duration\",\"hoursPerDay\",\"reminderMinutes\",\"statusId\",\"scheduleSpanId\",\"updatedById\",\"closedById\",\"closedAt\",\"spanDesc\",\"displayColor\",\"tableReference\",\"moduleId\",\"scheduleTypeId\",\"systemFlag\",\"displayFlag\",\"color\",\"softFlag\",\"oppNarrative\",\"stageId\",\"taxCodeId\",\"OpportunityInterest\",\"interest\",\"probability\",\"primarySalesRepId\",\"secondarySalesRepId\",\"contactId\",\"siteId\",\"customerPO\",\"locationId\",\"departmentId\",\"expectedCloseDate\",\"pipelineChangeDate\",\"dateBecameLead\",\"closedDate\",\"productSequence\",\"eneteredBy\",\"has\",\"hasEvery\",\"hasSome\",\"wonFlag\",\"lostFlag\",\"closeFlag\",\"seqNbr\",\"funnelColor\",\"timeBillableFlag\",\"expenseBillableFlag\",\"productBillableFlag\",\"timeInvoiceableFlag\",\"expenseInvoiceableFlag\",\"productInvoiceableFlag\",\"autoAssignNewFlag\",\"autoAssignEmailCreatedFlag\",\"autoAssignPortalCreatedFlag\",\"projectFlag\",\"lockDescriptionFlag\",\"emailContactFlag\",\"emailResourceFlag\",\"resolutionSortOrder\",\"internalAnalysisSortOrder\",\"notesMd\",\"authorId\",\"problemFlag\",\"resolutionFlag\",\"internalAnalysisFlag\",\"internalMemberFlag\",\"createdByParentFlag\",\"mergedFlag\",\"bundledFlag\",\"serviceTicketId\",\"summary\",\"addressLine1\",\"addressLine2\",\"city\",\"USState\",\"state\",\"zipCode\",\"Country\",\"country\",\"contactName\",\"phone\",\"phoneExtension\",\"PhoneType\",\"phoneType\",\"email\",\"poNumber\",\"billCompleteFlag\",\"billUnapprovedFlag\",\"billingAmount\",\"BillingMethod\",\"billingMethod\",\"dateRequested\",\"BillingType\",\"billingType\",\"billingInstructions\",\"rejectedFlag\",\"redFlag\",\"publishFlag\",\"ticketOwnerId\",\"serviceTicketBoardId\",\"severityId\",\"impactId\",\"priorityId\",\"sourceId\",\"parentId\",\"companyAddressId\",\"billingCompanyId\",\"billingAddressId\",\"rejectedAt\",\"qty\",\"internalNote\",\"shortDescription\",\"sequenceNumber\",\"procurementNotes\",\"productNarrative\",\"unitPrice\",\"unitCost\",\"listPrice\",\"discount\",\"recurringRevenue\",\"recurringCost\",\"qtyPicked\",\"qtyShipped\",\"cancelReason\",\"cancelQty\",\"taxableFlag\",\"invoiceFlag\",\"poApprovedFlag\",\"calcPriceFlag\",\"calcCostFlag\",\"cancelFlag\",\"catalogItemId\",\"corporateLocationId\",\"cancelById\",\"cancelledAt\",\"customerDescription\",\"internalNotes\",\"subcategoryId\",\"manufacturerId\",\"partNumber\",\"vendorName\",\"vendorSku\",\"vendorCwId\",\"price\",\"cost\",\"inactive\",\"salesTaxable\",\"onHand\",\"classId\",\"minQty\",\"warehouseId\",\"itemId\",\"enteredById\",\"lockedFlag\",\"qtyOnHand\",\"warehouseBinId\",\"minQuantity\",\"maxQuantity\",\"categoryId\",\"active\",\"default\",\"nickname\",\"title\",\"GenderType\",\"gender\",\"birthday\",\"fax\",\"defaultMailFlag\",\"defaultBillFlag\",\"defaultShipFlag\",\"website\",\"deleteFlag\",\"dateDeleted\",\"taxId\",\"taxExempt\",\"deletedById\",\"deletedAt\",\"moniker\",\"permissions\",\"login\",\"image\",\"hidden\",\"cwIdentifier\",\"userId\",\"token\",\"sessionKey\",\"expires\",\"refreshTokenGenerated\",\"refreshedAt\",\"invalidatedAt\",\"syncJobRunId\",\"tableName\",\"syncMode\",\"recordsProcessed\",\"recordsInserted\",\"recordsSkipped\",\"recordsFailed\",\"recordsDeleted\",\"sampleErrors\",\"durationMs\",\"SyncJobType\",\"jobType\",\"SyncJobStatus\",\"triggeredBy\",\"startedAt\",\"completedAt\",\"errorSummary\",\"is\",\"isNot\",\"connectOrCreate\",\"upsert\",\"disconnect\",\"delete\",\"connect\",\"createMany\",\"set\",\"updateMany\",\"deleteMany\",\"push\",\"increment\",\"decrement\",\"multiply\",\"divide\"]"), + graph: "yhjSA9AFDQQAAMAMACA8AAC_DL0IIqwGAAC9DAAwrQYAAAkAEK4GAAC9DAAwrwYBAAAAAbgGQAD2CgAhuQZAAPYKACG7CAAAvgy7CCK9CAEA8goAIb4IQAD1CgAhvwhAAPUKACHACAEA8woAIQEAAAABACAQAwAAwgwAIKwGAADBDAAwrQYAAAMAEK4GAADBDAAwrwYBAPIKACG4BkAA9goAIbAIAQDyCgAhsQgBAPIKACGyCAEA8goAIbMIAgCUCwAhtAgCAJQLACG1CAIAlAsAIbYIAgCUCwAhtwgCAJQLACG4CAAAiQsAILkIAgCUCwAhAQMAAN0VACAQAwAAwgwAIKwGAADBDAAwrQYAAAMAEK4GAADBDAAwrwYBAAAAAbgGQAD2CgAhsAgBAPIKACGxCAEA8goAIbIIAQDyCgAhswgCAJQLACG0CAIAlAsAIbUIAgCUCwAhtggCAJQLACG3CAIAlAsAIbgIAACJCwAguQgCAJQLACEDAAAAAwAgAQAABAAwAgAABQAgAQAAAAMAIAEAAAABACANBAAAwAwAIDwAAL8MvQgirAYAAL0MADCtBgAACQAQrgYAAL0MADCvBgEA8goAIbgGQAD2CgAhuQZAAPYKACG7CAAAvgy7CCK9CAEA8goAIb4IQAD1CgAhvwhAAPUKACHACAEA8woAIQQEAADcFQAgvggAAMMMACC_CAAAwwwAIMAIAADDDAAgAwAAAAkAIAEAAAoAMAIAAAEAIAMAAAAJACABAAAKADACAAABACADAAAACQAgAQAACgAwAgAAAQAgCgQAANsVACA8AAAAvQgCrwYBAAAAAbgGQAAAAAG5BkAAAAABuwgAAAC7CAK9CAEAAAABvghAAAAAAb8IQAAAAAHACAEAAAABAQsAAA4AIAk8AAAAvQgCrwYBAAAAAbgGQAAAAAG5BkAAAAABuwgAAAC7CAK9CAEAAAABvghAAAAAAb8IQAAAAAHACAEAAAABAQsAABAAMAELAAAQADAKBAAAzhUAIDwAAM0VvQgirwYBAMkMACG4BkAAzgwAIbkGQADODAAhuwgAAMwVuwgivQgBAMkMACG-CEAAzQwAIb8IQADNDAAhwAgBAMsMACECAAAAAQAgCwAAEwAgCTwAAM0VvQgirwYBAMkMACG4BkAAzgwAIbkGQADODAAhuwgAAMwVuwgivQgBAMkMACG-CEAAzQwAIb8IQADNDAAhwAgBAMsMACECAAAACQAgCwAAFQAgAgAAAAkAIAsAABUAIAMAAAABACASAAAOACATAAATACABAAAAAQAgAQAAAAkAIAYFAADJFQAgGAAAyxUAIBkAAMoVACC-CAAAwwwAIL8IAADDDAAgwAgAAMMMACAMPAAAuAy9CCKsBgAAtgwAMK0GAAAcABCuBgAAtgwAMK8GAQDdCgAhuAZAAOIKACG5BkAA4goAIbsIAAC3DLsIIr0IAQDdCgAhvghAAOEKACG_CEAA4QoAIcAIAQDfCgAhAwAAAAkAIAEAABsAMBcAABwAIAMAAAAJACABAAAKADACAAABACABAAAABQAgAQAAAAUAIAMAAAADACABAAAEADACAAAFACADAAAAAwAgAQAABAAwAgAABQAgAwAAAAMAIAEAAAQAMAIAAAUAIA0DAADIFQAgrwYBAAAAAbgGQAAAAAGwCAEAAAABsQgBAAAAAbIIAQAAAAGzCAIAAAABtAgCAAAAAbUIAgAAAAG2CAIAAAABtwgCAAAAAbgIgAAAAAG5CAIAAAABAQsAACQAIAyvBgEAAAABuAZAAAAAAbAIAQAAAAGxCAEAAAABsggBAAAAAbMIAgAAAAG0CAIAAAABtQgCAAAAAbYIAgAAAAG3CAIAAAABuAiAAAAAAbkIAgAAAAEBCwAAJgAwAQsAACYAMA0DAADHFQAgrwYBAMkMACG4BkAAzgwAIbAIAQDJDAAhsQgBAMkMACGyCAEAyQwAIbMIAgDKDAAhtAgCAMoMACG1CAIAygwAIbYIAgDKDAAhtwgCAMoMACG4CIAAAAABuQgCAMoMACECAAAABQAgCwAAKQAgDK8GAQDJDAAhuAZAAM4MACGwCAEAyQwAIbEIAQDJDAAhsggBAMkMACGzCAIAygwAIbQIAgDKDAAhtQgCAMoMACG2CAIAygwAIbcIAgDKDAAhuAiAAAAAAbkIAgDKDAAhAgAAAAMAIAsAACsAIAIAAAADACALAAArACADAAAABQAgEgAAJAAgEwAAKQAgAQAAAAUAIAEAAAADACAFBQAAwhUAIBgAAMUVACAZAADEFQAgKgAAwxUAICsAAMYVACAPrAYAALUMADCtBgAAMgAQrgYAALUMADCvBgEA3QoAIbgGQADiCgAhsAgBAN0KACGxCAEA3QoAIbIIAQDdCgAhswgCAN4KACG0CAIA3goAIbUIAgDeCgAhtggCAN4KACG3CAIA3goAIbgIAACACwAguQgCAN4KACEDAAAAAwAgAQAAMQAwFwAAMgAgAwAAAAMAIAEAAAQAMAIAAAUAIAswAACMDAAgrAYAAK0MADCtBgAAQwAQrgYAAK0MADCvBgEAAAABqQgBAPIKACGrCAEAAAABrAhAAPYKACGtCCAA9AoAIa4IQAD1CgAhrwhAAPUKACEBAAAANQAgCi4AALQMACCsBgAAswwAMK0GAAA3ABCuBgAAswwAMK8GAQDyCgAhuAZAAPYKACG5BkAA9goAIZQIAQDyCgAhowgBAPIKACGkCAEA8goAIQEuAADBFQAgCi4AALQMACCsBgAAswwAMK0GAAA3ABCuBgAAswwAMK8GAQAAAAG4BkAA9goAIbkGQAD2CgAhlAgBAPIKACGjCAEAAAABpAgBAPIKACEDAAAANwAgAQAAOAAwAgAAOQAgIi8AAK8MACAxAACwDAAgMgAAsQwAIDMBAPMKACE4AAChDAAgOQAA_goAIGgAAKsMACByAACyDAAgcwAAsgwAIHQAAP4KACB1AACsCwAgdgAArAsAIHcAAKwLACB4AACsCwAgeQAAkgwAIKwGAACuDAAwrQYAADsAEK4GAACuDAAwrwYBAPIKACGwBgIAlwsAIbIGAQDzCgAhswYBAPMKACG4BkAA9goAIbkGQAD2CgAhxgcBAPIKACGRCCAA9AoAIZQIAQDzCgAhpAgBAPMKACGlCAEA8goAIaYIAQDzCgAhpwggAPQKACGoCAEA8woAIakIAQDzCgAhqggBAPMKACEYLwAAvRUAIDEAAL4VACAyAAC_FQAgMwAAwwwAIDgAALUVACA5AADQEAAgaAAAuxUAIHIAAMAVACBzAADAFQAgdAAA0BAAIHUAAPURACB2AAD1EQAgdwAA9REAIHgAAPURACB5AACsFQAgsAYAAMMMACCyBgAAwwwAILMGAADDDAAglAgAAMMMACCkCAAAwwwAIKYIAADDDAAgqAgAAMMMACCpCAAAwwwAIKoIAADDDAAgIi8AAK8MACAxAACwDAAgMgAAsQwAIDMBAPMKACE4AAChDAAgOQAA_goAIGgAAKsMACByAACyDAAgcwAAsgwAIHQAAP4KACB1AACsCwAgdgAArAsAIHcAAKwLACB4AACsCwAgeQAAkgwAIKwGAACuDAAwrQYAADsAEK4GAACuDAAwrwYBAAAAAbAGAgAAAAGyBgEA8woAIbMGAQDzCgAhuAZAAPYKACG5BkAA9goAIcYHAQAAAAGRCCAA9AoAIZQIAQDzCgAhpAgBAPMKACGlCAEAAAABpggBAPMKACGnCCAA9AoAIagIAQAAAAGpCAEAAAABqggBAPMKACEDAAAAOwAgAQAAPAAwAgAAPQAgAQAAADsAIA8wAAD3CgAgrAYAAPEKADCtBgAAQAAQrgYAAPEKADCvBgEA8goAIbAGAgCUCwAhsQYBAPIKACGyBgEA8goAIbMGAQDyCgAhtAYBAPMKACG1BiAA9AoAIbYGAQDzCgAhtwZAAPUKACG4BkAA9goAIbkGQAD2CgAhAQAAAEAAIAEAAAA7ACALMAAAjAwAIKwGAACtDAAwrQYAAEMAEK4GAACtDAAwrwYBAPIKACGpCAEA8goAIasIAQDyCgAhrAhAAPYKACGtCCAA9AoAIa4IQAD1CgAhrwhAAPUKACEDMAAAvxAAIK4IAADDDAAgrwgAAMMMACADAAAAQwAgAQAARAAwAgAANQAgGjUAAPgLACA5AAD-CgAgWQAArAsAIGgAAKsMACBrAACKCwAgbgAArAwAIG8AAPcKACBwAAD3CgAgcQAArAsAIKwGAACqDAAwrQYAAEYAEK4GAACqDAAwrwYCAJQLACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHcBgEA8goAIcIHAQDzCgAhiggBAPMKACGcCAEA8woAIZ0IIAD0CgAhnghAAPUKACGfCAEA8woAIaAIIAD0CgAhoQgBAPMKACGiCEAA9QoAIRA1AACiFQAgOQAA0BAAIFkAAPURACBoAAC7FQAgawAA6hAAIG4AALwVACBvAAC_EAAgcAAAvxAAIHEAAPURACDCBwAAwwwAIIoIAADDDAAgnAgAAMMMACCeCAAAwwwAIJ8IAADDDAAgoQgAAMMMACCiCAAAwwwAIBo1AAD4CwAgOQAA_goAIFkAAKwLACBoAACrDAAgawAAigsAIG4AAKwMACBvAAD3CgAgcAAA9woAIHEAAKwLACCsBgAAqgwAMK0GAABGABCuBgAAqgwAMK8GAgAAAAG4BkAA9goAIbkGQAD2CgAhxQYBAAAAAdwGAQDyCgAhwgcBAPMKACGKCAEA8woAIZwIAQDzCgAhnQggAPQKACGeCEAA9QoAIZ8IAQDzCgAhoAggAPQKACGhCAEA8woAIaIIQAD1CgAhAwAAAEYAIAEAAEcAMAIAAEgAIBo0AADvCwAgOQAA_goAIFkAAKwLACBgAACaDAAgrAYAAKgMADCtBgAASgAQrgYAAKgMADCvBgIAlAsAIbIGAQDyCgAhswYBAPIKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHgBgIAlwsAIeYGAgCXCwAhwgcBAPMKACHDBwEA8woAIcUHAACPDMUHI8YHAQDzCgAh2wcCAJcLACGRCCAA9AoAIZIIIAD0CgAhkwgBAPMKACGUCAEA8woAIZYIAACpDJYII5cIQAD1CgAhDzQAAJ4VACA5AADQEAAgWQAA9REAIGAAALQVACDgBgAAwwwAIOYGAADDDAAgwgcAAMMMACDDBwAAwwwAIMUHAADDDAAgxgcAAMMMACDbBwAAwwwAIJMIAADDDAAglAgAAMMMACCWCAAAwwwAIJcIAADDDAAgGjQAAO8LACA5AAD-CgAgWQAArAsAIGAAAJoMACCsBgAAqAwAMK0GAABKABCuBgAAqAwAMK8GAgAAAAGyBgEA8goAIbMGAQDyCgAhuAZAAPYKACG5BkAA9goAIcUGAQAAAAHgBgIAlwsAIeYGAgCXCwAhwgcBAPMKACHDBwEA8woAIcUHAACPDMUHI8YHAQDzCgAh2wcCAJcLACGRCCAA9AoAIZIIIAD0CgAhkwgBAPMKACGUCAEA8woAIZYIAACpDJYII5cIQAD1CgAhAwAAAEoAIAEAAEsAMAIAAEwAIB4zAAD3CgAgNAAA9QsAIDUAAPgLACBZAACsCwAgZgAA_goAIGcAAKwLACCsBgAA9wsAMK0GAABOABCuBgAA9wsAMK8GAgCUCwAhtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIcoGIAD0CgAh3AYBAPIKACHgBgIAlAsAIfgGAQDzCgAhuQcBAPMKACG6BwEA8woAIbsHAQDzCgAhvQcAAOcLvQcjvgcBAPMKACHABwAA6AvAByPCBwEA8woAIZgIAQDzCgAhmQggAPQKACGaCCAA9AoAIZsIIAD0CgAhAQAAAE4AIAEAAAA7ACADAAAASgAgAQAASwAwAgAATAAgLzMBAPIKACE0AADvCwAgOAAAoQwAIDoAAKIMACA7AACjDAAgPAAApAwAID0AAKUMACA-AAD3CgAgPwAA9woAIEAAAJkMACBBAACaDAAgQgAApgwAIEUBAPMKACFYAADqCwAgXQEA8woAIWUAAKcMACCsBgAAnwwAMK0GAABSABCuBgAAnwwAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAh3AYBAPIKACHeBgIAlAsAIeAGAgCXCwAh5wYgAPQKACH2BgIAlwsAIfkGAQDzCgAhhAcBAPMKACGFBwIAlwsAIYYHAgCXCwAhiAcAAKAMiAcjiQcIAIMMACGKBwEA8woAIYsHAQDzCgAhjAcCAJcLACGNBwIAlwsAIY4HAQDzCgAhjwcCAJcLACGQBwIAlwsAIZEHQAD1CgAhkgdAAPUKACGTB0AA9QoAIZQHQAD1CgAhlQcAAJ4LACCWBwEA8goAISE0AACeFQAgOAAAtRUAIDoAALYVACA7AAC3FQAgPAAAuBUAID0AALkVACA-AAC_EAAgPwAAvxAAIEAAALMVACBBAAC0FQAgQgAAqxUAIEUAAMMMACBYAACBFQAgXQAAwwwAIGUAALoVACDgBgAAwwwAIPYGAADDDAAg-QYAAMMMACCEBwAAwwwAIIUHAADDDAAghgcAAMMMACCIBwAAwwwAIIoHAADDDAAgiwcAAMMMACCMBwAAwwwAII0HAADDDAAgjgcAAMMMACCPBwAAwwwAIJAHAADDDAAgkQcAAMMMACCSBwAAwwwAIJMHAADDDAAglAcAAMMMACAvMwEA8goAITQAAO8LACA4AAChDAAgOgAAogwAIDsAAKMMACA8AACkDAAgPQAApQwAID4AAPcKACA_AAD3CgAgQAAAmQwAIEEAAJoMACBCAACmDAAgRQEA8woAIVgAAOoLACBdAQDzCgAhZQAApwwAIKwGAACfDAAwrQYAAFIAEK4GAACfDAAwrwYCAAAAAbgGQAD2CgAhuQZAAPYKACHFBgEAAAAB3AYBAPIKACHeBgIAlAsAIeAGAgCXCwAh5wYgAPQKACH2BgIAlwsAIfkGAQDzCgAhhAcBAPMKACGFBwIAlwsAIYYHAgCXCwAhiAcAAKAMiAcjiQcIAIMMACGKBwEA8woAIYsHAQDzCgAhjAcCAJcLACGNBwIAlwsAIY4HAQDzCgAhjwcCAJcLACGQBwIAlwsAIZEHQAD1CgAhkgdAAPUKACGTB0AA9QoAIZQHQAD1CgAhlQcAAJ4LACCWBwEA8goAIQMAAABSACABAABTADACAABUACAQNgAAngwAIDcAAPcKACCsBgAAnAwAMK0GAABWABCuBgAAnAwAMK8GAQDyCgAhuAZAAPYKACG5BkAA9goAIc4GAACJCwAgzwYAAIkLACDQBgEA8goAIdEGAACJCwAg0gYAAZ0MACHTBgEA8goAIdQGAQDyCgAh1QYBAPMKACEDNgAAqhUAIDcAAL8QACDVBgAAwwwAIBA2AACeDAAgNwAA9woAIKwGAACcDAAwrQYAAFYAEK4GAACcDAAwrwYBAAAAAbgGQAD2CgAhuQZAAPYKACHOBgAAiQsAIM8GAACJCwAg0AYBAAAAAdEGAACJCwAg0gYAAZ0MACHTBgEA8goAIdQGAQDyCgAh1QYBAPMKACEDAAAAVgAgAQAAVwAwAgAAWAAgAQAAADsAIAMAAABSACABAABTADACAABUACABAAAAUgAgDDkAAP4KACCsBgAApwsAMK0GAABdABCuBgAApwsAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAh3AYBAPIKACH4BgEA8woAIZ0HAgCXCwAhngcBAPMKACEBAAAAXQAgAwAAAFIAIAEAAFMAMAIAAFQAIAEAAABSACAROQAA_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_EAAg1QYAAMMMACDgBgAAwwwAIPgGAADDDAAg-QYAAMMMACD6BgAAwwwAIIwHAADDDAAguQcAAMMMACC6BwAAwwwAILsHAADDDAAgvQcAAMMMACC-BwAAwwwAIMAHAADDDAAgwQcAAMMMACDCBwAAwwwAIMMHAADDDAAgxQcAAMMMACDGBwAAwwwAIMcHAADDDAAgzQcAAMMMACDQBwAAwwwAINQHAADDDAAg1QcAAMMMACDaBwAAwwwAINsHAADDDAAg3AcAAMMMACDdBwAAwwwAIN4HAADDDAAgSDMAAPcKACA0AADvCwAgNwAA9woAIEAAAJkMACBCAACXDAAgRQAAkgwAIFgAAOoLACBaAACTDAAgWwAAlAwAIFwAAJUMACBdAACWDAAgXgAAmAwAIF8AAPcKACBgAACaDAAgYQAA7wsAIGIAAJoMACBjAAD3CgAgrAYAAI4MADCtBgAAdQAQrgYAAI4MADCvBgIAAAABuAZAAPYKACG5BkAA9goAIcUGAQAAAAHVBgEA8woAIeAGAgCXCwAh5wYgAPQKACH4BgEA8woAIfkGAQDzCgAh-gZAAPUKACGMBwIAlwsAIY8HAgCUCwAhnwcgAPQKACGgByAA9AoAIaEHIAD0CgAhogcgAPQKACGjByAA9AoAIaQHIAD0CgAhuAcBAPIKACG5BwEA8woAIboHAQDzCgAhuwcBAPMKACG9BwAA5wu9ByO-BwEA8woAIcAHAADoC8AHI8EHAQDzCgAhwgcBAPMKACHDBwEA8woAIcUHAACPDMUHI8YHAQDzCgAhxwcBAPMKACHIByAA9AoAIckHIAD0CgAhygcIAIMMACHMBwAAkAzMByLNB0AA9QoAIc8HAACRDM8HItAHAQDzCgAh0QcgAPQKACHSByAA9AoAIdMHIAD0CgAh1AcBAPMKACHVBwIAlwsAIdYHAgCUCwAh1wcCAJQLACHYBwIAlAsAIdkHAgCUCwAh2gcCAJcLACHbBwIAlwsAIdwHAgCXCwAh3QcCAJcLACHeB0AA9QoAIQMAAAB1ACABAAB2ADACAAB3ACAWQwAAjAwAIEQAAI0MACBFAQDyCgAhrAYAAIsMADCtBgAAeQAQrgYAAIsMADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIdUGAQDzCgAh-AYBAPMKACGuBwEA8goAIa8HAQDyCgAhsAcgAPQKACGxByAA9AoAIbIHIAD0CgAhswcgAPQKACG0ByAA9AoAIbUHIAD0CgAhtgcgAPQKACG3BwIAlAsAIQRDAAC_EAAgRAAAqRUAINUGAADDDAAg-AYAAMMMACAWQwAAjAwAIEQAAI0MACBFAQDyCgAhrAYAAIsMADCtBgAAeQAQrgYAAIsMADCvBgIAAAABuAZAAPYKACG5BkAA9goAIcUGAQAAAAHVBgEA8woAIfgGAQDzCgAhrgcBAPIKACGvBwEA8goAIbAHIAD0CgAhsQcgAPQKACGyByAA9AoAIbMHIAD0CgAhtAcgAPQKACG1ByAA9AoAIbYHIAD0CgAhtwcCAJQLACEDAAAAeQAgAQAAegAwAgAAewAgLjYAAIgMACBEAACHDAAgVgAAiQwAIFcAAIoMACCsBgAAhgwAMK0GAAB9ABCuBgAAhgwAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHUBgIAlwsAIdUGAQDzCgAh7AYgAPQKACHtBiAA9AoAIfgGAQDzCgAh-QYBAPMKACH6BkAA9QoAIbcHAgCXCwAh3wcIAIMMACHgBwEA8woAIeEHAQDzCgAh4gcCAJcLACHjBwEA8woAIeQHAQDzCgAh5QcIAIMMACHmBwgAgwwAIecHCACDDAAh6AcIAIMMACHpBwgAgwwAIeoHCACDDAAh6wcCAJQLACHsBwIAlAsAIe0HAQDzCgAh7gcIAP0KACHvByAA9AoAIfAHIAD0CgAh8QcgAPQKACHyByAA9AoAIfMHIAD0CgAh9AcgAPQKACH1BwIAlAsAIfYHAgCUCwAh9wcBAPIKACH4B0AA9QoAIRM2AACqFQAgRAAAqRUAIFYAAKQVACBXAACrFQAgyAYAAMMMACDUBgAAwwwAINUGAADDDAAg-AYAAMMMACD5BgAAwwwAIPoGAADDDAAgtwcAAMMMACDgBwAAwwwAIOEHAADDDAAg4gcAAMMMACDjBwAAwwwAIOQHAADDDAAg7QcAAMMMACDuBwAAwwwAIPgHAADDDAAgLjYAAIgMACBEAACHDAAgVgAAiQwAIFcAAIoMACCsBgAAhgwAMK0GAAB9ABCuBgAAhgwAMK8GAgAAAAG4BkAA9goAIbkGQAD2CgAhxQYBAAAAAcgGAQDzCgAh1AYCAJcLACHVBgEA8woAIewGIAD0CgAh7QYgAPQKACH4BgEA8woAIfkGAQDzCgAh-gZAAPUKACG3BwIAlwsAId8HCACDDAAh4AcBAPMKACHhBwEA8woAIeIHAgCXCwAh4wcBAPMKACHkBwEA8woAIeUHCACDDAAh5gcIAIMMACHnBwgAgwwAIegHCACDDAAh6QcIAIMMACHqBwgAgwwAIesHAgCUCwAh7AcCAJQLACHtBwEA8woAIe4HCAD9CgAh7wcgAPQKACHwByAA9AoAIfEHIAD0CgAh8gcgAPQKACHzByAA9AoAIfQHIAD0CgAh9QcCAJQLACH2BwIAlAsAIfcHAQDyCgAh-AdAAPUKACEDAAAAfQAgAQAAfgAwAgAAfwAgAQAAAHUAIAEAAABSACAgRgAA1QsAIEcAANULACBLAACEDAAgTAAAhQwAIFEAAM8LACBUAADOCwAgVQAA6gsAIKwGAACCDAAwrQYAAIMBABCuBgAAggwAMK8GAgCUCwAhsQYBAPMKACG3BkAA9QoAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh3AYBAPIKACH5BwEA8woAIfoHAQDzCgAh-wcCAJQLACH8BwIAlwsAIf0HAQDzCgAh_gcBAPMKACH_BwEA8woAIYAIAgCXCwAhgQgIAIMMACGCCAgAgwwAIYMIIAD0CgAhhAggAPQKACGFCAIAlAsAIYYIAQDzCgAhEkYAAIUUACBHAACFFAAgSwAApxUAIEwAAKgVACBRAADnEwAgVAAA5hMAIFUAAIEVACCxBgAAwwwAILcGAADDDAAgyAYAAMMMACD5BwAAwwwAIPoHAADDDAAg_AcAAMMMACD9BwAAwwwAIP4HAADDDAAg_wcAAMMMACCACAAAwwwAIIYIAADDDAAgIEYAANULACBHAADVCwAgSwAAhAwAIEwAAIUMACBRAADPCwAgVAAAzgsAIFUAAOoLACCsBgAAggwAMK0GAACDAQAQrgYAAIIMADCvBgIAAAABsQYBAAAAAbcGQAD1CgAhuAZAAPYKACG5BkAA9goAIcUGAQAAAAHIBgEA8woAIdwGAQDyCgAh-QcBAPMKACH6BwEA8woAIfsHAgCUCwAh_AcCAJcLACH9BwEA8woAIf4HAQDzCgAh_wcBAPMKACGACAIAlwsAIYEICACDDAAhgggIAIMMACGDCCAA9AoAIYQIIAD0CgAhhQgCAJQLACGGCAEA8woAIQMAAACDAQAgAQAAhAEAMAIAAIUBACADAAAAgwEAIAEAAIQBADACAACFAQAgD0kAAIEMACBKAADVCwAgrAYAAIAMADCtBgAAiAEAEK4GAACADAAwrwYCAJQLACG1BiAA9AoAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhkAgCAJQLACEFSQAAphUAIEoAAIUUACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACAPSQAAgQwAIEoAANULACCsBgAAgAwAMK0GAACIAQAQrgYAAIAMADCvBgIAAAABtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAAAAAcgGAQDzCgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhkAgCAJQLACEDAAAAiAEAIAEAAIkBADACAACKAQAgAQAAAIgBACADAAAAgwEAIAEAAIQBADACAACFAQAgAQAAAIMBACANSgAA1QsAIKwGAADUCwAwrQYAAI8BABCuBgAA1AsAMK8GAgCUCwAhtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdUGAQDzCgAh3AYBAPIKACH4BgEA8woAIQEAAACPAQAgAwAAAIMBACABAACEAQAwAgAAhQEAIAEAAACDAQAgD04AAPoLACBPAAD9CwAgUwAA_wsAIKwGAAD-CwAwrQYAAJMBABCuBgAA_gsAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAh-AYBAPMKACGICAIAlwsAIYkIAgCXCwAhjAgCAJQLACGNCAIAlAsAIQZOAACjFQAgTwAApBUAIFMAAKUVACD4BgAAwwwAIIgIAADDDAAgiQgAAMMMACAPTgAA-gsAIE8AAP0LACBTAAD_CwAgrAYAAP4LADCtBgAAkwEAEK4GAAD-CwAwrwYCAAAAAbgGQAD2CgAhuQZAAPYKACHFBgEAAAAB-AYBAPMKACGICAIAlwsAIYkIAgCXCwAhjAgCAJQLACGNCAIAlAsAIQMAAACTAQAgAQAAlAEAMAIAAJUBACADAAAAkwEAIAEAAJQBADACAACVAQAgEFAAAM4LACBRAADPCwAgUgAA0AsAIKwGAADNCwAwrQYAAJgBABCuBgAAzQsAMK8GAgCUCwAhtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdUGAQDzCgAh3AYBAPIKACH4BgEA8woAIYsIIAD0CgAhAQAAAJgBACAOTgAA_AsAIE8AAP0LACCsBgAA-wsAMK0GAACaAQAQrgYAAPsLADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIfgGAQDzCgAhhwgCAJQLACGICAIAlAsAIYkIAgCXCwAhiggBAPMKACEFTgAAoxUAIE8AAKQVACD4BgAAwwwAIIkIAADDDAAgiggAAMMMACAOTgAA_AsAIE8AAP0LACCsBgAA-wsAMK0GAACaAQAQrgYAAPsLADCvBgIAAAABuAZAAPYKACG5BkAA9goAIcUGAQAAAAH4BgEA8woAIYcIAgCUCwAhiAgCAJQLACGJCAIAlwsAIYoIAQDzCgAhAwAAAJoBACABAACbAQAwAgAAnAEAIAEAAACDAQAgAwAAAJMBACABAACUAQAwAgAAlQEAIBJNAADPCwAgTgAA-gsAIKwGAAD5CwAwrQYAAKABABCuBgAA-QsAMK8GAgCUCwAhtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIcoGIAD0CgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhiAgCAJcLACGOCAIAlAsAIY8IAgCUCwAhBk0AAOcTACBOAACjFQAgyAYAAMMMACDVBgAAwwwAIPgGAADDDAAgiAgAAMMMACASTQAAzwsAIE4AAPoLACCsBgAA-QsAMK0GAACgAQAQrgYAAPkLADCvBgIAAAABtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAAAAAcgGAQDzCgAhygYgAPQKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACGICAIAlwsAIY4IAgCUCwAhjwgCAJQLACEDAAAAoAEAIAEAAKEBADACAACiAQAgAQAAAJoBACABAAAAkwEAIAEAAACgAQAgAQAAAJMBACABAAAAgwEAIAEAAACYAQAgAwAAAJoBACABAACbAQAwAgAAnAEAIAMAAAB9ACABAAB-ADACAAB_ACABAAAAgwEAIAEAAACDAQAgAQAAAJMBACABAAAAmgEAIAEAAAB9ACADAAAAdQAgAQAAdgAwAgAAdwAgAQAAAHUAIAMAAAB1ACABAAB2ADACAAB3ACABAAAAdQAgAwAAAHUAIAEAAHYAMAIAAHcAIAEAAAB1ACADAAAAdQAgAQAAdgAwAgAAdwAgAQAAAHUAIAMAAAB1ACABAAB2ADACAAB3ACABAAAAdQAgAQAAAHEAIAEAAAA7ACABAAAARgAgAQAAAEoAIAEAAABOACABAAAARgAgAQAAAE4AIAEAAAA7ACABAAAAOwAgAQAAADsAIAEAAAB5ACABAAAAfQAgAQAAAHUAIAMAAAB9ACABAAB-ADACAAB_ACABAAAAUgAgAQAAAHEAIAEAAAB9ACAMOQAA_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_xQAIGQAAP4UACCvBgIAAAABtQYgAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHcBgEAAAAB-AYBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDAQsAAOECACAOrwYCAAAAAbUGIAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAAB3AYBAAAAAfgGAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHAwELAADjAgAwAQsAAOMCADAROQAA3BQAIFUAAN4UACBkAADdFAAgrwYCAMoMACG1BiAAzAwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh3AYBAMkMACH4BgEAywwAIbkHAQDLDAAhugcBAMsMACG7BwEAywwAIb0HAAD6DL0HI74HAQDLDAAhwAcAAPsMwAcjAgAAANoCACALAADmAgAgDq8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdwGAQDJDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHIwIAAABuACALAADoAgAgAgAAAG4AIAsAAOgCACADAAAA2gIAIBIAAOECACATAADmAgAgAQAAANoCACABAAAAbgAgDQUAANcUACAYAADaFAAgGQAA2RQAICoAANgUACArAADbFAAgyAYAAMMMACD4BgAAwwwAILkHAADDDAAgugcAAMMMACC7BwAAwwwAIL0HAADDDAAgvgcAAMMMACDABwAAwwwAIBGsBgAA5QsAMK0GAADvAgAQrgYAAOULADCvBgIA3goAIbUGIADgCgAhuAZAAOIKACG5BkAA4goAIcUGAQDdCgAhyAYBAN8KACHcBgEA3QoAIfgGAQDfCgAhuQcBAN8KACG6BwEA3woAIbsHAQDfCgAhvQcAALoLvQcjvgcBAN8KACHABwAAuwvAByMDAAAAbgAgAQAA7gIAMBcAAO8CACADAAAAbgAgAQAA3QIAMAIAANoCACAMOQAA_goAIKwGAADkCwAwrQYAAMwBABCuBgAA5AsAMK8GAgAAAAG4BkAA9goAIbkGQAD2CgAhxQYBAAAAAcgGAQDzCgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhAQAAAPICACABAAAA8gIAIAQ5AADQEAAgyAYAAMMMACDVBgAAwwwAIPgGAADDDAAgAwAAAMwBACABAAD1AgAwAgAA8gIAIAMAAADMAQAgAQAA9QIAMAIAAPICACADAAAAzAEAIAEAAPUCADACAADyAgAgCTkAANYUACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAdUGAQAAAAHcBgEAAAAB-AYBAAAAAQELAAD5AgAgCK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAAB1QYBAAAAAdwGAQAAAAH4BgEAAAABAQsAAPsCADABCwAA-wIAMAk5AADMFAAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdUGAQDLDAAh3AYBAMkMACH4BgEAywwAIQIAAADyAgAgCwAA_gIAIAivBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhAgAAAMwBACALAACAAwAgAgAAAMwBACALAACAAwAgAwAAAPICACASAAD5AgAgEwAA_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-gy9ByO-BwEAywwAIcAHAAD7DMAHI8IHAQDLDAAhmAgBAMsMACGZCCAAzAwAIZoIIADMDAAhmwggAMwMACECAAAA3wEAIAsAAMcDACAVrwYCAMoMACG1BiAAzAwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAhygYgAMwMACHcBgEAyQwAIeAGAgDKDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-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_QMAIAIAAADxAwAgCwAA_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-gcBAAAAAfsHAgAAAAH8BwIAAAAB_QcBAAAAAf4HAQAAAAH_BwEAAAABgAgCAAAAAYEICAAAAAGCCAgAAAABgwggAAAAAYQIIAAAAAGFCAIAAAABhggBAAAAAQELAAC1BQAgFq8GAgAAAAGxBgEAAAABtwZAAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHcBgEAAAAB-QcBAAAAAfoHAQAAAAH7BwIAAAAB_AcCAAAAAf0HAQAAAAH-BwEAAAAB_wcBAAAAAYAIAgAAAAGBCAgAAAABgggIAAAAAYMIIAAAAAGECCAAAAABhQgCAAAAAYYIAQAAAAEBCwAAtwUAMAELAAC3BQAwAQAAAI8BACAdRgAA4RIAIEcAAOISACBLAADjEgAgTAAA5BIAIFEAAOUSACBUAADmEgAgVQAA5xIAIK8GAgDKDAAhsQYBAMsMACG3BkAAzQwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh3AYBAMkMACH5BwEAywwAIfoHAQDLDAAh-wcCAMoMACH8BwIAgA0AIf0HAQDLDAAh_gcBAMsMACH_BwEAywwAIYAIAgCADQAhgQgIAP0MACGCCAgA_QwAIYMIIADMDAAhhAggAMwMACGFCAIAygwAIYYIAQDLDAAhAgAAAIUBACALAAC7BQAgFq8GAgDKDAAhsQYBAMsMACG3BkAAzQwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh3AYBAMkMACH5BwEAywwAIfoHAQDLDAAh-wcCAMoMACH8BwIAgA0AIf0HAQDLDAAh_gcBAMsMACH_BwEAywwAIYAIAgCADQAhgQgIAP0MACGCCAgA_QwAIYMIIADMDAAhhAggAMwMACGFCAIAygwAIYYIAQDLDAAhAgAAAIMBACALAAC9BQAgAgAAAIMBACALAAC9BQAgAQAAAI8BACADAAAAhQEAIBIAALUFACATAAC7BQAgAQAAAIUBACABAAAAgwEAIBAFAADcEgAgGAAA3xIAIBkAAN4SACAqAADdEgAgKwAA4BIAILEGAADDDAAgtwYAAMMMACDIBgAAwwwAIPkHAADDDAAg-gcAAMMMACD8BwAAwwwAIP0HAADDDAAg_gcAAMMMACD_BwAAwwwAIIAIAADDDAAghggAAMMMACAZrAYAAMoLADCtBgAAxQUAEK4GAADKCwAwrwYCAN4KACGxBgEA3woAIbcGQADhCgAhuAZAAOIKACG5BkAA4goAIcUGAQDdCgAhyAYBAN8KACHcBgEA3QoAIfkHAQDfCgAh-gcBAN8KACH7BwIA3goAIfwHAgCMCwAh_QcBAN8KACH-BwEA3woAIf8HAQDfCgAhgAgCAIwLACGBCAgAnQsAIYIICACdCwAhgwggAOAKACGECCAA4AoAIYUIAgDeCgAhhggBAN8KACEDAAAAgwEAIAEAAMQFADAXAADFBQAgAwAAAIMBACABAACEAQAwAgAAhQEAIAEAAAB_ACABAAAAfwAgAwAAAH0AIAEAAH4AMAIAAH8AIAMAAAB9ACABAAB-ADACAAB_ACADAAAAfQAgAQAAfgAwAgAAfwAgKzYAAKINACBEAACjDgAgVgAAow0AIFcAAKQNACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAdQGAgAAAAHVBgEAAAAB7AYgAAAAAe0GIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAG3BwIAAAAB3wcIAAAAAeAHAQAAAAHhBwEAAAAB4gcCAAAAAeMHAQAAAAHkBwEAAAAB5QcIAAAAAeYHCAAAAAHnBwgAAAAB6AcIAAAAAekHCAAAAAHqBwgAAAAB6wcCAAAAAewHAgAAAAHtBwEAAAAB7gcIAAAAAe8HIAAAAAHwByAAAAAB8QcgAAAAAfIHIAAAAAHzByAAAAAB9AcgAAAAAfUHAgAAAAH2BwIAAAAB9wcBAAAAAfgHQAAAAAEBCwAAzQUAICevBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAdQGAgAAAAHVBgEAAAAB7AYgAAAAAe0GIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAG3BwIAAAAB3wcIAAAAAeAHAQAAAAHhBwEAAAAB4gcCAAAAAeMHAQAAAAHkBwEAAAAB5QcIAAAAAeYHCAAAAAHnBwgAAAAB6AcIAAAAAekHCAAAAAHqBwgAAAAB6wcCAAAAAewHAgAAAAHtBwEAAAAB7gcIAAAAAe8HIAAAAAHwByAAAAAB8QcgAAAAAfIHIAAAAAHzByAAAAAB9AcgAAAAAfUHAgAAAAH2BwIAAAAB9wcBAAAAAfgHQAAAAAEBCwAAzwUAMAELAADPBQAwAQAAAHUAIAEAAABSACArNgAAng0AIEQAAKEOACBWAACfDQAgVwAAoA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHUBgIAgA0AIdUGAQDLDAAh7AYgAMwMACHtBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIbcHAgCADQAh3wcIAP0MACHgBwEAywwAIeEHAQDLDAAh4gcCAIANACHjBwEAywwAIeQHAQDLDAAh5QcIAP0MACHmBwgA_QwAIecHCAD9DAAh6AcIAP0MACHpBwgA_QwAIeoHCAD9DAAh6wcCAMoMACHsBwIAygwAIe0HAQDLDAAh7gcIAJwNACHvByAAzAwAIfAHIADMDAAh8QcgAMwMACHyByAAzAwAIfMHIADMDAAh9AcgAMwMACH1BwIAygwAIfYHAgDKDAAh9wcBAMkMACH4B0AAzQwAIQIAAAB_ACALAADUBQAgJ68GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHUBgIAgA0AIdUGAQDLDAAh7AYgAMwMACHtBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIbcHAgCADQAh3wcIAP0MACHgBwEAywwAIeEHAQDLDAAh4gcCAIANACHjBwEAywwAIeQHAQDLDAAh5QcIAP0MACHmBwgA_QwAIecHCAD9DAAh6AcIAP0MACHpBwgA_QwAIeoHCAD9DAAh6wcCAMoMACHsBwIAygwAIe0HAQDLDAAh7gcIAJwNACHvByAAzAwAIfAHIADMDAAh8QcgAMwMACHyByAAzAwAIfMHIADMDAAh9AcgAMwMACH1BwIAygwAIfYHAgDKDAAh9wcBAMkMACH4B0AAzQwAIQIAAAB9ACALAADWBQAgAgAAAH0AIAsAANYFACABAAAAdQAgAQAAAFIAIAMAAAB_ACASAADNBQAgEwAA1AUAIAEAAAB_ACABAAAAfQAgFAUAANcSACAYAADaEgAgGQAA2RIAICoAANgSACArAADbEgAgyAYAAMMMACDUBgAAwwwAINUGAADDDAAg-AYAAMMMACD5BgAAwwwAIPoGAADDDAAgtwcAAMMMACDgBwAAwwwAIOEHAADDDAAg4gcAAMMMACDjBwAAwwwAIOQHAADDDAAg7QcAAMMMACDuBwAAwwwAIPgHAADDDAAgKqwGAADJCwAwrQYAAN8FABCuBgAAyQsAMK8GAgDeCgAhuAZAAOIKACG5BkAA4goAIcUGAQDdCgAhyAYBAN8KACHUBgIAjAsAIdUGAQDfCgAh7AYgAOAKACHtBiAA4AoAIfgGAQDfCgAh-QYBAN8KACH6BkAA4QoAIbcHAgCMCwAh3wcIAJ0LACHgBwEA3woAIeEHAQDfCgAh4gcCAIwLACHjBwEA3woAIeQHAQDfCgAh5QcIAJ0LACHmBwgAnQsAIecHCACdCwAh6AcIAJ0LACHpBwgAnQsAIeoHCACdCwAh6wcCAN4KACHsBwIA3goAIe0HAQDfCgAh7gcIAPkKACHvByAA4AoAIfAHIADgCgAh8QcgAOAKACHyByAA4AoAIfMHIADgCgAh9AcgAOAKACH1BwIA3goAIfYHAgDeCgAh9wcBAN0KACH4B0AA4QoAIQMAAAB9ACABAADeBQAwFwAA3wUAIAMAAAB9ACABAAB-ADACAAB_ACABAAAAdwAgAQAAAHcAIAMAAAB1ACABAAB2ADACAAB3ACADAAAAdQAgAQAAdgAwAgAAdwAgAwAAAHUAIAEAAHYAMAIAAHcAIEUzAADLDQAgNAAAug0AIDcAAMANACBAAAC7DQAgQgAAtw0AIEUAALENACBYAACyDQAgWgAAsw0AIFsAALQNACBcAAC1DQAgXQAAtg0AIF4AALgNACBfAAC5DQAgYAAAvA0AIGEAAL0NACBiAAC-DQAgYwAAvw0AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHVBgEAAAAB4AYCAAAAAecGIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAGMBwIAAAABjwcCAAAAAZ8HIAAAAAGgByAAAAABoQcgAAAAAaIHIAAAAAGjByAAAAABpAcgAAAAAbgHAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8EHAQAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAccHAQAAAAHIByAAAAAByQcgAAAAAcoHCAAAAAHMBwAAAMwHAs0HQAAAAAHPBwAAAM8HAtAHAQAAAAHRByAAAAAB0gcgAAAAAdMHIAAAAAHUBwEAAAAB1QcCAAAAAdYHAgAAAAHXBwIAAAAB2AcCAAAAAdkHAgAAAAHaBwIAAAAB2wcCAAAAAdwHAgAAAAHdBwIAAAAB3gdAAAAAAQELAADnBQAgNK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHVBgEAAAAB4AYCAAAAAecGIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAGMBwIAAAABjwcCAAAAAZ8HIAAAAAGgByAAAAABoQcgAAAAAaIHIAAAAAGjByAAAAABpAcgAAAAAbgHAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8EHAQAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAccHAQAAAAHIByAAAAAByQcgAAAAAcoHCAAAAAHMBwAAAMwHAs0HQAAAAAHPBwAAAM8HAtAHAQAAAAHRByAAAAAB0gcgAAAAAdMHIAAAAAHUBwEAAAAB1QcCAAAAAdYHAgAAAAHXBwIAAAAB2AcCAAAAAdkHAgAAAAHaBwIAAAAB2wcCAAAAAdwHAgAAAAHdBwIAAAAB3gdAAAAAAQELAADpBQAwAQsAAOkFADABAAAAcQAgAQAAADsAIAEAAABGACABAAAASgAgAQAAAE4AIAEAAABGACABAAAATgAgAQAAADsAIAEAAAA7ACABAAAAOwAgRTMAAMkNACA0AACLDQAgNwAAkQ0AIEAAAIwNACBCAACIDQAgRQAAgg0AIFgAAIMNACBaAACEDQAgWwAAhQ0AIFwAAIYNACBdAACHDQAgXgAAiQ0AIF8AAIoNACBgAACNDQAgYQAAjg0AIGIAAI8NACBjAACQDQAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIeAGAgCADQAh5wYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACGMBwIAgA0AIY8HAgDKDAAhnwcgAMwMACGgByAAzAwAIaEHIADMDAAhogcgAMwMACGjByAAzAwAIaQHIADMDAAhuAcBAMkMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8EHAQDLDAAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAhxwcBAMsMACHIByAAzAwAIckHIADMDAAhygcIAP0MACHMBwAA_gzMByLNB0AAzQwAIc8HAAD_DM8HItAHAQDLDAAh0QcgAMwMACHSByAAzAwAIdMHIADMDAAh1AcBAMsMACHVBwIAgA0AIdYHAgDKDAAh1wcCAMoMACHYBwIAygwAIdkHAgDKDAAh2gcCAIANACHbBwIAgA0AIdwHAgCADQAh3QcCAIANACHeB0AAzQwAIQIAAAB3ACALAAD2BQAgNK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACECAAAAdQAgCwAA-AUAIAIAAAB1ACALAAD4BQAgAQAAAHEAIAEAAAA7ACABAAAARgAgAQAAAEoAIAEAAABOACABAAAARgAgAQAAAE4AIAEAAAA7ACABAAAAOwAgAQAAADsAIAMAAAB3ACASAADnBQAgEwAA9gUAIAEAAAB3ACABAAAAdQAgIAUAANISACAYAADVEgAgGQAA1BIAICoAANMSACArAADWEgAg1QYAAMMMACDgBgAAwwwAIPgGAADDDAAg-QYAAMMMACD6BgAAwwwAIIwHAADDDAAguQcAAMMMACC6BwAAwwwAILsHAADDDAAgvQcAAMMMACC-BwAAwwwAIMAHAADDDAAgwQcAAMMMACDCBwAAwwwAIMMHAADDDAAgxQcAAMMMACDGBwAAwwwAIMcHAADDDAAgzQcAAMMMACDQBwAAwwwAINQHAADDDAAg1QcAAMMMACDaBwAAwwwAINsHAADDDAAg3AcAAMMMACDdBwAAwwwAIN4HAADDDAAgN6wGAAC5CwAwrQYAAIkGABCuBgAAuQsAMK8GAgDeCgAhuAZAAOIKACG5BkAA4goAIcUGAQDdCgAh1QYBAN8KACHgBgIAjAsAIecGIADgCgAh-AYBAN8KACH5BgEA3woAIfoGQADhCgAhjAcCAIwLACGPBwIA3goAIZ8HIADgCgAhoAcgAOAKACGhByAA4AoAIaIHIADgCgAhowcgAOAKACGkByAA4AoAIbgHAQDdCgAhuQcBAN8KACG6BwEA3woAIbsHAQDfCgAhvQcAALoLvQcjvgcBAN8KACHABwAAuwvAByPBBwEA3woAIcIHAQDfCgAhwwcBAN8KACHFBwAAvAvFByPGBwEA3woAIccHAQDfCgAhyAcgAOAKACHJByAA4AoAIcoHCACdCwAhzAcAAL0LzAcizQdAAOEKACHPBwAAvgvPByLQBwEA3woAIdEHIADgCgAh0gcgAOAKACHTByAA4AoAIdQHAQDfCgAh1QcCAIwLACHWBwIA3goAIdcHAgDeCgAh2AcCAN4KACHZBwIA3goAIdoHAgCMCwAh2wcCAIwLACHcBwIAjAsAId0HAgCMCwAh3gdAAOEKACEDAAAAdQAgAQAAiAYAMBcAAIkGACADAAAAdQAgAQAAdgAwAgAAdwAgAQAAAHsAIAEAAAB7ACADAAAAeQAgAQAAegAwAgAAewAgAwAAAHkAIAEAAHoAMAIAAHsAIAMAAAB5ACABAAB6ADACAAB7ACATQwAArw0AIEQAAO8MACBFAQAAAAGvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB1QYBAAAAAfgGAQAAAAGuBwEAAAABrwcBAAAAAbAHIAAAAAGxByAAAAABsgcgAAAAAbMHIAAAAAG0ByAAAAABtQcgAAAAAbYHIAAAAAG3BwIAAAABAQsAAJEGACARRQEAAAABrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAH4BgEAAAABrgcBAAAAAa8HAQAAAAGwByAAAAABsQcgAAAAAbIHIAAAAAGzByAAAAABtAcgAAAAAbUHIAAAAAG2ByAAAAABtwcCAAAAAQELAACTBgAwAQsAAJMGADATQwAArQ0AIEQAAO0MACBFAQDJDAAhrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIfgGAQDLDAAhrgcBAMkMACGvBwEAyQwAIbAHIADMDAAhsQcgAMwMACGyByAAzAwAIbMHIADMDAAhtAcgAMwMACG1ByAAzAwAIbYHIADMDAAhtwcCAMoMA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_BwAwClkAAOoRACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAhygYgAMwMACHVBgEAywwAIdwGAQDJDAAh-AYBAMsMACECAAAAtQcAIAsAAMIHACAJrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIcoGIADMDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhAgAAALgHACALAADEBwAgAgAAALgHACALAADEBwAgAwAAALUHACASAAC9BwAgEwAAwgcAIAEAAAC1BwAgAQAAALgHACAIBQAA5REAIBgAAOgRACAZAADnEQAgKgAA5hEAICsAAOkRACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACAMrAYAAKoLADCtBgAAywcAEK4GAACqCwAwrwYCAN4KACG4BkAA4goAIbkGQADiCgAhxQYBAN0KACHIBgEA3woAIcoGIADgCgAh1QYBAN8KACHcBgEA3QoAIfgGAQDfCgAhAwAAALgHACABAADKBwAwFwAAywcAIAMAAAC4BwAgAQAAuQcAMAIAALUHACAErAYAAKkLADCtBgAA0QcAEK4GAACpCwAwrwYBAAAAAQEAAADOBwAgAQAAAM4HACAErAYAAKkLADCtBgAA0QcAEK4GAACpCwAwrwYBAPIKACEAAwAAANEHACABAADSBwAwAgAAzgcAIAMAAADRBwAgAQAA0gcAMAIAAM4HACADAAAA0QcAIAEAANIHADACAADOBwAgAa8GAQAAAAEBCwAA1gcAIAGvBgEAAAABAQsAANgHADABCwAA2AcAMAGvBgEAyQwAIQIAAADOBwAgCwAA2wcAIAGvBgEAyQwAIQIAAADRBwAgCwAA3QcAIAIAAADRBwAgCwAA3QcAIAMAAADOBwAgEgAA1gcAIBMAANsHACABAAAAzgcAIAEAAADRBwAgAwUAAOIRACAYAADkEQAgGQAA4xEAIASsBgAAqAsAMK0GAADkBwAQrgYAAKgLADCvBgEA3QoAIQMAAADRBwAgAQAA4wcAMBcAAOQHACADAAAA0QcAIAEAANIHADACAADOBwAgDDkAAP4KACCsBgAApwsAMK0GAABdABCuBgAApwsAMK8GAgAAAAG4BkAA9goAIbkGQAD2CgAhxQYBAAAAAdwGAQDyCgAh-AYBAPMKACGdBwIAlwsAIZ4HAQDzCgAhAQAAAOcHACABAAAA5wcAIAQ5AADQEAAg-AYAAMMMACCdBwAAwwwAIJ4HAADDDAAgAwAAAF0AIAEAAOoHADACAADnBwAgAwAAAF0AIAEAAOoHADACAADnBwAgAwAAAF0AIAEAAOoHADACAADnBwAgCTkAAOERACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAfgGAQAAAAGdBwIAAAABngcBAAAAAQELAADuBwAgCK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHcBgEAAAAB-AYBAAAAAZ0HAgAAAAGeBwEAAAABAQsAAPAHADABCwAA8AcAMAk5AADXEQAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAIfgGAQDLDAAhnQcCAIANACGeBwEAywwAIQIAAADnBwAgCwAA8wcAIAivBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdwGAQDJDAAh-AYBAMsMACGdBwIAgA0AIZ4HAQDLDAAhAgAAAF0AIAsAAPUHACACAAAAXQAgCwAA9QcAIAMAAADnBwAgEgAA7gcAIBMAAPMHACABAAAA5wcAIAEAAABdACAIBQAA0hEAIBgAANURACAZAADUEQAgKgAA0xEAICsAANYRACD4BgAAwwwAIJ0HAADDDAAgngcAAMMMACALrAYAAKYLADCtBgAA_AcAEK4GAACmCwAwrwYCAN4KACG4BkAA4goAIbkGQADiCgAhxQYBAN0KACHcBgEA3QoAIfgGAQDfCgAhnQcCAIwLACGeBwEA3woAIQMAAABdACABAAD7BwAwFwAA_AcAIAMAAABdACABAADqBwAwAgAA5wcAIA05AAD-CgAgrAYAAKULADCtBgAAgggAEK4GAAClCwAwrwYCAAAAAbUGIAD0CgAhuAZAAPYKACG5BkAA9goAIcUGAQAAAAHIBgEA8woAIdUGAQDzCgAh3AYBAPIKACH4BgEA8woAIQEAAAD_BwAgAQAAAP8HACANOQAA_goAIKwGAAClCwAwrQYAAIIIABCuBgAApQsAMK8GAgCUCwAhtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdUGAQDzCgAh3AYBAPIKACH4BgEA8woAIQQ5AADQEAAgyAYAAMMMACDVBgAAwwwAIPgGAADDDAAgAwAAAIIIACABAACDCAAwAgAA_wcAIAMAAACCCAAgAQAAgwgAMAIAAP8HACADAAAAgggAIAEAAIMIADACAAD_BwAgCjkAANERACCvBgIAAAABtQYgAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHVBgEAAAAB3AYBAAAAAfgGAQAAAAEBCwAAhwgAIAmvBgIAAAABtQYgAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHVBgEAAAAB3AYBAAAAAfgGAQAAAAEBCwAAiQgAMAELAACJCAAwCjkAAMcRACCvBgIAygwAIbUGIADMDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHVBgEAywwAIdwGAQDJDAAh-AYBAMsMACECAAAA_wcAIAsAAIwIACAJrwYCAMoMACG1BiAAzAwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhAgAAAIIIACALAACOCAAgAgAAAIIIACALAACOCAAgAwAAAP8HACASAACHCAAgEwAAjAgAIAEAAAD_BwAgAQAAAIIIACAIBQAAwhEAIBgAAMURACAZAADEEQAgKgAAwxEAICsAAMYRACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACAMrAYAAKQLADCtBgAAlQgAEK4GAACkCwAwrwYCAN4KACG1BiAA4AoAIbgGQADiCgAhuQZAAOIKACHFBgEA3QoAIcgGAQDfCgAh1QYBAN8KACHcBgEA3QoAIfgGAQDfCgAhAwAAAIIIACABAACUCAAwFwAAlQgAIAMAAACCCAAgAQAAgwgAMAIAAP8HACAROQAA_goAIKwGAACjCwAwrQYAAGEAEK4GAACjCwAwrwYCAAAAAbUGIAD0CgAhuAZAAPYKACG5BkAA9goAIcUGAQAAAAHIBgEA8woAIcoGIAD0CgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhmgcgAPQKACGbByAA9AoAIZwHIAD0CgAhAQAAAJgIACABAAAAmAgAIAQ5AADQEAAgyAYAAMMMACDVBgAAwwwAIPgGAADDDAAgAwAAAGEAIAEAAJsIADACAACYCAAgAwAAAGEAIAEAAJsIADACAACYCAAgAwAAAGEAIAEAAJsIADACAACYCAAgDjkAAMERACCvBgIAAAABtQYgAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHKBiAAAAAB1QYBAAAAAdwGAQAAAAH4BgEAAAABmgcgAAAAAZsHIAAAAAGcByAAAAABAQsAAJ8IACANrwYCAAAAAbUGIAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAABygYgAAAAAdUGAQAAAAHcBgEAAAAB-AYBAAAAAZoHIAAAAAGbByAAAAABnAcgAAAAAQELAAChCAAwAQsAAKEIADAOOQAAtxEAIK8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIcoGIADMDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhmgcgAMwMACGbByAAzAwAIZwHIADMDAAhAgAAAJgIACALAACkCAAgDa8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIcoGIADMDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhmgcgAMwMACGbByAAzAwAIZwHIADMDAAhAgAAAGEAIAsAAKYIACACAAAAYQAgCwAApggAIAMAAACYCAAgEgAAnwgAIBMAAKQIACABAAAAmAgAIAEAAABhACAIBQAAshEAIBgAALURACAZAAC0EQAgKgAAsxEAICsAALYRACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACAQrAYAAKILADCtBgAArQgAEK4GAACiCwAwrwYCAN4KACG1BiAA4AoAIbgGQADiCgAhuQZAAOIKACHFBgEA3QoAIcgGAQDfCgAhygYgAOAKACHVBgEA3woAIdwGAQDdCgAh-AYBAN8KACGaByAA4AoAIZsHIADgCgAhnAcgAOAKACEDAAAAYQAgAQAArAgAMBcAAK0IACADAAAAYQAgAQAAmwgAMAIAAJgIACABAAAAVAAgAQAAAFQAIAMAAABSACABAABTADACAABUACADAAAAUgAgAQAAUwAwAgAAVAAgAwAAAFIAIAEAAFMAMAIAAFQAICwzAQAAAAE0AAC8DgAgOAAAtQ4AIDoAALYOACA7AAC3DgAgPAAAuA4AID0AALkOACA-AAC6DgAgPwAAuw4AIEAAAL0OACBBAADjDgAgQgAAvg4AIEUBAAAAAVgAAMAOACBdAQAAAAFlAAC_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_QoAIfUGAgCXCwAh9gYCAJcLACH3BgIAlwsAIfgGAQDzCgAh-QYBAPMKACH6BkAA9QoAIQMAAADcCAAgAQAA3QgAMAIAAN4IACAPigUAAJALACCsBgAAlQsAMK0GAADgCAAQrgYAAJULADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAhygYgAPQKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACGCBwEA8woAIYMHIAD0CgAhAQAAAOAIACASigUAAJALACCsBgAAkgsAMK0GAADiCAAQrgYAAJILADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAh_AYBAPMKACH9BgEA8woAIf4GAQDzCgAh_wYBAPMKACGAByAA9AoAIYEHIAD0CgAhAQAAAOIIACADAAAA3AgAIAEAAN0IADACAADeCAAgAQAAANwIACAHigUAAJALACCsBgAAjwsAMK0GAADmCAAQrgYAAI8LADCvBgIAlAsAIfcGAQDzCgAh-wYBAPMKACEBAAAA5ggAIAMAAADcCAAgAQAA3QgAMAIAAN4IACABAAAA3AgAIAEAAADcCAAgAQAAANoIACAFigUAAIkRACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACCCBwAAwwwAIAMAAADgCAAgAQAA7AgAMAIAANoIACADAAAA4AgAIAEAAOwIADACAADaCAAgAwAAAOAIACABAADsCAAwAgAA2ggAIAyKBQAAqREAIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAABygYgAAAAAdUGAQAAAAHcBgEAAAAB-AYBAAAAAYIHAQAAAAGDByAAAAABAQsAAPAIACALrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHKBiAAAAAB1QYBAAAAAdwGAQAAAAH4BgEAAAABggcBAAAAAYMHIAAAAAEBCwAA8ggAMAELAADyCAAwDIoFAACfEQAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIcoGIADMDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhggcBAMsMACGDByAAzAwAIQIAAADaCAAgCwAA9QgAIAuvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAhygYgAMwMACHVBgEAywwAIdwGAQDJDAAh-AYBAMsMACGCBwEAywwAIYMHIADMDAAhAgAAAOAIACALAAD3CAAgAgAAAOAIACALAAD3CAAgAwAAANoIACASAADwCAAgEwAA9QgAIAEAAADaCAAgAQAAAOAIACAJBQAAmhEAIBgAAJ0RACAZAACcEQAgKgAAmxEAICsAAJ4RACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACCCBwAAwwwAIA6sBgAAkwsAMK0GAAD-CAAQrgYAAJMLADCvBgIA3goAIbgGQADiCgAhuQZAAOIKACHFBgEA3QoAIcgGAQDfCgAhygYgAOAKACHVBgEA3woAIdwGAQDdCgAh-AYBAN8KACGCBwEA3woAIYMHIADgCgAhAwAAAOAIACABAAD9CAAwFwAA_ggAIAMAAADgCAAgAQAA7AgAMAIAANoIACASigUAAJALACCsBgAAkgsAMK0GAADiCAAQrgYAAJILADCvBgIAAAABuAZAAPYKACG5BkAA9goAIcUGAQAAAAHIBgEA8woAIdUGAQDzCgAh3AYBAPIKACH4BgEA8woAIfwGAQDzCgAh_QYBAPMKACH-BgEA8woAIf8GAQDzCgAhgAcgAPQKACGBByAA9AoAIQEAAACBCQAgAQAAAIEJACAIigUAAIkRACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACD8BgAAwwwAIP0GAADDDAAg_gYAAMMMACD_BgAAwwwAIAMAAADiCAAgAQAAhAkAMAIAAIEJACADAAAA4ggAIAEAAIQJADACAACBCQAgAwAAAOIIACABAACECQAwAgAAgQkAIA-KBQAAmREAIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAAB1QYBAAAAAdwGAQAAAAH4BgEAAAAB_AYBAAAAAf0GAQAAAAH-BgEAAAAB_wYBAAAAAYAHIAAAAAGBByAAAAABAQsAAIgJACAOrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHVBgEAAAAB3AYBAAAAAfgGAQAAAAH8BgEAAAAB_QYBAAAAAf4GAQAAAAH_BgEAAAABgAcgAAAAAYEHIAAAAAEBCwAAigkAMAELAACKCQAwD4oFAACPEQAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdUGAQDLDAAh3AYBAMkMACH4BgEAywwAIfwGAQDLDAAh_QYBAMsMACH-BgEAywwAIf8GAQDLDAAhgAcgAMwMACGBByAAzAwAIQIAAACBCQAgCwAAjQkAIA6vBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAh_AYBAMsMACH9BgEAywwAIf4GAQDLDAAh_wYBAMsMACGAByAAzAwAIYEHIADMDAAhAgAAAOIIACALAACPCQAgAgAAAOIIACALAACPCQAgAwAAAIEJACASAACICQAgEwAAjQkAIAEAAACBCQAgAQAAAOIIACAMBQAAihEAIBgAAI0RACAZAACMEQAgKgAAixEAICsAAI4RACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACD8BgAAwwwAIP0GAADDDAAg_gYAAMMMACD_BgAAwwwAIBGsBgAAkQsAMK0GAACWCQAQrgYAAJELADCvBgIA3goAIbgGQADiCgAhuQZAAOIKACHFBgEA3QoAIcgGAQDfCgAh1QYBAN8KACHcBgEA3QoAIfgGAQDfCgAh_AYBAN8KACH9BgEA3woAIf4GAQDfCgAh_wYBAN8KACGAByAA4AoAIYEHIADgCgAhAwAAAOIIACABAACVCQAwFwAAlgkAIAMAAADiCAAgAQAAhAkAMAIAAIEJACAHigUAAJALACCsBgAAjwsAMK0GAADmCAAQrgYAAI8LADCvBgIAAAAB9wYBAPMKACH7BgEA8woAIQEAAACZCQAgAQAAAJkJACADigUAAIkRACD3BgAAwwwAIPsGAADDDAAgAwAAAOYIACABAACcCQAwAgAAmQkAIAMAAADmCAAgAQAAnAkAMAIAAJkJACADAAAA5ggAIAEAAJwJADACAACZCQAgBIoFAACIEQAgrwYCAAAAAfcGAQAAAAH7BgEAAAABAQsAAKAJACADrwYCAAAAAfcGAQAAAAH7BgEAAAABAQsAAKIJADABCwAAogkAMASKBQAA-xAAIK8GAgDKDAAh9wYBAMsMACH7BgEAywwAIQIAAACZCQAgCwAApQkAIAOvBgIAygwAIfcGAQDLDAAh-wYBAMsMACECAAAA5ggAIAsAAKcJACACAAAA5ggAIAsAAKcJACADAAAAmQkAIBIAAKAJACATAAClCQAgAQAAAJkJACABAAAA5ggAIAcFAAD2EAAgGAAA-RAAIBkAAPgQACAqAAD3EAAgKwAA-hAAIPcGAADDDAAg-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_woAMK0GAACpCgAQrgYAAP8KADCvBgEA3QoAIbgGQADiCgAhuQZAAOIKACHOBgAAgAsAIM8GAACACwAg0AYBAN0KACHRBgAAgAsAINIGAAGBCwAh0wYBAN0KACHUBgEA3QoAIdUGAQDfCgAhAwAAAFYAIAEAAKgKADAXAACpCgAgAwAAAFYAIAEAAFcAMAIAAFgAIA8zAQDzCgAhNwEA8goAITkAAP4KACCsBgAA_AoAMK0GAABlABCuBgAA_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_BgEAAAABwAYBAAAAAcEGAQDrCgAhwgYBAAAAAcMGAQAAAAHEBgEAAAABBQUAAOQKACAYAADqCgAgGQAA6goAILoGIAAAAAHBBiAA6QoAIQsFAADnCgAgGAAA6AoAIBkAAOgKACC6BkAAAAABuwZAAAAABbwGQAAAAAW9BkAAAAABvgZAAAAAAb8GQAAAAAHABkAAAAABwQZAAOYKACELBQAA5AoAIBgAAOUKACAZAADlCgAgugZAAAAAAbsGQAAAAAS8BkAAAAAEvQZAAAAAAb4GQAAAAAG_BkAAAAABwAZAAAAAAcEGQADjCgAhCwUAAOQKACAYAADlCgAgGQAA5QoAILoGQAAAAAG7BkAAAAAEvAZAAAAABL0GQAAAAAG-BkAAAAABvwZAAAAAAcAGQAAAAAHBBkAA4woAIQi6BgIAAAABuwYCAAAABLwGAgAAAAS9BgIAAAABvgYCAAAAAb8GAgAAAAHABgIAAAABwQYCAOQKACEIugZAAAAAAbsGQAAAAAS8BkAAAAAEvQZAAAAAAb4GQAAAAAG_BkAAAAABwAZAAAAAAcEGQADlCgAhCwUAAOcKACAYAADoCgAgGQAA6AoAILoGQAAAAAG7BkAAAAAFvAZAAAAABb0GQAAAAAG-BkAAAAABvwZAAAAAAcAGQAAAAAHBBkAA5goAIQi6BgIAAAABuwYCAAAABbwGAgAAAAW9BgIAAAABvgYCAAAAAb8GAgAAAAHABgIAAAABwQYCAOcKACEIugZAAAAAAbsGQAAAAAW8BkAAAAAFvQZAAAAAAb4GQAAAAAG_BkAAAAABwAZAAAAAAcEGQADoCgAhBQUAAOQKACAYAADqCgAgGQAA6goAILoGIAAAAAHBBiAA6QoAIQK6BiAAAAABwQYgAOoKACEOBQAA5woAIBgAAOwKACAZAADsCgAgugYBAAAAAbsGAQAAAAW8BgEAAAAFvQYBAAAAAb4GAQAAAAG_BgEAAAABwAYBAAAAAcEGAQDrCgAhwgYBAAAAAcMGAQAAAAHEBgEAAAABC7oGAQAAAAG7BgEAAAAFvAYBAAAABb0GAQAAAAG-BgEAAAABvwYBAAAAAcAGAQAAAAHBBgEA7AoAIcIGAQAAAAHDBgEAAAABxAYBAAAAAQ0FAADkCgAgGAAA5AoAIBkAAOQKACAqAADuCgAgKwAA5AoAILoGAgAAAAG7BgIAAAAEvAYCAAAABL0GAgAAAAG-BgIAAAABvwYCAAAAAcAGAgAAAAHBBgIA7QoAIQi6BggAAAABuwYIAAAABLwGCAAAAAS9BggAAAABvgYIAAAAAb8GCAAAAAHABggAAAABwQYIAO4KACEOBQAA5AoAIBgAAPAKACAZAADwCgAgugYBAAAAAbsGAQAAAAS8BgEAAAAEvQYBAAAAAb4GAQAAAAG_BgEAAAABwAYBAAAAAcEGAQDvCgAhwgYBAAAAAcMGAQAAAAHEBgEAAAABC7oGAQAAAAG7BgEAAAAEvAYBAAAABL0GAQAAAAG-BgEAAAABvwYBAAAAAcAGAQAAAAHBBgEA8AoAIcIGAQAAAAHDBgEAAAABxAYBAAAAAQ8wAAD3CgAgrAYAAPEKADCtBgAAQAAQrgYAAPEKADCvBgEA8goAIbAGAgCUCwAhsQYBAPIKACGyBgEA8goAIbMGAQDyCgAhtAYBAPMKACG1BiAA9AoAIbYGAQDzCgAhtwZAAPUKACG4BkAA9goAIbkGQAD2CgAhC7oGAQAAAAG7BgEAAAAEvAYBAAAABL0GAQAAAAG-BgEAAAABvwYBAAAAAcAGAQAAAAHBBgEA8AoAIcIGAQAAAAHDBgEAAAABxAYBAAAAAQu6BgEAAAABuwYBAAAABbwGAQAAAAW9BgEAAAABvgYBAAAAAb8GAQAAAAHABgEAAAABwQYBAOwKACHCBgEAAAABwwYBAAAAAcQGAQAAAAECugYgAAAAAcEGIADqCgAhCLoGQAAAAAG7BkAAAAAFvAZAAAAABb0GQAAAAAG-BkAAAAABvwZAAAAAAcAGQAAAAAHBBkAA6AoAIQi6BkAAAAABuwZAAAAABLwGQAAAAAS9BkAAAAABvgZAAAAAAb8GQAAAAAHABkAAAAABwQZAAOUKACEkLwAArwwAIDEAALAMACAyAACxDAAgMwEA8woAITgAAKEMACA5AAD-CgAgaAAAqwwAIHIAALIMACBzAACyDAAgdAAA_goAIHUAAKwLACB2AACsCwAgdwAArAsAIHgAAKwLACB5AACSDAAgrAYAAK4MADCtBgAAOwAQrgYAAK4MADCvBgEA8goAIbAGAgCXCwAhsgYBAPMKACGzBgEA8woAIbgGQAD2CgAhuQZAAPYKACHGBwEA8goAIZEIIAD0CgAhlAgBAPMKACGkCAEA8woAIaUIAQDyCgAhpggBAPMKACGnCCAA9AoAIagIAQDzCgAhqQgBAPMKACGqCAEA8woAIcEIAAA7ACDCCAAAOwAgDjMBAN8KACE3AQDdCgAhrAYAAPgKADCtBgAAwQoAEK4GAAD4CgAwrwYCAN4KACG4BkAA4goAIbkGQADiCgAhxQYBAN0KACHGBgEA3QoAIccGAQDdCgAhyAYBAN8KACHJBggA-QoAIcoGIADgCgAhDQUAAOcKACAYAAD7CgAgGQAA-woAICoAAPsKACArAAD7CgAgugYIAAAAAbsGCAAAAAW8BggAAAAFvQYIAAAAAb4GCAAAAAG_BggAAAABwAYIAAAAAcEGCAD6CgAhDQUAAOcKACAYAAD7CgAgGQAA-woAICoAAPsKACArAAD7CgAgugYIAAAAAbsGCAAAAAW8BggAAAAFvQYIAAAAAb4GCAAAAAG_BggAAAABwAYIAAAAAcEGCAD6CgAhCLoGCAAAAAG7BggAAAAFvAYIAAAABb0GCAAAAAG-BggAAAABvwYIAAAAAcAGCAAAAAHBBggA-woAIQ8zAQDzCgAhNwEA8goAITkAAP4KACCsBgAA_AoAMK0GAABlABCuBgAA_AoAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhxgYBAPIKACHHBgEA8goAIcgGAQDzCgAhyQYIAP0KACHKBiAA9AoAIQi6BggAAAABuwYIAAAABbwGCAAAAAW9BggAAAABvgYIAAAAAb8GCAAAAAHABggAAAABwQYIAPsKACEDywYAAFIAIMwGAABSACDNBgAAUgAgDqwGAAD_CgAwrQYAAKkKABCuBgAA_woAMK8GAQDdCgAhuAZAAOIKACG5BkAA4goAIc4GAACACwAgzwYAAIALACDQBgEA3QoAIdEGAACACwAg0gYAAYELACHTBgEA3QoAIdQGAQDdCgAh1QYBAN8KACEPBQAA5AoAIBgAAIQLACAZAACECwAgugaAAAAAAb0GgAAAAAG-BoAAAAABvwaAAAAAAcAGgAAAAAHBBoAAAAAB1gYBAAAAAdcGAQAAAAHYBgEAAAAB2QaAAAAAAdoGgAAAAAHbBoAAAAABBwUAAOQKACAYAACDCwAgGQAAgwsAILoGAAEAAAG7BgABAAAEvAYAAQAABMEGAAGCCwAhBwUAAOQKACAYAACDCwAgGQAAgwsAILoGAAEAAAG7BgABAAAEvAYAAQAABMEGAAGCCwAhBLoGAAEAAAG7BgABAAAEvAYAAQAABMEGAAGDCwAhDLoGgAAAAAG9BoAAAAABvgaAAAAAAb8GgAAAAAHABoAAAAABwQaAAAAAAdYGAQAAAAHXBgEAAAAB2AYBAAAAAdkGgAAAAAHaBoAAAAAB2waAAAAAAQxFAQDfCgAhrAYAAIULADCtBgAAkQoAEK4GAACFCwAwrwYBAN0KACG4BkAA4goAIbkGQADiCgAh3AYBAN0KACHdBgEA3woAId4GAQDdCgAh3wYAAIALACDgBgEA3QoAIQqsBgAAhgsAMK0GAAD5CQAQrgYAAIYLADCvBgEA3QoAIbgGQADiCgAhuQZAAOIKACHcBgEA3QoAIeEGAQDdCgAh4gYBAN0KACHjBgEA3QoAIQqsBgAAhwsAMK0GAADjCQAQrgYAAIcLADCvBgEA3QoAIbgGQADiCgAhuQZAAOIKACHcBgEA3QoAId8GAACACwAg5AYBAN0KACHlBgEA3woAIQtrAACKCwAgrAYAAIgLADCtBgAA0AkAEK4GAACICwAwrwYBAPIKACG4BkAA9goAIbkGQAD2CgAh3AYBAPIKACHfBgAAiQsAIOQGAQDyCgAh5QYBAPMKACEMugaAAAAAAb0GgAAAAAG-BoAAAAABvwaAAAAAAcAGgAAAAAHBBoAAAAAB1gYBAAAAAdcGAQAAAAHYBgEAAAAB2QaAAAAAAdoGgAAAAAHbBoAAAAABA8sGAADhAQAgzAYAAOEBACDNBgAA4QEAICCsBgAAiwsAMK0GAADKCQAQrgYAAIsLADCvBgIA3goAIbgGQADiCgAhuQZAAOIKACHFBgEA3QoAIcgGAQDfCgAh1QYBAN8KACHcBgEA3QoAId4GAgCMCwAh5gYBAN8KACHnBiAA4AoAIegGIADgCgAh6QYgAOAKACHqBiAA4AoAIesGIADgCgAh7AYgAOAKACHtBiAA4AoAIe4GAQDfCgAh7wZAAOEKACHwBkAA4QoAIfEGQADhCgAh8gYIAPkKACHzBgIAjAsAIfQGCAD5CgAh9QYCAIwLACH2BgIAjAsAIfcGAgCMCwAh-AYBAN8KACH5BgEA3woAIfoGQADhCgAhDQUAAOcKACAYAADnCgAgGQAA5woAICoAAPsKACArAADnCgAgugYCAAAAAbsGAgAAAAW8BgIAAAAFvQYCAAAAAb4GAgAAAAG_BgIAAAABwAYCAAAAAcEGAgCNCwAhDQUAAOcKACAYAADnCgAgGQAA5woAICoAAPsKACArAADnCgAgugYCAAAAAbsGAgAAAAW8BgIAAAAFvQYCAAAAAb4GAgAAAAG_BgIAAAABwAYCAAAAAcEGAgCNCwAhBqwGAACOCwAwrQYAAK4JABCuBgAAjgsAMK8GAgDeCgAh9wYBAN8KACH7BgEA3woAIQeKBQAAkAsAIKwGAACPCwAwrQYAAOYIABCuBgAAjwsAMK8GAgCUCwAh9wYBAPMKACH7BgEA8woAIQPLBgAA3AgAIMwGAADcCAAgzQYAANwIACARrAYAAJELADCtBgAAlgkAEK4GAACRCwAwrwYCAN4KACG4BkAA4goAIbkGQADiCgAhxQYBAN0KACHIBgEA3woAIdUGAQDfCgAh3AYBAN0KACH4BgEA3woAIfwGAQDfCgAh_QYBAN8KACH-BgEA3woAIf8GAQDfCgAhgAcgAOAKACGBByAA4AoAIRKKBQAAkAsAIKwGAACSCwAwrQYAAOIIABCuBgAAkgsAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACH8BgEA8woAIf0GAQDzCgAh_gYBAPMKACH_BgEA8woAIYAHIAD0CgAhgQcgAPQKACEOrAYAAJMLADCtBgAA_ggAEK4GAACTCwAwrwYCAN4KACG4BkAA4goAIbkGQADiCgAhxQYBAN0KACHIBgEA3woAIcoGIADgCgAh1QYBAN8KACHcBgEA3QoAIfgGAQDfCgAhggcBAN8KACGDByAA4AoAIQi6BgIAAAABuwYCAAAABLwGAgAAAAS9BgIAAAABvgYCAAAAAb8GAgAAAAHABgIAAAABwQYCAOQKACEPigUAAJALACCsBgAAlQsAMK0GAADgCAAQrgYAAJULADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAhygYgAPQKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACGCBwEA8woAIYMHIAD0CgAhIzoAAJkLACA8AACYCwAgiwUAAJoLACCsBgAAlgsAMK0GAADcCAAQrgYAAJYLADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh1QYBAPMKACHcBgEA8goAId4GAgCXCwAh5gYBAPMKACHnBiAA9AoAIegGIAD0CgAh6QYgAPQKACHqBiAA9AoAIesGIAD0CgAh7AYgAPQKACHtBiAA9AoAIe4GAQDzCgAh7wZAAPUKACHwBkAA9QoAIfEGQAD1CgAh8gYIAP0KACHzBgIAlwsAIfQGCAD9CgAh9QYCAJcLACH2BgIAlwsAIfcGAgCXCwAh-AYBAPMKACH5BgEA8woAIfoGQAD1CgAhCLoGAgAAAAG7BgIAAAAFvAYCAAAABb0GAgAAAAG-BgIAAAABvwYCAAAAAcAGAgAAAAHBBgIA5woAIRGKBQAAkAsAIKwGAACVCwAwrQYAAOAIABCuBgAAlQsAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHKBiAA9AoAIdUGAQDzCgAh3AYBAPIKACH4BgEA8woAIYIHAQDzCgAhgwcgAPQKACHBCAAA4AgAIMIIAADgCAAgFIoFAACQCwAgrAYAAJILADCtBgAA4ggAEK4GAACSCwAwrwYCAJQLACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdUGAQDzCgAh3AYBAPIKACH4BgEA8woAIfwGAQDzCgAh_QYBAPMKACH-BgEA8woAIf8GAQDzCgAhgAcgAPQKACGBByAA9AoAIcEIAADiCAAgwggAAOIIACAJigUAAJALACCsBgAAjwsAMK0GAADmCAAQrgYAAI8LADCvBgIAlAsAIfcGAQDzCgAh-wYBAPMKACHBCAAA5ggAIMIIAADmCAAgIjMBAN0KACFFAQDfCgAhXQEA3woAIawGAACbCwAwrQYAANcIABCuBgAAmwsAMK8GAgDeCgAhuAZAAOIKACG5BkAA4goAIcUGAQDdCgAh3AYBAN0KACHeBgIA3goAIeAGAgCMCwAh5wYgAOAKACH2BgIAjAsAIfkGAQDfCgAhhAcBAN8KACGFBwIAjAsAIYYHAgCMCwAhiAcAAJwLiAcjiQcIAJ0LACGKBwEA3woAIYsHAQDfCgAhjAcCAIwLACGNBwIAjAsAIY4HAQDfCgAhjwcCAIwLACGQBwIAjAsAIZEHQADhCgAhkgdAAOEKACGTB0AA4QoAIZQHQADhCgAhlQcAAJ4LACCWBwEA3QoAIQcFAADnCgAgGAAAoQsAIBkAAKELACC6BgAAAIgHA7sGAAAAiAcJvAYAAACIBwnBBgAAoAuIByMNBQAA5AoAIBgAAO4KACAZAADuCgAgKgAA7goAICsAAO4KACC6BggAAAABuwYIAAAABLwGCAAAAAS9BggAAAABvgYIAAAAAb8GCAAAAAHABggAAAABwQYIAJ8LACEEugYCAAAABZcHAgAAAAGYBwIAAAAEmQcCAAAABA0FAADkCgAgGAAA7goAIBkAAO4KACAqAADuCgAgKwAA7goAILoGCAAAAAG7BggAAAAEvAYIAAAABL0GCAAAAAG-BggAAAABvwYIAAAAAcAGCAAAAAHBBggAnwsAIQcFAADnCgAgGAAAoQsAIBkAAKELACC6BgAAAIgHA7sGAAAAiAcJvAYAAACIBwnBBgAAoAuIByMEugYAAACIBwO7BgAAAIgHCbwGAAAAiAcJwQYAAKELiAcjEKwGAACiCwAwrQYAAK0IABCuBgAAogsAMK8GAgDeCgAhtQYgAOAKACG4BkAA4goAIbkGQADiCgAhxQYBAN0KACHIBgEA3woAIcoGIADgCgAh1QYBAN8KACHcBgEA3QoAIfgGAQDfCgAhmgcgAOAKACGbByAA4AoAIZwHIADgCgAhETkAAP4KACCsBgAAowsAMK0GAABhABCuBgAAowsAMK8GAgCUCwAhtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIcoGIAD0CgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhmgcgAPQKACGbByAA9AoAIZwHIAD0CgAhDKwGAACkCwAwrQYAAJUIABCuBgAApAsAMK8GAgDeCgAhtQYgAOAKACG4BkAA4goAIbkGQADiCgAhxQYBAN0KACHIBgEA3woAIdUGAQDfCgAh3AYBAN0KACH4BgEA3woAIQ05AAD-CgAgrAYAAKULADCtBgAAgggAEK4GAAClCwAwrwYCAJQLACG1BiAA9AoAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhC6wGAACmCwAwrQYAAPwHABCuBgAApgsAMK8GAgDeCgAhuAZAAOIKACG5BkAA4goAIcUGAQDdCgAh3AYBAN0KACH4BgEA3woAIZ0HAgCMCwAhngcBAN8KACEMOQAA_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_C88HIgcFAADkCgAgGAAAwAsAIBkAAMALACC6BgAAAM8HArsGAAAAzwcIvAYAAADPBwjBBgAAvwvPByIEugYAAADPBwK7BgAAAM8HCLwGAAAAzwcIwQYAAMALzwciBwUAAOQKACAYAADCCwAgGQAAwgsAILoGAAAAzAcCuwYAAADMBwi8BgAAAMwHCMEGAADBC8wHIgS6BgAAAMwHArsGAAAAzAcIvAYAAADMBwjBBgAAwgvMByIHBQAA5woAIBgAAMQLACAZAADECwAgugYAAADFBwO7BgAAAMUHCbwGAAAAxQcJwQYAAMMLxQcjBLoGAAAAxQcDuwYAAADFBwm8BgAAAMUHCcEGAADEC8UHIwcFAADnCgAgGAAAxgsAIBkAAMYLACC6BgAAAMAHA7sGAAAAwAcJvAYAAADABwnBBgAAxQvAByMEugYAAADABwO7BgAAAMAHCbwGAAAAwAcJwQYAAMYLwAcjBwUAAOcKACAYAADICwAgGQAAyAsAILoGAAAAvQcDuwYAAAC9Bwm8BgAAAL0HCcEGAADHC70HIwS6BgAAAL0HA7sGAAAAvQcJvAYAAAC9BwnBBgAAyAu9ByMqrAYAAMkLADCtBgAA3wUAEK4GAADJCwAwrwYCAN4KACG4BkAA4goAIbkGQADiCgAhxQYBAN0KACHIBgEA3woAIdQGAgCMCwAh1QYBAN8KACHsBiAA4AoAIe0GIADgCgAh-AYBAN8KACH5BgEA3woAIfoGQADhCgAhtwcCAIwLACHfBwgAnQsAIeAHAQDfCgAh4QcBAN8KACHiBwIAjAsAIeMHAQDfCgAh5AcBAN8KACHlBwgAnQsAIeYHCACdCwAh5wcIAJ0LACHoBwgAnQsAIekHCACdCwAh6gcIAJ0LACHrBwIA3goAIewHAgDeCgAh7QcBAN8KACHuBwgA-QoAIe8HIADgCgAh8AcgAOAKACHxByAA4AoAIfIHIADgCgAh8wcgAOAKACH0ByAA4AoAIfUHAgDeCgAh9gcCAN4KACH3BwEA3QoAIfgHQADhCgAhGawGAADKCwAwrQYAAMUFABCuBgAAygsAMK8GAgDeCgAhsQYBAN8KACG3BkAA4QoAIbgGQADiCgAhuQZAAOIKACHFBgEA3QoAIcgGAQDfCgAh3AYBAN0KACH5BwEA3woAIfoHAQDfCgAh-wcCAN4KACH8BwIAjAsAIf0HAQDfCgAh_gcBAN8KACH_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-AYBAN8KACEMOQAA_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_goAIFkAAKwLACBoAACrDAAgawAAigsAIG4AAKwMACBvAAD3CgAgcAAA9woAIHEAAKwLACCsBgAAqgwAMK0GAABGABCuBgAAqgwAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAh3AYBAPIKACHCBwEA8woAIYoIAQDzCgAhnAgBAPMKACGdCCAA9AoAIZ4IQAD1CgAhnwgBAPMKACGgCCAA9AoAIaEIAQDzCgAhoghAAPUKACHBCAAARgAgwggAAEYAIAPLBgAA6QEAIMwGAADpAQAgzQYAAOkBACAeMwAA9woAIDQAAPULACA1AAD4CwAgWQAArAsAIGYAAP4KACBnAACsCwAgrAYAAPcLADCtBgAATgAQrgYAAPcLADCvBgIAlAsAIbUGIAD0CgAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHKBiAA9AoAIdwGAQDyCgAh4AYCAJQLACH4BgEA8woAIbkHAQDzCgAhugcBAPMKACG7BwEA8woAIb0HAADnC70HI74HAQDzCgAhwAcAAOgLwAcjwgcBAPMKACGYCAEA8woAIZkIIAD0CgAhmgggAPQKACGbCCAA9AoAIQPLBgAASgAgzAYAAEoAIM0GAABKACASTQAAzwsAIE4AAPoLACCsBgAA-QsAMK0GAACgAQAQrgYAAPkLADCvBgIAlAsAIbUGIAD0CgAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHKBiAA9AoAIdUGAQDzCgAh3AYBAPIKACH4BgEA8woAIYgIAgCXCwAhjggCAJQLACGPCAIAlAsAIRJQAADOCwAgUQAAzwsAIFIAANALACCsBgAAzQsAMK0GAACYAQAQrgYAAM0LADCvBgIAlAsAIbUGIAD0CgAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACGLCCAA9AoAIcEIAACYAQAgwggAAJgBACAOTgAA_AsAIE8AAP0LACCsBgAA-wsAMK0GAACaAQAQrgYAAPsLADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIfgGAQDzCgAhhwgCAJQLACGICAIAlAsAIYkIAgCXCwAhiggBAPMKACESUAAAzgsAIFEAAM8LACBSAADQCwAgrAYAAM0LADCtBgAAmAEAEK4GAADNCwAwrwYCAJQLACG1BiAA9AoAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhiwggAPQKACHBCAAAmAEAIMIIAACYAQAgIkYAANULACBHAADVCwAgSwAAhAwAIEwAAIUMACBRAADPCwAgVAAAzgsAIFUAAOoLACCsBgAAggwAMK0GAACDAQAQrgYAAIIMADCvBgIAlAsAIbEGAQDzCgAhtwZAAPUKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdwGAQDyCgAh-QcBAPMKACH6BwEA8woAIfsHAgCUCwAh_AcCAJcLACH9BwEA8woAIf4HAQDzCgAh_wcBAPMKACGACAIAlwsAIYEICACDDAAhgggIAIMMACGDCCAA9AoAIYQIIAD0CgAhhQgCAJQLACGGCAEA8woAIcEIAACDAQAgwggAAIMBACAPTgAA-gsAIE8AAP0LACBTAAD_CwAgrAYAAP4LADCtBgAAkwEAEK4GAAD-CwAwrwYCAJQLACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACH4BgEA8woAIYgIAgCXCwAhiQgCAJcLACGMCAIAlAsAIY0IAgCUCwAhFE0AAM8LACBOAAD6CwAgrAYAAPkLADCtBgAAoAEAEK4GAAD5CwAwrwYCAJQLACG1BiAA9AoAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAhygYgAPQKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACGICAIAlwsAIY4IAgCUCwAhjwgCAJQLACHBCAAAoAEAIMIIAACgAQAgD0kAAIEMACBKAADVCwAgrAYAAIAMADCtBgAAiAEAEK4GAACADAAwrwYCAJQLACG1BiAA9AoAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhkAgCAJQLACEPSAAA2QsAIKwGAADYCwAwrQYAAIoEABCuBgAA2AsAMK8GAgCUCwAhtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdUGAQDzCgAh3AYBAPIKACH4BgEA8woAIcEIAACKBAAgwggAAIoEACAgRgAA1QsAIEcAANULACBLAACEDAAgTAAAhQwAIFEAAM8LACBUAADOCwAgVQAA6gsAIKwGAACCDAAwrQYAAIMBABCuBgAAggwAMK8GAgCUCwAhsQYBAPMKACG3BkAA9QoAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh3AYBAPIKACH5BwEA8woAIfoHAQDzCgAh-wcCAJQLACH8BwIAlwsAIf0HAQDzCgAh_gcBAPMKACH_BwEA8woAIYAIAgCXCwAhgQgIAIMMACGCCAgAgwwAIYMIIAD0CgAhhAggAPQKACGFCAIAlAsAIYYIAQDzCgAhCLoGCAAAAAG7BggAAAAEvAYIAAAABL0GCAAAAAG-BggAAAABvwYIAAAAAcAGCAAAAAHBBggA7goAIRFJAACBDAAgSgAA1QsAIKwGAACADAAwrQYAAIgBABCuBgAAgAwAMK8GAgCUCwAhtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdUGAQDzCgAh3AYBAPIKACH4BgEA8woAIZAIAgCUCwAhwQgAAIgBACDCCAAAiAEAIA9KAADVCwAgrAYAANQLADCtBgAAjwEAEK4GAADUCwAwrwYCAJQLACG1BiAA9AoAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhwQgAAI8BACDCCAAAjwEAIC42AACIDAAgRAAAhwwAIFYAAIkMACBXAACKDAAgrAYAAIYMADCtBgAAfQAQrgYAAIYMADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh1AYCAJcLACHVBgEA8woAIewGIAD0CgAh7QYgAPQKACH4BgEA8woAIfkGAQDzCgAh-gZAAPUKACG3BwIAlwsAId8HCACDDAAh4AcBAPMKACHhBwEA8woAIeIHAgCXCwAh4wcBAPMKACHkBwEA8woAIeUHCACDDAAh5gcIAIMMACHnBwgAgwwAIegHCACDDAAh6QcIAIMMACHqBwgAgwwAIesHAgCUCwAh7AcCAJQLACHtBwEA8woAIe4HCAD9CgAh7wcgAPQKACHwByAA9AoAIfEHIAD0CgAh8gcgAPQKACHzByAA9AoAIfQHIAD0CgAh9QcCAJQLACH2BwIAlAsAIfcHAQDyCgAh-AdAAPUKACFKMwAA9woAIDQAAO8LACA3AAD3CgAgQAAAmQwAIEIAAJcMACBFAACSDAAgWAAA6gsAIFoAAJMMACBbAACUDAAgXAAAlQwAIF0AAJYMACBeAACYDAAgXwAA9woAIGAAAJoMACBhAADvCwAgYgAAmgwAIGMAAPcKACCsBgAAjgwAMK0GAAB1ABCuBgAAjgwAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAh1QYBAPMKACHgBgIAlwsAIecGIAD0CgAh-AYBAPMKACH5BgEA8woAIfoGQAD1CgAhjAcCAJcLACGPBwIAlAsAIZ8HIAD0CgAhoAcgAPQKACGhByAA9AoAIaIHIAD0CgAhowcgAPQKACGkByAA9AoAIbgHAQDyCgAhuQcBAPMKACG6BwEA8woAIbsHAQDzCgAhvQcAAOcLvQcjvgcBAPMKACHABwAA6AvAByPBBwEA8woAIcIHAQDzCgAhwwcBAPMKACHFBwAAjwzFByPGBwEA8woAIccHAQDzCgAhyAcgAPQKACHJByAA9AoAIcoHCACDDAAhzAcAAJAMzAcizQdAAPUKACHPBwAAkQzPByLQBwEA8woAIdEHIAD0CgAh0gcgAPQKACHTByAA9AoAIdQHAQDzCgAh1QcCAJcLACHWBwIAlAsAIdcHAgCUCwAh2AcCAJQLACHZBwIAlAsAIdoHAgCXCwAh2wcCAJcLACHcBwIAlwsAId0HAgCXCwAh3gdAAPUKACHBCAAAdQAgwggAAHUAIDEzAQDyCgAhNAAA7wsAIDgAAKEMACA6AACiDAAgOwAAowwAIDwAAKQMACA9AAClDAAgPgAA9woAID8AAPcKACBAAACZDAAgQQAAmgwAIEIAAKYMACBFAQDzCgAhWAAA6gsAIF0BAPMKACFlAACnDAAgrAYAAJ8MADCtBgAAUgAQrgYAAJ8MADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIdwGAQDyCgAh3gYCAJQLACHgBgIAlwsAIecGIAD0CgAh9gYCAJcLACH5BgEA8woAIYQHAQDzCgAhhQcCAJcLACGGBwIAlwsAIYgHAACgDIgHI4kHCACDDAAhigcBAPMKACGLBwEA8woAIYwHAgCXCwAhjQcCAJcLACGOBwEA8woAIY8HAgCXCwAhkAcCAJcLACGRB0AA9QoAIZIHQAD1CgAhkwdAAPUKACGUB0AA9QoAIZUHAACeCwAglgcBAPIKACHBCAAAUgAgwggAAFIAICJGAADVCwAgRwAA1QsAIEsAAIQMACBMAACFDAAgUQAAzwsAIFQAAM4LACBVAADqCwAgrAYAAIIMADCtBgAAgwEAEK4GAACCDAAwrwYCAJQLACGxBgEA8woAIbcGQAD1CgAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHcBgEA8goAIfkHAQDzCgAh-gcBAPMKACH7BwIAlAsAIfwHAgCXCwAh_QcBAPMKACH-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-AsAIFkAAKwLACBmAAD-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_goAIKwGAACjCwAwrQYAAGEAEK4GAACjCwAwrwYCAJQLACG1BiAA9AoAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAhygYgAPQKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACGaByAA9AoAIZsHIAD0CgAhnAcgAPQKACHBCAAAYQAgwggAAGEAIBEzAQDzCgAhNwEA8goAITkAAP4KACCsBgAA_AoAMK0GAABlABCuBgAA_AoAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhxgYBAPIKACHHBgEA8goAIcgGAQDzCgAhyQYIAP0KACHKBiAA9AoAIcEIAABlACDCCAAAZQAgFjkAAP4KACBVAADqCwAgZAAA6QsAIKwGAADmCwAwrQYAAG4AEK4GAADmCwAwrwYCAJQLACG1BiAA9AoAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh3AYBAPIKACH4BgEA8woAIbkHAQDzCgAhugcBAPMKACG7BwEA8woAIb0HAADnC70HI74HAQDzCgAhwAcAAOgLwAcjwQgAAG4AIMIIAABuACAOOQAA_goAIKwGAADkCwAwrQYAAMwBABCuBgAA5AsAMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACHBCAAAzAEAIMIIAADMAQAgGjQAAO8LACA5AAD-CgAgWQAArAsAIGAAAJoMACCsBgAAqAwAMK0GAABKABCuBgAAqAwAMK8GAgCUCwAhsgYBAPIKACGzBgEA8goAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIeAGAgCXCwAh5gYCAJcLACHCBwEA8woAIcMHAQDzCgAhxQcAAI8MxQcjxgcBAPMKACHbBwIAlwsAIZEIIAD0CgAhkgggAPQKACGTCAEA8woAIZQIAQDzCgAhlggAAKkMlggjlwhAAPUKACEEugYAAACWCAO7BgAAAJYICbwGAAAAlggJwQYAAN8LlggjGjUAAPgLACA5AAD-CgAgWQAArAsAIGgAAKsMACBrAACKCwAgbgAArAwAIG8AAPcKACBwAAD3CgAgcQAArAsAIKwGAACqDAAwrQYAAEYAEK4GAACqDAAwrwYCAJQLACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHcBgEA8goAIcIHAQDzCgAhiggBAPMKACGcCAEA8woAIZ0IIAD0CgAhnghAAPUKACGfCAEA8woAIaAIIAD0CgAhoQgBAPMKACGiCEAA9QoAIQPLBgAATgAgzAYAAE4AIM0GAABOACADywYAAO8BACDMBgAA7wEAIM0GAADvAQAgCzAAAIwMACCsBgAArQwAMK0GAABDABCuBgAArQwAMK8GAQDyCgAhqQgBAPIKACGrCAEA8goAIawIQAD2CgAhrQggAPQKACGuCEAA9QoAIa8IQAD1CgAhIi8AAK8MACAxAACwDAAgMgAAsQwAIDMBAPMKACE4AAChDAAgOQAA_goAIGgAAKsMACByAACyDAAgcwAAsgwAIHQAAP4KACB1AACsCwAgdgAArAsAIHcAAKwLACB4AACsCwAgeQAAkgwAIKwGAACuDAAwrQYAADsAEK4GAACuDAAwrwYBAPIKACGwBgIAlwsAIbIGAQDzCgAhswYBAPMKACG4BkAA9goAIbkGQAD2CgAhxgcBAPIKACGRCCAA9AoAIZQIAQDzCgAhpAgBAPMKACGlCAEA8goAIaYIAQDzCgAhpwggAPQKACGoCAEA8woAIakIAQDzCgAhqggBAPMKACEDywYAADcAIMwGAAA3ACDNBgAANwAgETAAAPcKACCsBgAA8QoAMK0GAABAABCuBgAA8QoAMK8GAQDyCgAhsAYCAJQLACGxBgEA8goAIbIGAQDyCgAhswYBAPIKACG0BgEA8woAIbUGIAD0CgAhtgYBAPMKACG3BkAA9QoAIbgGQAD2CgAhuQZAAPYKACHBCAAAQAAgwggAAEAAIAPLBgAAQwAgzAYAAEMAIM0GAABDACADywYAAEYAIMwGAABGACDNBgAARgAgCi4AALQMACCsBgAAswwAMK0GAAA3ABCuBgAAswwAMK8GAQDyCgAhuAZAAPYKACG5BkAA9goAIZQIAQDyCgAhowgBAPIKACGkCAEA8goAIQPLBgAAOwAgzAYAADsAIM0GAAA7ACAPrAYAALUMADCtBgAAMgAQrgYAALUMADCvBgEA3QoAIbgGQADiCgAhsAgBAN0KACGxCAEA3QoAIbIIAQDdCgAhswgCAN4KACG0CAIA3goAIbUIAgDeCgAhtggCAN4KACG3CAIA3goAIbgIAACACwAguQgCAN4KACEMPAAAuAy9CCKsBgAAtgwAMK0GAAAcABCuBgAAtgwAMK8GAQDdCgAhuAZAAOIKACG5BkAA4goAIbsIAAC3DLsIIr0IAQDdCgAhvghAAOEKACG_CEAA4QoAIcAIAQDfCgAhBwUAAOQKACAYAAC8DAAgGQAAvAwAILoGAAAAuwgCuwYAAAC7CAi8BgAAALsICMEGAAC7DLsIIgcFAADkCgAgGAAAugwAIBkAALoMACC6BgAAAL0IArsGAAAAvQgIvAYAAAC9CAjBBgAAuQy9CCIHBQAA5AoAIBgAALoMACAZAAC6DAAgugYAAAC9CAK7BgAAAL0ICLwGAAAAvQgIwQYAALkMvQgiBLoGAAAAvQgCuwYAAAC9CAi8BgAAAL0ICMEGAAC6DL0IIgcFAADkCgAgGAAAvAwAIBkAALwMACC6BgAAALsIArsGAAAAuwgIvAYAAAC7CAjBBgAAuwy7CCIEugYAAAC7CAK7BgAAALsICLwGAAAAuwgIwQYAALwMuwgiDQQAAMAMACA8AAC_DL0IIqwGAAC9DAAwrQYAAAkAEK4GAAC9DAAwrwYBAPIKACG4BkAA9goAIbkGQAD2CgAhuwgAAL4MuwgivQgBAPIKACG-CEAA9QoAIb8IQAD1CgAhwAgBAPMKACEEugYAAAC7CAK7BgAAALsICLwGAAAAuwgIwQYAALwMuwgiBLoGAAAAvQgCuwYAAAC9CAi8BgAAAL0ICMEGAAC6DL0IIgPLBgAAAwAgzAYAAAMAIM0GAAADACAQAwAAwgwAIKwGAADBDAAwrQYAAAMAEK4GAADBDAAwrwYBAPIKACG4BkAA9goAIbAIAQDyCgAhsQgBAPIKACGyCAEA8goAIbMIAgCUCwAhtAgCAJQLACG1CAIAlAsAIbYIAgCUCwAhtwgCAJQLACG4CAAAiQsAILkIAgCUCwAhDwQAAMAMACA8AAC_DL0IIqwGAAC9DAAwrQYAAAkAEK4GAAC9DAAwrwYBAPIKACG4BkAA9goAIbkGQAD2CgAhuwgAAL4MuwgivQgBAPIKACG-CEAA9QoAIb8IQAD1CgAhwAgBAPMKACHBCAAACQAgwggAAAkAIAAAAAAAAAHJCAEAAAABBckIAgAAAAHNCAIAAAABzggCAAAAAc8IAgAAAAHQCAIAAAABAckIAQAAAAEByQggAAAAAQHJCEAAAAABAckIQAAAAAEHEgAA0AwAIBMAANMMACDDCAAA0QwAIMQIAADSDAAgxQgAADsAIMYIAAA7ACDHCAAAPQAgHS8AALEQACAyAACyEAAgMwEAAAABOAAAtxAAIDkAALUQACBoAAC4EAAgcgAAsxAAIHMAALQQACB0AAC2EAAgdQAAuRAAIHYAALoQACB3AAC7EAAgeAAAvBAAIHkAAL0QACCvBgEAAAABsgYBAAAAAbMGAQAAAAG4BkAAAAABuQZAAAAAAcYHAQAAAAGRCCAAAAABlAgBAAAAAaQIAQAAAAGlCAEAAAABpggBAAAAAacIIAAAAAGoCAEAAAABqQgBAAAAAaoIAQAAAAECAAAAPQAgEgAA0AwAIAMAAAA7ACASAADQDAAgEwAA1AwAIB8AAAA7ACALAADUDAAgLwAA1QwAIDIAANYMACAzAQDLDAAhOAAA2wwAIDkAANkMACBoAADcDAAgcgAA1wwAIHMAANgMACB0AADaDAAgdQAA3QwAIHYAAN4MACB3AADfDAAgeAAA4AwAIHkAAOEMACCvBgEAyQwAIbIGAQDLDAAhswYBAMsMACG4BkAAzgwAIbkGQADODAAhxgcBAMkMACGRCCAAzAwAIZQIAQDLDAAhpAgBAMsMACGlCAEAyQwAIaYIAQDLDAAhpwggAMwMACGoCAEAywwAIakIAQDLDAAhqggBAMsMACEdLwAA1QwAIDIAANYMACAzAQDLDAAhOAAA2wwAIDkAANkMACBoAADcDAAgcgAA1wwAIHMAANgMACB0AADaDAAgdQAA3QwAIHYAAN4MACB3AADfDAAgeAAA4AwAIHkAAOEMACCvBgEAyQwAIbIGAQDLDAAhswYBAMsMACG4BkAAzgwAIbkGQADODAAhxgcBAMkMACGRCCAAzAwAIZQIAQDLDAAhpAgBAMsMACGlCAEAyQwAIaYIAQDLDAAhpwggAMwMACGoCAEAywwAIakIAQDLDAAhqggBAMsMACEKEgAAphAAMBMAAKoQADDDCAAApxAAMMQIAACoEAAwxQgAAKkQADDGCAAAqRAAMMcIAACpEAAwyQgAAKkQADDKCAAAqxAAMMsIAACsEAAwCxIAAJoQADATAACfEAAwwwgAAJsQADDECAAAnBAAMMUIAACeEAAwxggAAJ4QADDHCAAAnhAAMMgIAACdEAAgyQgAAJ4QADDKCAAAoBAAMMsIAAChEAAwCxIAAI8QADATAACTEAAwwwgAAJAQADDECAAAkRAAMMUIAACPDwAwxggAAI8PADDHCAAAjw8AMMgIAACSEAAgyQgAAI8PADDKCAAAlBAAMMsIAACSDwAwCxIAAIsPADATAACQDwAwwwgAAIwPADDECAAAjQ8AMMUIAACPDwAwxggAAI8PADDHCAAAjw8AMMgIAACODwAgyQgAAI8PADDKCAAAkQ8AMMsIAACSDwAwCxIAAIIPADATAACGDwAwwwgAAIMPADDECAAAhA8AMMUIAACEDgAwxggAAIQOADDHCAAAhA4AMMgIAACFDwAgyQgAAIQOADDKCAAAhw8AMMsIAACHDgAwCxIAAPkOADATAAD9DgAwwwgAAPoOADDECAAA-w4AMMUIAACEDgAwxggAAIQOADDHCAAAhA4AMMgIAAD8DgAgyQgAAIQOADDKCAAA_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-AwAIAsAAPkMACA3rAYAAPcMADCtBgAA-AwAEK4GAAD3DAAwrwYCAJQLACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHVBgEA8woAIeAGAgCXCwAh5wYgAPQKACH4BgEA8woAIfkGAQDzCgAh-gZAAPUKACGMBwIAlwsAIY8HAgCUCwAhnwcgAPQKACGgByAA9AoAIaEHIAD0CgAhogcgAPQKACGjByAA9AoAIaQHIAD0CgAhuAcBAPIKACG5BwEA8woAIboHAQDzCgAhuwcBAPMKACG9BwAA5wu9ByO-BwEA8woAIcAHAADoC8AHI8EHAQDzCgAhwgcBAPMKACHDBwEA8woAIcUHAACPDMUHI8YHAQDzCgAhxwcBAPMKACHIByAA9AoAIckHIAD0CgAhygcIAIMMACHMBwAAkAzMByLNB0AA9QoAIc8HAACRDM8HItAHAQDzCgAh0QcgAPQKACHSByAA9AoAIdMHIAD0CgAh1AcBAPMKACHVBwIAlwsAIdYHAgCUCwAh1wcCAJQLACHYBwIAlAsAIdkHAgCUCwAh2gcCAJcLACHbBwIAlwsAIdwHAgCXCwAh3QcCAJcLACHeB0AA9QoAITesBgAA9wwAMK0GAAD4DAAQrgYAAPcMADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIdUGAQDzCgAh4AYCAJcLACHnBiAA9AoAIfgGAQDzCgAh-QYBAPMKACH6BkAA9QoAIYwHAgCXCwAhjwcCAJQLACGfByAA9AoAIaAHIAD0CgAhoQcgAPQKACGiByAA9AoAIaMHIAD0CgAhpAcgAPQKACG4BwEA8goAIbkHAQDzCgAhugcBAPMKACG7BwEA8woAIb0HAADnC70HI74HAQDzCgAhwAcAAOgLwAcjwQcBAPMKACHCBwEA8woAIcMHAQDzCgAhxQcAAI8MxQcjxgcBAPMKACHHBwEA8woAIcgHIAD0CgAhyQcgAPQKACHKBwgAgwwAIcwHAACQDMwHIs0HQAD1CgAhzwcAAJEMzwci0AcBAPMKACHRByAA9AoAIdIHIAD0CgAh0wcgAPQKACHUBwEA8woAIdUHAgCXCwAh1gcCAJQLACHXBwIAlAsAIdgHAgCUCwAh2QcCAJQLACHaBwIAlwsAIdsHAgCXCwAh3AcCAJcLACHdBwIAlwsAId4HQAD1CgAhM68GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-QYBAMsMACH6BkAAzQwAIYwHAgCADQAhjwcCAMoMACGfByAAzAwAIaAHIADMDAAhoQcgAMwMACGiByAAzAwAIaMHIADMDAAhpAcgAMwMACG4BwEAyQwAIbkHAQDLDAAhugcBAMsMACG7BwEAywwAIb0HAAD6DL0HI74HAQDLDAAhwAcAAPsMwAcjwQcBAMsMACHCBwEAywwAIcMHAQDLDAAhxQcAAPwMxQcjxgcBAMsMACHHBwEAywwAIcgHIADMDAAhyQcgAMwMACHKBwgA_QwAIcwHAAD-DMwHIs0HQADNDAAhzwcAAP8Mzwci0AcBAMsMACHRByAAzAwAIdIHIADMDAAh0wcgAMwMACHUBwEAywwAIdUHAgCADQAh1gcCAMoMACHXBwIAygwAIdgHAgDKDAAh2QcCAMoMACHaBwIAgA0AIdsHAgCADQAh3AcCAIANACHdBwIAgA0AId4HQADNDAAhAckIAAAAvQcDAckIAAAAwAcDAckIAAAAxQcDBckICAAAAAHNCAgAAAABzggIAAAAAc8ICAAAAAHQCAgAAAABAckIAAAAzAcCAckIAAAAzwcCBckIAgAAAAHNCAIAAAABzggCAAAAAc8IAgAAAAHQCAIAAAABQzQAAIsNACA3AACRDQAgQAAAjA0AIEIAAIgNACBFAACCDQAgWAAAgw0AIFoAAIQNACBbAACFDQAgXAAAhg0AIF0AAIcNACBeAACJDQAgXwAAig0AIGAAAI0NACBhAACODQAgYgAAjw0AIGMAAJANACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdUGAQDLDAAh4AYCAIANACHnBiAAzAwAIfkGAQDLDAAh-gZAAM0MACGMBwIAgA0AIY8HAgDKDAAhnwcgAMwMACGgByAAzAwAIaEHIADMDAAhogcgAMwMACGjByAAzAwAIaQHIADMDAAhuAcBAMkMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8EHAQDLDAAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAhxwcBAMsMACHIByAAzAwAIckHIADMDAAhygcIAP0MACHMBwAA_gzMByLNB0AAzQwAIc8HAAD_DM8HItAHAQDLDAAh0QcgAMwMACHSByAAzAwAIdMHIADMDAAh1AcBAMsMACHVBwIAgA0AIdYHAgDKDAAh1wcCAMoMACHYBwIAygwAIdkHAgDKDAAh2gcCAIANACHbBwIAgA0AIdwHAgCADQAh3QcCAIANACHeB0AAzQwAIQsSAAClDQAwEwAAqQ0AMMMIAACmDQAwxAgAAKcNADDFCAAA5gwAMMYIAADmDAAwxwgAAOYMADDICAAAqA0AIMkIAADmDAAwyggAAKoNADDLCAAA6QwAMAsSAACSDQAwEwAAlw0AMMMIAACTDQAwxAgAAJQNADDFCAAAlg0AMMYIAACWDQAwxwgAAJYNADDICAAAlQ0AIMkIAACWDQAwyggAAJgNADDLCAAAmQ0AMAUSAACCGAAgEwAAwhgAIMMIAACDGAAgxAgAAMEYACDHCAAAtQcAIAUSAACAGAAgEwAAvxgAIMMIAACBGAAgxAgAAL4YACDHCAAAgwcAIAUSAAD-FwAgEwAAvBgAIMMIAAD_FwAgxAgAALsYACDHCAAAnAcAIAUSAAD8FwAgEwAAuRgAIMMIAAD9FwAgxAgAALgYACDHCAAA6gYAIAUSAAD6FwAgEwAAthgAIMMIAAD7FwAgxAgAALUYACDHCAAA0QYAIAcSAAD4FwAgEwAAsxgAIMMIAAD5FwAgxAgAALIYACDFCAAAcQAgxggAAHEAIMcIAABzACAHEgAA9hcAIBMAALAYACDDCAAA9xcAIMQIAACvGAAgxQgAADsAIMYIAAA7ACDHCAAAPQAgBxIAAPQXACATAACtGAAgwwgAAPUXACDECAAArBgAIMUIAABGACDGCAAARgAgxwgAAEgAIAcSAADyFwAgEwAAqhgAIMMIAADzFwAgxAgAAKkYACDFCAAASgAgxggAAEoAIMcIAABMACAHEgAA8BcAIBMAAKcYACDDCAAA8RcAIMQIAACmGAAgxQgAAE4AIMYIAABOACDHCAAA3wEAIAcSAADuFwAgEwAApBgAIMMIAADvFwAgxAgAAKMYACDFCAAARgAgxggAAEYAIMcIAABIACAHEgAA7BcAIBMAAKEYACDDCAAA7RcAIMQIAACgGAAgxQgAAE4AIMYIAABOACDHCAAA3wEAIAcSAADqFwAgEwAAnhgAIMMIAADrFwAgxAgAAJ0YACDFCAAAOwAgxggAADsAIMcIAAA9ACAHEgAA6BcAIBMAAJsYACDDCAAA6RcAIMQIAACaGAAgxQgAADsAIMYIAAA7ACDHCAAAPQAgKTYAAKINACBWAACjDQAgVwAApA0AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAAB1AYCAAAAAdUGAQAAAAHsBiAAAAAB7QYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAd8HCAAAAAHgBwEAAAAB4QcBAAAAAeIHAgAAAAHjBwEAAAAB5AcBAAAAAeUHCAAAAAHmBwgAAAAB5wcIAAAAAegHCAAAAAHpBwgAAAAB6gcIAAAAAesHAgAAAAHsBwIAAAAB7QcBAAAAAe4HCAAAAAHvByAAAAAB8AcgAAAAAfEHIAAAAAHyByAAAAAB8wcgAAAAAfQHIAAAAAH1BwIAAAAB9gcCAAAAAfcHAQAAAAH4B0AAAAABAgAAAH8AIBIAAKENACADAAAAfwAgEgAAoQ0AIBMAAJ0NACABCwAAmRgAMC42AACIDAAgRAAAhwwAIFYAAIkMACBXAACKDAAgrAYAAIYMADCtBgAAfQAQrgYAAIYMADCvBgIAAAABuAZAAPYKACG5BkAA9goAIcUGAQAAAAHIBgEA8woAIdQGAgCXCwAh1QYBAPMKACHsBiAA9AoAIe0GIAD0CgAh-AYBAPMKACH5BgEA8woAIfoGQAD1CgAhtwcCAJcLACHfBwgAgwwAIeAHAQDzCgAh4QcBAPMKACHiBwIAlwsAIeMHAQDzCgAh5AcBAPMKACHlBwgAgwwAIeYHCACDDAAh5wcIAIMMACHoBwgAgwwAIekHCACDDAAh6gcIAIMMACHrBwIAlAsAIewHAgCUCwAh7QcBAPMKACHuBwgA_QoAIe8HIAD0CgAh8AcgAPQKACHxByAA9AoAIfIHIAD0CgAh8wcgAPQKACH0ByAA9AoAIfUHAgCUCwAh9gcCAJQLACH3BwEA8goAIfgHQAD1CgAhAgAAAH8AIAsAAJ0NACACAAAAmg0AIAsAAJsNACAqrAYAAJkNADCtBgAAmg0AEK4GAACZDQAwrwYCAJQLACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdQGAgCXCwAh1QYBAPMKACHsBiAA9AoAIe0GIAD0CgAh-AYBAPMKACH5BgEA8woAIfoGQAD1CgAhtwcCAJcLACHfBwgAgwwAIeAHAQDzCgAh4QcBAPMKACHiBwIAlwsAIeMHAQDzCgAh5AcBAPMKACHlBwgAgwwAIeYHCACDDAAh5wcIAIMMACHoBwgAgwwAIekHCACDDAAh6gcIAIMMACHrBwIAlAsAIewHAgCUCwAh7QcBAPMKACHuBwgA_QoAIe8HIAD0CgAh8AcgAPQKACHxByAA9AoAIfIHIAD0CgAh8wcgAPQKACH0ByAA9AoAIfUHAgCUCwAh9gcCAJQLACH3BwEA8goAIfgHQAD1CgAhKqwGAACZDQAwrQYAAJoNABCuBgAAmQ0AMK8GAgCUCwAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHUBgIAlwsAIdUGAQDzCgAh7AYgAPQKACHtBiAA9AoAIfgGAQDzCgAh-QYBAPMKACH6BkAA9QoAIbcHAgCXCwAh3wcIAIMMACHgBwEA8woAIeEHAQDzCgAh4gcCAJcLACHjBwEA8woAIeQHAQDzCgAh5QcIAIMMACHmBwgAgwwAIecHCACDDAAh6AcIAIMMACHpBwgAgwwAIeoHCACDDAAh6wcCAJQLACHsBwIAlAsAIe0HAQDzCgAh7gcIAP0KACHvByAA9AoAIfAHIAD0CgAh8QcgAPQKACHyByAA9AoAIfMHIAD0CgAh9AcgAPQKACH1BwIAlAsAIfYHAgCUCwAh9wcBAPIKACH4B0AA9QoAISavBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1AYCAIANACHVBgEAywwAIewGIADMDAAh7QYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACHfBwgA_QwAIeAHAQDLDAAh4QcBAMsMACHiBwIAgA0AIeMHAQDLDAAh5AcBAMsMACHlBwgA_QwAIeYHCAD9DAAh5wcIAP0MACHoBwgA_QwAIekHCAD9DAAh6gcIAP0MACHrBwIAygwAIewHAgDKDAAh7QcBAMsMACHuBwgAnA0AIe8HIADMDAAh8AcgAMwMACHxByAAzAwAIfIHIADMDAAh8wcgAMwMACH0ByAAzAwAIfUHAgDKDAAh9gcCAMoMACH3BwEAyQwAIfgHQADNDAAhBckICAAAAAHNCAgAAAABzggIAAAAAc8ICAAAAAHQCAgAAAABKTYAAJ4NACBWAACfDQAgVwAAoA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHUBgIAgA0AIdUGAQDLDAAh7AYgAMwMACHtBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAId8HCAD9DAAh4AcBAMsMACHhBwEAywwAIeIHAgCADQAh4wcBAMsMACHkBwEAywwAIeUHCAD9DAAh5gcIAP0MACHnBwgA_QwAIegHCAD9DAAh6QcIAP0MACHqBwgA_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_hcAIMMIAAD_FwAgxwgAAJwHACADEgAA_BcAIMMIAAD9FwAgxwgAAOoGACADEgAA-hcAIMMIAAD7FwAgxwgAANEGACADEgAA-BcAIMMIAAD5FwAgxwgAAHMAIAMSAAD2FwAgwwgAAPcXACDHCAAAPQAgAxIAAPQXACDDCAAA9RcAIMcIAABIACADEgAA8hcAIMMIAADzFwAgxwgAAEwAIAMSAADwFwAgwwgAAPEXACDHCAAA3wEAIAMSAADuFwAgwwgAAO8XACDHCAAASAAgAxIAAOwXACDDCAAA7RcAIMcIAADfAQAgAxIAAOoXACDDCAAA6xcAIMcIAAA9ACADEgAA6BcAIMMIAADpFwAgxwgAAD0AIEMzAADLDQAgNAAAug0AIEAAALsNACBCAAC3DQAgRQAAsQ0AIFgAALINACBaAACzDQAgWwAAtA0AIFwAALUNACBdAAC2DQAgXgAAuA0AIF8AALkNACBgAAC8DQAgYQAAvQ0AIGIAAL4NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAeAGAgAAAAHnBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABjAcCAAAAAY8HAgAAAAGfByAAAAABoAcgAAAAAaEHIAAAAAGiByAAAAABowcgAAAAAaQHIAAAAAG4BwEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPBBwEAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAHHBwEAAAAByAcgAAAAAckHIAAAAAHKBwgAAAABzAcAAADMBwLNB0AAAAABzwcAAADPBwLQBwEAAAAB0QcgAAAAAdIHIAAAAAHTByAAAAAB1AcBAAAAAdUHAgAAAAHWBwIAAAAB1wcCAAAAAdgHAgAAAAHZBwIAAAAB2gcCAAAAAdsHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAECAAAAdwAgEgAAyg0AIAMAAAB3ACASAADKDQAgEwAAyA0AIAELAADnFwAwAgAAAHcAIAsAAMgNACACAAAA-AwAIAsAAMcNACAzrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyQ0AIDQAAIsNACBAAACMDQAgQgAAiA0AIEUAAIINACBYAACDDQAgWgAAhA0AIFsAAIUNACBcAACGDQAgXQAAhw0AIF4AAIkNACBfAACKDQAgYAAAjQ0AIGEAAI4NACBiAACPDQAgYwAAkA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh4AYCAIANACHnBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIYwHAgCADQAhjwcCAMoMACGfByAAzAwAIaAHIADMDAAhoQcgAMwMACGiByAAzAwAIaMHIADMDAAhpAcgAMwMACG4BwEAyQwAIbkHAQDLDAAhugcBAMsMACG7BwEAywwAIb0HAAD6DL0HI74HAQDLDAAhwAcAAPsMwAcjwQcBAMsMACHCBwEAywwAIcMHAQDLDAAhxQcAAPwMxQcjxgcBAMsMACHHBwEAywwAIcgHIADMDAAhyQcgAMwMACHKBwgA_QwAIcwHAAD-DMwHIs0HQADNDAAhzwcAAP8Mzwci0AcBAMsMACHRByAAzAwAIdIHIADMDAAh0wcgAMwMACHUBwEAywwAIdUHAgCADQAh1gcCAMoMACHXBwIAygwAIdgHAgDKDAAh2QcCAMoMACHaBwIAgA0AIdsHAgCADQAh3AcCAIANACHdBwIAgA0AId4HQADNDAAhBxIAAOIXACATAADlFwAgwwgAAOMXACDECAAA5BcAIMUIAAA7ACDGCAAAOwAgxwgAAD0AIEMzAADLDQAgNAAAug0AIEAAALsNACBCAAC3DQAgRQAAsQ0AIFgAALINACBaAACzDQAgWwAAtA0AIFwAALUNACBdAAC2DQAgXgAAuA0AIF8AALkNACBgAAC8DQAgYQAAvQ0AIGIAAL4NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAeAGAgAAAAHnBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABjAcCAAAAAY8HAgAAAAGfByAAAAABoAcgAAAAAaEHIAAAAAGiByAAAAABowcgAAAAAaQHIAAAAAG4BwEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPBBwEAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAHHBwEAAAAByAcgAAAAAckHIAAAAAHKBwgAAAABzAcAAADMBwLNB0AAAAABzwcAAADPBwLQBwEAAAAB0QcgAAAAAdIHIAAAAAHTByAAAAAB1AcBAAAAAdUHAgAAAAHWBwIAAAAB1wcCAAAAAdgHAgAAAAHZBwIAAAAB2gcCAAAAAdsHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAEDEgAA4hcAIMMIAADjFwAgxwgAAD0AIEMzAADLDQAgNAAAug0AIDcAAMANACBAAAC7DQAgQgAAtw0AIEUAALENACBYAACyDQAgWgAAsw0AIFsAALQNACBcAAC1DQAgXQAAtg0AIF4AALgNACBfAAC5DQAgYAAAvA0AIGEAAL0NACBiAAC-DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHgBgIAAAAB5wYgAAAAAfgGAQAAAAH6BkAAAAABjAcCAAAAAY8HAgAAAAGfByAAAAABoAcgAAAAAaEHIAAAAAGiByAAAAABowcgAAAAAaQHIAAAAAG4BwEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPBBwEAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAHHBwEAAAAByAcgAAAAAckHIAAAAAHKBwgAAAABzAcAAADMBwLNB0AAAAABzwcAAADPBwLQBwEAAAAB0QcgAAAAAdIHIAAAAAHTByAAAAAB1AcBAAAAAdUHAgAAAAHWBwIAAAAB1wcCAAAAAdgHAgAAAAHZBwIAAAAB2gcCAAAAAdsHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAECAAAAdwAgEgAA1A0AIAMAAAB3ACASAADUDQAgEwAA0w0AIAELAADhFwAwAgAAAHcAIAsAANMNACACAAAA-AwAIAsAANINACAzrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIeAGAgCADQAh5wYgAMwMACH4BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyQ0AIDQAAIsNACA3AACRDQAgQAAAjA0AIEIAAIgNACBFAACCDQAgWAAAgw0AIFoAAIQNACBbAACFDQAgXAAAhg0AIF0AAIcNACBeAACJDQAgXwAAig0AIGAAAI0NACBhAACODQAgYgAAjw0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH6BkAAzQwAIYwHAgCADQAhjwcCAMoMACGfByAAzAwAIaAHIADMDAAhoQcgAMwMACGiByAAzAwAIaMHIADMDAAhpAcgAMwMACG4BwEAyQwAIbkHAQDLDAAhugcBAMsMACG7BwEAywwAIb0HAAD6DL0HI74HAQDLDAAhwAcAAPsMwAcjwQcBAMsMACHCBwEAywwAIcMHAQDLDAAhxQcAAPwMxQcjxgcBAMsMACHHBwEAywwAIcgHIADMDAAhyQcgAMwMACHKBwgA_QwAIcwHAAD-DMwHIs0HQADNDAAhzwcAAP8Mzwci0AcBAMsMACHRByAAzAwAIdIHIADMDAAh0wcgAMwMACHUBwEAywwAIdUHAgCADQAh1gcCAMoMACHXBwIAygwAIdgHAgDKDAAh2QcCAMoMACHaBwIAgA0AIdsHAgCADQAh3AcCAIANACHdBwIAgA0AId4HQADNDAAhQzMAAMsNACA0AAC6DQAgNwAAwA0AIEAAALsNACBCAAC3DQAgRQAAsQ0AIFgAALINACBaAACzDQAgWwAAtA0AIFwAALUNACBdAAC2DQAgXgAAuA0AIF8AALkNACBgAAC8DQAgYQAAvQ0AIGIAAL4NACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB1QYBAAAAAeAGAgAAAAHnBiAAAAAB-AYBAAAAAfoGQAAAAAGMBwIAAAABjwcCAAAAAZ8HIAAAAAGgByAAAAABoQcgAAAAAaIHIAAAAAGjByAAAAABpAcgAAAAAbgHAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8EHAQAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAccHAQAAAAHIByAAAAAByQcgAAAAAcoHCAAAAAHMBwAAAMwHAs0HQAAAAAHPBwAAAM8HAtAHAQAAAAHRByAAAAAB0gcgAAAAAdMHIAAAAAHUBwEAAAAB1QcCAAAAAdYHAgAAAAHXBwIAAAAB2AcCAAAAAdkHAgAAAAHaBwIAAAAB2wcCAAAAAdwHAgAAAAHdBwIAAAAB3gdAAAAAAUMzAADLDQAgNAAAug0AIDcAAMANACBAAAC7DQAgQgAAtw0AIEUAALENACBYAACyDQAgWgAAsw0AIFsAALQNACBcAAC1DQAgXQAAtg0AIF4AALgNACBgAAC8DQAgYQAAvQ0AIGIAAL4NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHgBgIAAAAB5wYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAYwHAgAAAAGPBwIAAAABnwcgAAAAAaAHIAAAAAGhByAAAAABogcgAAAAAaMHIAAAAAGkByAAAAABuAcBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwQcBAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABxwcBAAAAAcgHIAAAAAHJByAAAAABygcIAAAAAcwHAAAAzAcCzQdAAAAAAc8HAAAAzwcC0AcBAAAAAdEHIAAAAAHSByAAAAAB0wcgAAAAAdUHAgAAAAHWBwIAAAAB1wcCAAAAAdgHAgAAAAHZBwIAAAAB2gcCAAAAAdsHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAECAAAAdwAgEgAA3Q0AIAMAAAB3ACASAADdDQAgEwAA3A0AIAELAADgFwAwAgAAAHcAIAsAANwNACACAAAA-AwAIAsAANsNACAzrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIeAGAgCADQAh5wYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACGMBwIAgA0AIY8HAgDKDAAhnwcgAMwMACGgByAAzAwAIaEHIADMDAAhogcgAMwMACGjByAAzAwAIaQHIADMDAAhuAcBAMkMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8EHAQDLDAAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAhxwcBAMsMACHIByAAzAwAIckHIADMDAAhygcIAP0MACHMBwAA_gzMByLNB0AAzQwAIc8HAAD_DM8HItAHAQDLDAAh0QcgAMwMACHSByAAzAwAIdMHIADMDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyQ0AIDQAAIsNACA3AACRDQAgQAAAjA0AIEIAAIgNACBFAACCDQAgWAAAgw0AIFoAAIQNACBbAACFDQAgXAAAhg0AIF0AAIcNACBeAACJDQAgYAAAjQ0AIGEAAI4NACBiAACPDQAgYwAAkA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_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_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId4HQADNDAAhQzMAAMkNACA0AACLDQAgNwAAkQ0AIEAAAIwNACBCAACIDQAgRQAAgg0AIFgAAIMNACBaAACEDQAgWwAAhQ0AIFwAAIYNACBdAACHDQAgXgAAiQ0AIF8AAIoNACBgAACNDQAgYQAAjg0AIGMAAJANACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdUGAQDLDAAh4AYCAIANACHnBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIYwHAgCADQAhjwcCAMoMACGfByAAzAwAIaAHIADMDAAhoQcgAMwMACGiByAAzAwAIaMHIADMDAAhpAcgAMwMACG4BwEAyQwAIbkHAQDLDAAhugcBAMsMACG7BwEAywwAIb0HAAD6DL0HI74HAQDLDAAhwAcAAPsMwAcjwQcBAMsMACHCBwEAywwAIcMHAQDLDAAhxQcAAPwMxQcjxgcBAMsMACHHBwEAywwAIcgHIADMDAAhyQcgAMwMACHKBwgA_QwAIcwHAAD-DMwHIs0HQADNDAAhzwcAAP8Mzwci0AcBAMsMACHRByAAzAwAIdIHIADMDAAh0wcgAMwMACHUBwEAywwAIdUHAgCADQAh1gcCAMoMACHXBwIAygwAIdgHAgDKDAAh2QcCAMoMACHaBwIAgA0AIdsHAgCADQAh3AcCAIANACHeB0AAzQwAIUMzAADLDQAgNAAAug0AIDcAAMANACBAAAC7DQAgQgAAtw0AIEUAALENACBYAACyDQAgWgAAsw0AIFsAALQNACBcAAC1DQAgXQAAtg0AIF4AALgNACBfAAC5DQAgYAAAvA0AIGEAAL0NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHgBgIAAAAB5wYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAYwHAgAAAAGPBwIAAAABnwcgAAAAAaAHIAAAAAGhByAAAAABogcgAAAAAaMHIAAAAAGkByAAAAABuAcBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwQcBAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABxwcBAAAAAcgHIAAAAAHJByAAAAABygcIAAAAAcwHAAAAzAcCzQdAAAAAAc8HAAAAzwcC0AcBAAAAAdEHIAAAAAHSByAAAAAB0wcgAAAAAdQHAQAAAAHVBwIAAAAB1gcCAAAAAdcHAgAAAAHYBwIAAAAB2QcCAAAAAdoHAgAAAAHbBwIAAAAB3AcCAAAAAd4HQAAAAAFDMwAAyw0AIDQAALoNACA3AADADQAgQAAAuw0AIEIAALcNACBFAACxDQAgWAAAsg0AIFoAALMNACBbAAC0DQAgXAAAtQ0AIF0AALYNACBeAAC4DQAgXwAAuQ0AIGEAAL0NACBiAAC-DQAgYwAAvw0AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHVBgEAAAAB4AYCAAAAAecGIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAGMBwIAAAABjwcCAAAAAZ8HIAAAAAGgByAAAAABoQcgAAAAAaIHIAAAAAGjByAAAAABpAcgAAAAAbgHAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8EHAQAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAccHAQAAAAHIByAAAAAByQcgAAAAAcoHCAAAAAHMBwAAAMwHAs0HQAAAAAHPBwAAAM8HAtAHAQAAAAHRByAAAAAB0gcgAAAAAdMHIAAAAAHUBwEAAAAB1QcCAAAAAdYHAgAAAAHXBwIAAAAB2AcCAAAAAdkHAgAAAAHaBwIAAAAB3AcCAAAAAd0HAgAAAAHeB0AAAAABAgAAAHcAIBIAAP8NACADAAAAdwAgEgAA_w0AIBMAAP4NACABCwAA2hcAMAIAAAB3ACALAAD-DQAgAgAAAPgMACALAAD9DQAgM68GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh3AcCAIANACHdBwIAgA0AId4HQADNDAAhQzMAAMkNACA0AACLDQAgNwAAkQ0AIEAAAIwNACBCAACIDQAgRQAAgg0AIFgAAIMNACBaAACEDQAgWwAAhQ0AIFwAAIYNACBdAACHDQAgXgAAiQ0AIF8AAIoNACBhAACODQAgYgAAjw0AIGMAAJANACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdUGAQDLDAAh4AYCAIANACHnBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIYwHAgCADQAhjwcCAMoMACGfByAAzAwAIaAHIADMDAAhoQcgAMwMACGiByAAzAwAIaMHIADMDAAhpAcgAMwMACG4BwEAyQwAIbkHAQDLDAAhugcBAMsMACG7BwEAywwAIb0HAAD6DL0HI74HAQDLDAAhwAcAAPsMwAcjwQcBAMsMACHCBwEAywwAIcMHAQDLDAAhxQcAAPwMxQcjxgcBAMsMACHHBwEAywwAIcgHIADMDAAhyQcgAMwMACHKBwgA_QwAIcwHAAD-DMwHIs0HQADNDAAhzwcAAP8Mzwci0AcBAMsMACHRByAAzAwAIdIHIADMDAAh0wcgAMwMACHUBwEAywwAIdUHAgCADQAh1gcCAMoMACHXBwIAygwAIdgHAgDKDAAh2QcCAMoMACHaBwIAgA0AIdwHAgCADQAh3QcCAIANACHeB0AAzQwAIUMzAADLDQAgNAAAug0AIDcAAMANACBAAAC7DQAgQgAAtw0AIEUAALENACBYAACyDQAgWgAAsw0AIFsAALQNACBcAAC1DQAgXQAAtg0AIF4AALgNACBfAAC5DQAgYQAAvQ0AIGIAAL4NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHgBgIAAAAB5wYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAYwHAgAAAAGPBwIAAAABnwcgAAAAAaAHIAAAAAGhByAAAAABogcgAAAAAaMHIAAAAAGkByAAAAABuAcBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwQcBAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABxwcBAAAAAcgHIAAAAAHJByAAAAABygcIAAAAAcwHAAAAzAcCzQdAAAAAAc8HAAAAzwcC0AcBAAAAAdEHIAAAAAHSByAAAAAB0wcgAAAAAdQHAQAAAAHVBwIAAAAB1gcCAAAAAdcHAgAAAAHYBwIAAAAB2QcCAAAAAdoHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAEqMwEAAAABNAAAvA4AIDgAALUOACA6AAC2DgAgOwAAtw4AIDwAALgOACA9AAC5DgAgPgAAug4AID8AALsOACBAAAC9DgAgQgAAvg4AIEUBAAAAAVgAAMAOACBdAQAAAAFlAAC_DgAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdwGAQAAAAHeBgIAAAAB4AYCAAAAAecGIAAAAAH2BgIAAAAB-QYBAAAAAYQHAQAAAAGFBwIAAAABhgcCAAAAAYgHAAAAiAcDiQcIAAAAAYoHAQAAAAGLBwEAAAABjAcCAAAAAY4HAQAAAAGPBwIAAAABkAcCAAAAAZEHQAAAAAGSB0AAAAABkwdAAAAAAZQHQAAAAAGVBwAAtA4AIJYHAQAAAAECAAAAVAAgEgAAsw4AIAMAAABUACASAACzDgAgEwAAjA4AIAELAADZFwAwLzMBAPIKACE0AADvCwAgOAAAoQwAIDoAAKIMACA7AACjDAAgPAAApAwAID0AAKUMACA-AAD3CgAgPwAA9woAIEAAAJkMACBBAACaDAAgQgAApgwAIEUBAPMKACFYAADqCwAgXQEA8woAIWUAAKcMACCsBgAAnwwAMK0GAABSABCuBgAAnwwAMK8GAgAAAAG4BkAA9goAIbkGQAD2CgAhxQYBAAAAAdwGAQDyCgAh3gYCAJQLACHgBgIAlwsAIecGIAD0CgAh9gYCAJcLACH5BgEA8woAIYQHAQDzCgAhhQcCAJcLACGGBwIAlwsAIYgHAACgDIgHI4kHCACDDAAhigcBAPMKACGLBwEA8woAIYwHAgCXCwAhjQcCAJcLACGOBwEA8woAIY8HAgCXCwAhkAcCAJcLACGRB0AA9QoAIZIHQAD1CgAhkwdAAPUKACGUB0AA9QoAIZUHAACeCwAglgcBAPIKACECAAAAVAAgCwAAjA4AIAIAAACIDgAgCwAAiQ4AICIzAQDyCgAhRQEA8woAIV0BAPMKACGsBgAAhw4AMK0GAACIDgAQrgYAAIcOADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIdwGAQDyCgAh3gYCAJQLACHgBgIAlwsAIecGIAD0CgAh9gYCAJcLACH5BgEA8woAIYQHAQDzCgAhhQcCAJcLACGGBwIAlwsAIYgHAACgDIgHI4kHCACDDAAhigcBAPMKACGLBwEA8woAIYwHAgCXCwAhjQcCAJcLACGOBwEA8woAIY8HAgCXCwAhkAcCAJcLACGRB0AA9QoAIZIHQAD1CgAhkwdAAPUKACGUB0AA9QoAIZUHAACeCwAglgcBAPIKACEiMwEA8goAIUUBAPMKACFdAQDzCgAhrAYAAIcOADCtBgAAiA4AEK4GAACHDgAwrwYCAJQLACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHcBgEA8goAId4GAgCUCwAh4AYCAJcLACHnBiAA9AoAIfYGAgCXCwAh-QYBAPMKACGEBwEA8woAIYUHAgCXCwAhhgcCAJcLACGIBwAAoAyIByOJBwgAgwwAIYoHAQDzCgAhiwcBAPMKACGMBwIAlwsAIY0HAgCXCwAhjgcBAPMKACGPBwIAlwsAIZAHAgCXCwAhkQdAAPUKACGSB0AA9QoAIZMHQAD1CgAhlAdAAPUKACGVBwAAngsAIJYHAQDyCgAhHjMBAMkMACFFAQDLDAAhXQEAywwAIa8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHeBgIAygwAIeAGAgCADQAh5wYgAMwMACH2BgIAgA0AIfkGAQDLDAAhhAcBAMsMACGFBwIAgA0AIYYHAgCADQAhiAcAAIoOiAcjiQcIAP0MACGKBwEAywwAIYsHAQDLDAAhjAcCAIANACGOBwEAywwAIY8HAgCADQAhkAcCAIANACGRB0AAzQwAIZIHQADNDAAhkwdAAM0MACGUB0AAzQwAIZUHAACLDgAglgcBAMkMACEByQgAAACIBwMCyQgCAAAABMwIAgAAAAUqMwEAyQwAITQAAJQOACA4AACNDgAgOgAAjg4AIDsAAI8OACA8AACQDgAgPQAAkQ4AID4AAJIOACA_AACTDgAgQAAAlQ4AIEIAAJYOACBFAQDLDAAhWAAAmA4AIF0BAMsMACFlAACXDgAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAId4GAgDKDAAh4AYCAIANACHnBiAAzAwAIfYGAgCADQAh-QYBAMsMACGEBwEAywwAIYUHAgCADQAhhgcCAIANACGIBwAAig6IByOJBwgA_QwAIYoHAQDLDAAhiwcBAMsMACGMBwIAgA0AIY4HAQDLDAAhjwcCAIANACGQBwIAgA0AIZEHQADNDAAhkgdAAM0MACGTB0AAzQwAIZQHQADNDAAhlQcAAIsOACCWBwEAyQwAIQsSAACkDgAwEwAAqQ4AMMMIAAClDgAwxAgAAKYOADDFCAAAqA4AMMYIAACoDgAwxwgAAKgOADDICAAApw4AIMkIAACoDgAwyggAAKoOADDLCAAAqw4AMAUSAACtFwAgEwAA1xcAIMMIAACuFwAgxAgAANYXACDHCAAA_wcAIAcSAACrFwAgEwAA1BcAIMMIAACsFwAgxAgAANMXACDFCAAAXQAgxggAAF0AIMcIAADnBwAgBxIAAKkXACATAADRFwAgwwgAAKoXACDECAAA0BcAIMUIAABhACDGCAAAYQAgxwgAAJgIACAHEgAApxcAIBMAAM4XACDDCAAAqBcAIMQIAADNFwAgxQgAAGUAIMYIAABlACDHCAAArAoAIAcSAAClFwAgEwAAyxcAIMMIAACmFwAgxAgAAMoXACDFCAAAOwAgxggAADsAIMcIAAA9ACAHEgAAoxcAIBMAAMgXACDDCAAApBcAIMQIAADHFwAgxQgAADsAIMYIAAA7ACDHCAAAPQAgBxIAAKEXACATAADFFwAgwwgAAKIXACDECAAAxBcAIMUIAABGACDGCAAARgAgxwgAAEgAIAcSAACfFwAgEwAAwhcAIMMIAACgFwAgxAgAAMEXACDFCAAASgAgxggAAEoAIMcIAABMACAHEgAAnRcAIBMAAL8XACDDCAAAnhcAIMQIAAC-FwAgxQgAAG4AIMYIAABuACDHCAAA2gIAIAcSAACbFwAgEwAAvBcAIMMIAACcFwAgxAgAALsXACDFCAAAzAEAIMYIAADMAQAgxwgAAPICACALEgAAmQ4AMBMAAJ0OADDDCAAAmg4AMMQIAACbDgAwxQgAAJYNADDGCAAAlg0AMMcIAACWDQAwyAgAAJwOACDJCAAAlg0AMMoIAACeDgAwywgAAJkNADApRAAAow4AIFYAAKMNACBXAACkDQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHVBgEAAAAB7AYgAAAAAe0GIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAG3BwIAAAAB3wcIAAAAAeAHAQAAAAHhBwEAAAAB4gcCAAAAAeMHAQAAAAHkBwEAAAAB5QcIAAAAAeYHCAAAAAHnBwgAAAAB6AcIAAAAAekHCAAAAAHqBwgAAAAB6wcCAAAAAewHAgAAAAHtBwEAAAAB7gcIAAAAAe8HIAAAAAHwByAAAAAB8QcgAAAAAfIHIAAAAAHzByAAAAAB9AcgAAAAAfUHAgAAAAH2BwIAAAAB9wcBAAAAAfgHQAAAAAECAAAAfwAgEgAAog4AIAMAAAB_ACASAACiDgAgEwAAoA4AIAELAAC6FwAwAgAAAH8AIAsAAKAOACACAAAAmg0AIAsAAJ8OACAmrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdUGAQDLDAAh7AYgAMwMACHtBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIbcHAgCADQAh3wcIAP0MACHgBwEAywwAIeEHAQDLDAAh4gcCAIANACHjBwEAywwAIeQHAQDLDAAh5QcIAP0MACHmBwgA_QwAIecHCAD9DAAh6AcIAP0MACHpBwgA_QwAIeoHCAD9DAAh6wcCAMoMACHsBwIAygwAIe0HAQDLDAAh7gcIAJwNACHvByAAzAwAIfAHIADMDAAh8QcgAMwMACHyByAAzAwAIfMHIADMDAAh9AcgAMwMACH1BwIAygwAIfYHAgDKDAAh9wcBAMkMACH4B0AAzQwAISlEAAChDgAgVgAAnw0AIFcAAKANACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1QYBAMsMACHsBiAAzAwAIe0GIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhtwcCAIANACHfBwgA_QwAIeAHAQDLDAAh4QcBAMsMACHiBwIAgA0AIeMHAQDLDAAh5AcBAMsMACHlBwgA_QwAIeYHCAD9DAAh5wcIAP0MACHoBwgA_QwAIekHCAD9DAAh6gcIAP0MACHrBwIAygwAIewHAgDKDAAh7QcBAMsMACHuBwgAnA0AIe8HIADMDAAh8AcgAMwMACHxByAAzAwAIfIHIADMDAAh8wcgAMwMACH0ByAAzAwAIfUHAgDKDAAh9gcCAMoMACH3BwEAyQwAIfgHQADNDAAhBxIAALUXACATAAC4FwAgwwgAALYXACDECAAAtxcAIMUIAAB1ACDGCAAAdQAgxwgAAHcAIClEAACjDgAgVgAAow0AIFcAAKQNACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAdUGAQAAAAHsBiAAAAAB7QYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAbcHAgAAAAHfBwgAAAAB4AcBAAAAAeEHAQAAAAHiBwIAAAAB4wcBAAAAAeQHAQAAAAHlBwgAAAAB5gcIAAAAAecHCAAAAAHoBwgAAAAB6QcIAAAAAeoHCAAAAAHrBwIAAAAB7AcCAAAAAe0HAQAAAAHuBwgAAAAB7wcgAAAAAfAHIAAAAAHxByAAAAAB8gcgAAAAAfMHIAAAAAH0ByAAAAAB9QcCAAAAAfYHAgAAAAH3BwEAAAAB-AdAAAAAAQMSAAC1FwAgwwgAALYXACDHCAAAdwAgCzcAALIOACCvBgEAAAABuAZAAAAAAbkGQAAAAAHOBoAAAAABzwaAAAAAAdAGAQAAAAHRBoAAAAAB0gYAAQAAAdMGAQAAAAHVBgEAAAABAgAAAFgAIBIAALEOACADAAAAWAAgEgAAsQ4AIBMAAK8OACABCwAAtBcAMBA2AACeDAAgNwAA9woAIKwGAACcDAAwrQYAAFYAEK4GAACcDAAwrwYBAAAAAbgGQAD2CgAhuQZAAPYKACHOBgAAiQsAIM8GAACJCwAg0AYBAAAAAdEGAACJCwAg0gYAAZ0MACHTBgEA8goAIdQGAQDyCgAh1QYBAPMKACECAAAAWAAgCwAArw4AIAIAAACsDgAgCwAArQ4AIA6sBgAAqw4AMK0GAACsDgAQrgYAAKsOADCvBgEA8goAIbgGQAD2CgAhuQZAAPYKACHOBgAAiQsAIM8GAACJCwAg0AYBAPIKACHRBgAAiQsAINIGAAGdDAAh0wYBAPIKACHUBgEA8goAIdUGAQDzCgAhDqwGAACrDgAwrQYAAKwOABCuBgAAqw4AMK8GAQDyCgAhuAZAAPYKACG5BkAA9goAIc4GAACJCwAgzwYAAIkLACDQBgEA8goAIdEGAACJCwAg0gYAAZ0MACHTBgEA8goAIdQGAQDyCgAh1QYBAPMKACEKrwYBAMkMACG4BkAAzgwAIbkGQADODAAhzgaAAAAAAc8GgAAAAAHQBgEAyQwAIdEGgAAAAAHSBgABrg4AIdMGAQDJDAAh1QYBAMsMACEByQgAAQAAAQs3AACwDgAgrwYBAMkMACG4BkAAzgwAIbkGQADODAAhzgaAAAAAAc8GgAAAAAHQBgEAyQwAIdEGgAAAAAHSBgABrg4AIdMGAQDJDAAh1QYBAMsMACEHEgAArxcAIBMAALIXACDDCAAAsBcAIMQIAACxFwAgxQgAADsAIMYIAAA7ACDHCAAAPQAgCzcAALIOACCvBgEAAAABuAZAAAAAAbkGQAAAAAHOBoAAAAABzwaAAAAAAdAGAQAAAAHRBoAAAAAB0gYAAQAAAdMGAQAAAAHVBgEAAAABAxIAAK8XACDDCAAAsBcAIMcIAAA9ACAqMwEAAAABNAAAvA4AIDgAALUOACA6AAC2DgAgOwAAtw4AIDwAALgOACA9AAC5DgAgPgAAug4AID8AALsOACBAAAC9DgAgQgAAvg4AIEUBAAAAAVgAAMAOACBdAQAAAAFlAAC_DgAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdwGAQAAAAHeBgIAAAAB4AYCAAAAAecGIAAAAAH2BgIAAAAB-QYBAAAAAYQHAQAAAAGFBwIAAAABhgcCAAAAAYgHAAAAiAcDiQcIAAAAAYoHAQAAAAGLBwEAAAABjAcCAAAAAY4HAQAAAAGPBwIAAAABkAcCAAAAAZEHQAAAAAGSB0AAAAABkwdAAAAAAZQHQAAAAAGVBwAAtA4AIJYHAQAAAAEByQgCAAAABAQSAACkDgAwwwgAAKUOADDHCAAAqA4AMMgIAACnDgAgAxIAAK0XACDDCAAArhcAIMcIAAD_BwAgAxIAAKsXACDDCAAArBcAIMcIAADnBwAgAxIAAKkXACDDCAAAqhcAIMcIAACYCAAgAxIAAKcXACDDCAAAqBcAIMcIAACsCgAgAxIAAKUXACDDCAAAphcAIMcIAAA9ACADEgAAoxcAIMMIAACkFwAgxwgAAD0AIAMSAAChFwAgwwgAAKIXACDHCAAASAAgAxIAAJ8XACDDCAAAoBcAIMcIAABMACADEgAAnRcAIMMIAACeFwAgxwgAANoCACADEgAAmxcAIMMIAACcFwAgxwgAAPICACAEEgAAmQ4AMMMIAACaDgAwxwgAAJYNADDICAAAnA4AIBU0AADlDgAgOQAA5g4AIFkAAOcOACCvBgIAAAABsgYBAAAAAbMGAQAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHgBgIAAAAB5gYCAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABkQggAAAAAZIIIAAAAAGTCAEAAAABlAgBAAAAAZYIAAAAlggDlwhAAAAAAQIAAABMACASAADkDgAgAwAAAEwAIBIAAOQOACATAADMDgAgAQsAAJoXADAaNAAA7wsAIDkAAP4KACBZAACsCwAgYAAAmgwAIKwGAACoDAAwrQYAAEoAEK4GAACoDAAwrwYCAAAAAbIGAQDyCgAhswYBAPIKACG4BkAA9goAIbkGQAD2CgAhxQYBAAAAAeAGAgCXCwAh5gYCAJcLACHCBwEA8woAIcMHAQDzCgAhxQcAAI8MxQcjxgcBAPMKACHbBwIAlwsAIZEIIAD0CgAhkgggAPQKACGTCAEA8woAIZQIAQDzCgAhlggAAKkMlggjlwhAAPUKACECAAAATAAgCwAAzA4AIAIAAADJDgAgCwAAyg4AIBasBgAAyA4AMK0GAADJDgAQrgYAAMgOADCvBgIAlAsAIbIGAQDyCgAhswYBAPIKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHgBgIAlwsAIeYGAgCXCwAhwgcBAPMKACHDBwEA8woAIcUHAACPDMUHI8YHAQDzCgAh2wcCAJcLACGRCCAA9AoAIZIIIAD0CgAhkwgBAPMKACGUCAEA8woAIZYIAACpDJYII5cIQAD1CgAhFqwGAADIDgAwrQYAAMkOABCuBgAAyA4AMK8GAgCUCwAhsgYBAPIKACGzBgEA8goAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIeAGAgCXCwAh5gYCAJcLACHCBwEA8woAIcMHAQDzCgAhxQcAAI8MxQcjxgcBAPMKACHbBwIAlwsAIZEIIAD0CgAhkgggAPQKACGTCAEA8woAIZQIAQDzCgAhlggAAKkMlggjlwhAAPUKACESrwYCAMoMACGyBgEAyQwAIbMGAQDJDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh4AYCAIANACHmBgIAgA0AIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIZEIIADMDAAhkgggAMwMACGTCAEAywwAIZQIAQDLDAAhlggAAMsOlggjlwhAAM0MACEByQgAAACWCAMVNAAAzQ4AIDkAAM4OACBZAADPDgAgrwYCAMoMACGyBgEAyQwAIbMGAQDJDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh4AYCAIANACHmBgIAgA0AIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIZEIIADMDAAhkgggAMwMACGTCAEAywwAIZQIAQDLDAAhlggAAMsOlggjlwhAAM0MACEHEgAAjhcAIBMAAJgXACDDCAAAjxcAIMQIAACXFwAgxQgAAEYAIMYIAABGACDHCAAASAAgCxIAANkOADATAADdDgAwwwgAANoOADDECAAA2w4AMMUIAACEDgAwxggAAIQOADDHCAAAhA4AMMgIAADcDgAgyQgAAIQOADDKCAAA3g4AMMsIAACHDgAwCxIAANAOADATAADUDgAwwwgAANEOADDECAAA0g4AMMUIAAD0DAAwxggAAPQMADDHCAAA9AwAMMgIAADTDgAgyQgAAPQMADDKCAAA1Q4AMMsIAAD3DAAwQzMAAMsNACA0AAC6DQAgNwAAwA0AIEIAALcNACBFAACxDQAgWAAAsg0AIFoAALMNACBbAAC0DQAgXAAAtQ0AIF0AALYNACBeAAC4DQAgXwAAuQ0AIGAAALwNACBhAAC9DQAgYgAAvg0AIGMAAL8NACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB1QYBAAAAAeAGAgAAAAHnBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABjwcCAAAAAZ8HIAAAAAGgByAAAAABoQcgAAAAAaIHIAAAAAGjByAAAAABpAcgAAAAAbgHAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8EHAQAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAccHAQAAAAHIByAAAAAByQcgAAAAAcoHCAAAAAHMBwAAAMwHAs0HQAAAAAHPBwAAAM8HAtAHAQAAAAHRByAAAAAB0gcgAAAAAdMHIAAAAAHUBwEAAAAB1QcCAAAAAdYHAgAAAAHXBwIAAAAB2AcCAAAAAdkHAgAAAAHaBwIAAAAB2wcCAAAAAdwHAgAAAAHdBwIAAAAB3gdAAAAAAQIAAAB3ACASAADYDgAgAwAAAHcAIBIAANgOACATAADXDgAgAQsAAJYXADACAAAAdwAgCwAA1w4AIAIAAAD4DAAgCwAA1g4AIDOvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdUGAQDLDAAh4AYCAIANACHnBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIY8HAgDKDAAhnwcgAMwMACGgByAAzAwAIaEHIADMDAAhogcgAMwMACGjByAAzAwAIaQHIADMDAAhuAcBAMkMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8EHAQDLDAAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAhxwcBAMsMACHIByAAzAwAIckHIADMDAAhygcIAP0MACHMBwAA_gzMByLNB0AAzQwAIc8HAAD_DM8HItAHAQDLDAAh0QcgAMwMACHSByAAzAwAIdMHIADMDAAh1AcBAMsMACHVBwIAgA0AIdYHAgDKDAAh1wcCAMoMACHYBwIAygwAIdkHAgDKDAAh2gcCAIANACHbBwIAgA0AIdwHAgCADQAh3QcCAIANACHeB0AAzQwAIUMzAADJDQAgNAAAiw0AIDcAAJENACBCAACIDQAgRQAAgg0AIFgAAIMNACBaAACEDQAgWwAAhQ0AIFwAAIYNACBdAACHDQAgXgAAiQ0AIF8AAIoNACBgAACNDQAgYQAAjg0AIGIAAI8NACBjAACQDQAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIeAGAgCADQAh5wYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyw0AIDQAALoNACA3AADADQAgQgAAtw0AIEUAALENACBYAACyDQAgWgAAsw0AIFsAALQNACBcAAC1DQAgXQAAtg0AIF4AALgNACBfAAC5DQAgYAAAvA0AIGEAAL0NACBiAAC-DQAgYwAAvw0AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHVBgEAAAAB4AYCAAAAAecGIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAGPBwIAAAABnwcgAAAAAaAHIAAAAAGhByAAAAABogcgAAAAAaMHIAAAAAGkByAAAAABuAcBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwQcBAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABxwcBAAAAAcgHIAAAAAHJByAAAAABygcIAAAAAcwHAAAAzAcCzQdAAAAAAc8HAAAAzwcC0AcBAAAAAdEHIAAAAAHSByAAAAAB0wcgAAAAAdQHAQAAAAHVBwIAAAAB1gcCAAAAAdcHAgAAAAHYBwIAAAAB2QcCAAAAAdoHAgAAAAHbBwIAAAAB3AcCAAAAAd0HAgAAAAHeB0AAAAABKjMBAAAAATQAALwOACA4AAC1DgAgOgAAtg4AIDsAALcOACA8AAC4DgAgPQAAuQ4AID4AALoOACA_AAC7DgAgQQAA4w4AIEIAAL4OACBFAQAAAAFYAADADgAgXQEAAAABZQAAvw4AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHcBgEAAAAB3gYCAAAAAeAGAgAAAAHnBiAAAAAB9gYCAAAAAfkGAQAAAAGEBwEAAAABhQcCAAAAAYYHAgAAAAGIBwAAAIgHA4kHCAAAAAGKBwEAAAABiwcBAAAAAY0HAgAAAAGOBwEAAAABjwcCAAAAAZAHAgAAAAGRB0AAAAABkgdAAAAAAZMHQAAAAAGUB0AAAAABlQcAALQOACCWBwEAAAABAgAAAFQAIBIAAOIOACADAAAAVAAgEgAA4g4AIBMAAOAOACABCwAAlRcAMAIAAABUACALAADgDgAgAgAAAIgOACALAADfDgAgHjMBAMkMACFFAQDLDAAhXQEAywwAIa8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHeBgIAygwAIeAGAgCADQAh5wYgAMwMACH2BgIAgA0AIfkGAQDLDAAhhAcBAMsMACGFBwIAgA0AIYYHAgCADQAhiAcAAIoOiAcjiQcIAP0MACGKBwEAywwAIYsHAQDLDAAhjQcCAIANACGOBwEAywwAIY8HAgCADQAhkAcCAIANACGRB0AAzQwAIZIHQADNDAAhkwdAAM0MACGUB0AAzQwAIZUHAACLDgAglgcBAMkMACEqMwEAyQwAITQAAJQOACA4AACNDgAgOgAAjg4AIDsAAI8OACA8AACQDgAgPQAAkQ4AID4AAJIOACA_AACTDgAgQQAA4Q4AIEIAAJYOACBFAQDLDAAhWAAAmA4AIF0BAMsMACFlAACXDgAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAId4GAgDKDAAh4AYCAIANACHnBiAAzAwAIfYGAgCADQAh-QYBAMsMACGEBwEAywwAIYUHAgCADQAhhgcCAIANACGIBwAAig6IByOJBwgA_QwAIYoHAQDLDAAhiwcBAMsMACGNBwIAgA0AIY4HAQDLDAAhjwcCAIANACGQBwIAgA0AIZEHQADNDAAhkgdAAM0MACGTB0AAzQwAIZQHQADNDAAhlQcAAIsOACCWBwEAyQwAIQcSAACQFwAgEwAAkxcAIMMIAACRFwAgxAgAAJIXACDFCAAATgAgxggAAE4AIMcIAADfAQAgKjMBAAAAATQAALwOACA4AAC1DgAgOgAAtg4AIDsAALcOACA8AAC4DgAgPQAAuQ4AID4AALoOACA_AAC7DgAgQQAA4w4AIEIAAL4OACBFAQAAAAFYAADADgAgXQEAAAABZQAAvw4AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHcBgEAAAAB3gYCAAAAAeAGAgAAAAHnBiAAAAAB9gYCAAAAAfkGAQAAAAGEBwEAAAABhQcCAAAAAYYHAgAAAAGIBwAAAIgHA4kHCAAAAAGKBwEAAAABiwcBAAAAAY0HAgAAAAGOBwEAAAABjwcCAAAAAZAHAgAAAAGRB0AAAAABkgdAAAAAAZMHQAAAAAGUB0AAAAABlQcAALQOACCWBwEAAAABAxIAAJAXACDDCAAAkRcAIMcIAADfAQAgFTQAAOUOACA5AADmDgAgWQAA5w4AIK8GAgAAAAGyBgEAAAABswYBAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAeAGAgAAAAHmBgIAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAGRCCAAAAABkgggAAAAAZMIAQAAAAGUCAEAAAABlggAAACWCAOXCEAAAAABAxIAAI4XACDDCAAAjxcAIMcIAABIACAEEgAA2Q4AMMMIAADaDgAwxwgAAIQOADDICAAA3A4AIAQSAADQDgAwwwgAANEOADDHCAAA9AwAMMgIAADTDgAgGTQAAOkOACA1AADqDgAgWQAA7A4AIGYAAOsOACBnAADtDgAgrwYCAAAAAbUGIAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAABygYgAAAAAdwGAQAAAAHgBgIAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPCBwEAAAABmAgBAAAAAZkIIAAAAAGaCCAAAAABmwggAAAAAQMSAACMFwAgwwgAAI0XACDHCAAASAAgBBIAAMEOADDDCAAAwg4AMMcIAADFDgAwyAgAAMQOACAEEgAAgA4AMMMIAACBDgAwxwgAAIQOADDICAAAgw4AIAQSAAD3DQAwwwgAAPgNADDHCAAA9AwAMMgIAAD6DQAgBBIAAO4NADDDCAAA7w0AMMcIAAD0DAAwyAgAAPENACALNgAA-A4AIK8GAQAAAAG4BkAAAAABuQZAAAAAAc4GgAAAAAHPBoAAAAAB0AYBAAAAAdEGgAAAAAHSBgABAAAB0wYBAAAAAdQGAQAAAAECAAAAWAAgEgAA9w4AIAMAAABYACASAAD3DgAgEwAA9Q4AIAELAACLFwAwAgAAAFgAIAsAAPUOACACAAAArA4AIAsAAPQOACAKrwYBAMkMACG4BkAAzgwAIbkGQADODAAhzgaAAAAAAc8GgAAAAAHQBgEAyQwAIdEGgAAAAAHSBgABrg4AIdMGAQDJDAAh1AYBAMkMACELNgAA9g4AIK8GAQDJDAAhuAZAAM4MACG5BkAAzgwAIc4GgAAAAAHPBoAAAAAB0AYBAMkMACHRBoAAAAAB0gYAAa4OACHTBgEAyQwAIdQGAQDJDAAhBRIAAIYXACATAACJFwAgwwgAAIcXACDECAAAiBcAIMcIAABUACALNgAA-A4AIK8GAQAAAAG4BkAAAAABuQZAAAAAAc4GgAAAAAHPBoAAAAAB0AYBAAAAAdEGgAAAAAHSBgABAAAB0wYBAAAAAdQGAQAAAAEDEgAAhhcAIMMIAACHFwAgxwgAAFQAICozAQAAAAE0AAC8DgAgOAAAtQ4AIDoAALYOACA7AAC3DgAgPAAAuA4AID0AALkOACA-AAC6DgAgQAAAvQ4AIEEAAOMOACBCAAC-DgAgRQEAAAABWAAAwA4AIF0BAAAAAWUAAL8OACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAd4GAgAAAAHgBgIAAAAB5wYgAAAAAfYGAgAAAAH5BgEAAAABhAcBAAAAAYUHAgAAAAGGBwIAAAABiAcAAACIBwOJBwgAAAABigcBAAAAAYwHAgAAAAGNBwIAAAABjgcBAAAAAY8HAgAAAAGQBwIAAAABkQdAAAAAAZIHQAAAAAGTB0AAAAABlAdAAAAAAZUHAAC0DgAglgcBAAAAAQIAAABUACASAACBDwAgAwAAAFQAIBIAAIEPACATAACADwAgAQsAAIUXADACAAAAVAAgCwAAgA8AIAIAAACIDgAgCwAA_w4AIB4zAQDJDAAhRQEAywwAIV0BAMsMACGvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdwGAQDJDAAh3gYCAMoMACHgBgIAgA0AIecGIADMDAAh9gYCAIANACH5BgEAywwAIYQHAQDLDAAhhQcCAIANACGGBwIAgA0AIYgHAACKDogHI4kHCAD9DAAhigcBAMsMACGMBwIAgA0AIY0HAgCADQAhjgcBAMsMACGPBwIAgA0AIZAHAgCADQAhkQdAAM0MACGSB0AAzQwAIZMHQADNDAAhlAdAAM0MACGVBwAAiw4AIJYHAQDJDAAhKjMBAMkMACE0AACUDgAgOAAAjQ4AIDoAAI4OACA7AACPDgAgPAAAkA4AID0AAJEOACA-AACSDgAgQAAAlQ4AIEEAAOEOACBCAACWDgAgRQEAywwAIVgAAJgOACBdAQDLDAAhZQAAlw4AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHeBgIAygwAIeAGAgCADQAh5wYgAMwMACH2BgIAgA0AIfkGAQDLDAAhhAcBAMsMACGFBwIAgA0AIYYHAgCADQAhiAcAAIoOiAcjiQcIAP0MACGKBwEAywwAIYwHAgCADQAhjQcCAIANACGOBwEAywwAIY8HAgCADQAhkAcCAIANACGRB0AAzQwAIZIHQADNDAAhkwdAAM0MACGUB0AAzQwAIZUHAACLDgAglgcBAMkMACEqMwEAAAABNAAAvA4AIDgAALUOACA6AAC2DgAgOwAAtw4AIDwAALgOACA9AAC5DgAgPgAAug4AIEAAAL0OACBBAADjDgAgQgAAvg4AIEUBAAAAAVgAAMAOACBdAQAAAAFlAAC_DgAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdwGAQAAAAHeBgIAAAAB4AYCAAAAAecGIAAAAAH2BgIAAAAB-QYBAAAAAYQHAQAAAAGFBwIAAAABhgcCAAAAAYgHAAAAiAcDiQcIAAAAAYoHAQAAAAGMBwIAAAABjQcCAAAAAY4HAQAAAAGPBwIAAAABkAcCAAAAAZEHQAAAAAGSB0AAAAABkwdAAAAAAZQHQAAAAAGVBwAAtA4AIJYHAQAAAAEqMwEAAAABNAAAvA4AIDgAALUOACA6AAC2DgAgOwAAtw4AIDwAALgOACA9AAC5DgAgPwAAuw4AIEAAAL0OACBBAADjDgAgQgAAvg4AIEUBAAAAAVgAAMAOACBdAQAAAAFlAAC_DgAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdwGAQAAAAHeBgIAAAAB4AYCAAAAAecGIAAAAAH2BgIAAAAB-QYBAAAAAYQHAQAAAAGFBwIAAAABhgcCAAAAAYgHAAAAiAcDiQcIAAAAAYsHAQAAAAGMBwIAAAABjQcCAAAAAY4HAQAAAAGPBwIAAAABkAcCAAAAAZEHQAAAAAGSB0AAAAABkwdAAAAAAZQHQAAAAAGVBwAAtA4AIJYHAQAAAAECAAAAVAAgEgAAig8AIAMAAABUACASAACKDwAgEwAAiQ8AIAELAACEFwAwAgAAAFQAIAsAAIkPACACAAAAiA4AIAsAAIgPACAeMwEAyQwAIUUBAMsMACFdAQDLDAAhrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAId4GAgDKDAAh4AYCAIANACHnBiAAzAwAIfYGAgCADQAh-QYBAMsMACGEBwEAywwAIYUHAgCADQAhhgcCAIANACGIBwAAig6IByOJBwgA_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_DwAwwwgAAPwPADDECAAA_Q8AMMUIAADFDgAwxggAAMUOADDHCAAAxQ4AMMgIAAD-DwAgyQgAAMUOADDKCAAAgBAAMMsIAADIDgAwCxIAAPAPADATAAD0DwAwwwgAAPEPADDECAAA8g8AMMUIAADiDQAwxggAAOINADDHCAAA4g0AMMgIAADzDwAgyQgAAOINADDKCAAA9Q8AMMsIAADlDQAwCxIAAMUPADATAADKDwAwwwgAAMYPADDECAAAxw8AMMUIAADJDwAwxggAAMkPADDHCAAAyQ8AMMgIAADIDwAgyQgAAMkPADDKCAAAyw8AMMsIAADMDwAwCxIAALkPADATAAC-DwAwwwgAALoPADDECAAAuw8AMMUIAAC9DwAwxggAAL0PADDHCAAAvQ8AMMgIAAC8DwAgyQgAAL0PADDKCAAAvw8AMMsIAADADwAwCxIAALAPADATAAC0DwAwwwgAALEPADDECAAAsg8AMMUIAACEDgAwxggAAIQOADDHCAAAhA4AMMgIAACzDwAgyQgAAIQOADDKCAAAtQ8AMMsIAACHDgAwBxIAANwWACATAACBFwAgwwgAAN0WACDECAAAgBcAIMUIAAA7ACDGCAAAOwAgxwgAAD0AIAsSAACnDwAwEwAAqw8AMMMIAACoDwAwxAgAAKkPADDFCAAA9AwAMMYIAAD0DAAwxwgAAPQMADDICAAAqg8AIMkIAAD0DAAwyggAAKwPADDLCAAA9wwAMAsSAACeDwAwEwAAog8AMMMIAACfDwAwxAgAAKAPADDFCAAA9AwAMMYIAAD0DAAwxwgAAPQMADDICAAAoQ8AIMkIAAD0DAAwyggAAKMPADDLCAAA9wwAMEMzAADLDQAgNAAAug0AIDcAAMANACBAAAC7DQAgQgAAtw0AIEUAALENACBYAACyDQAgWgAAsw0AIFsAALQNACBcAAC1DQAgXQAAtg0AIF4AALgNACBfAAC5DQAgYAAAvA0AIGIAAL4NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHgBgIAAAAB5wYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAYwHAgAAAAGPBwIAAAABnwcgAAAAAaAHIAAAAAGhByAAAAABogcgAAAAAaMHIAAAAAGkByAAAAABuAcBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwQcBAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABxwcBAAAAAcgHIAAAAAHJByAAAAABygcIAAAAAcwHAAAAzAcCzQdAAAAAAc8HAAAAzwcC0AcBAAAAAdEHIAAAAAHSByAAAAAB0wcgAAAAAdQHAQAAAAHVBwIAAAAB1gcCAAAAAdcHAgAAAAHYBwIAAAAB2QcCAAAAAdoHAgAAAAHbBwIAAAAB3QcCAAAAAd4HQAAAAAECAAAAdwAgEgAApg8AIAMAAAB3ACASAACmDwAgEwAApQ8AIAELAAD_FgAwAgAAAHcAIAsAAKUPACACAAAA-AwAIAsAAKQPACAzrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIeAGAgCADQAh5wYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACGMBwIAgA0AIY8HAgDKDAAhnwcgAMwMACGgByAAzAwAIaEHIADMDAAhogcgAMwMACGjByAAzAwAIaQHIADMDAAhuAcBAMkMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8EHAQDLDAAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAhxwcBAMsMACHIByAAzAwAIckHIADMDAAhygcIAP0MACHMBwAA_gzMByLNB0AAzQwAIc8HAAD_DM8HItAHAQDLDAAh0QcgAMwMACHSByAAzAwAIdMHIADMDAAh1AcBAMsMACHVBwIAgA0AIdYHAgDKDAAh1wcCAMoMACHYBwIAygwAIdkHAgDKDAAh2gcCAIANACHbBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyQ0AIDQAAIsNACA3AACRDQAgQAAAjA0AIEIAAIgNACBFAACCDQAgWAAAgw0AIFoAAIQNACBbAACFDQAgXAAAhg0AIF0AAIcNACBeAACJDQAgXwAAig0AIGAAAI0NACBiAACPDQAgYwAAkA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHdBwIAgA0AId4HQADNDAAhQzMAAMsNACA0AAC6DQAgNwAAwA0AIEAAALsNACBCAAC3DQAgRQAAsQ0AIFgAALINACBaAACzDQAgWwAAtA0AIFwAALUNACBdAAC2DQAgXgAAuA0AIF8AALkNACBgAAC8DQAgYgAAvg0AIGMAAL8NACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB1QYBAAAAAeAGAgAAAAHnBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABjAcCAAAAAY8HAgAAAAGfByAAAAABoAcgAAAAAaEHIAAAAAGiByAAAAABowcgAAAAAaQHIAAAAAG4BwEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPBBwEAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAHHBwEAAAAByAcgAAAAAckHIAAAAAHKBwgAAAABzAcAAADMBwLNB0AAAAABzwcAAADPBwLQBwEAAAAB0QcgAAAAAdIHIAAAAAHTByAAAAAB1AcBAAAAAdUHAgAAAAHWBwIAAAAB1wcCAAAAAdgHAgAAAAHZBwIAAAAB2gcCAAAAAdsHAgAAAAHdBwIAAAAB3gdAAAAAAUMzAADLDQAgNwAAwA0AIEAAALsNACBCAAC3DQAgRQAAsQ0AIFgAALINACBaAACzDQAgWwAAtA0AIFwAALUNACBdAAC2DQAgXgAAuA0AIF8AALkNACBgAAC8DQAgYQAAvQ0AIGIAAL4NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHnBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABjAcCAAAAAY8HAgAAAAGfByAAAAABoAcgAAAAAaEHIAAAAAGiByAAAAABowcgAAAAAaQHIAAAAAG4BwEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPBBwEAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAHHBwEAAAAByAcgAAAAAckHIAAAAAHKBwgAAAABzAcAAADMBwLNB0AAAAABzwcAAADPBwLQBwEAAAAB0QcgAAAAAdIHIAAAAAHTByAAAAAB1AcBAAAAAdUHAgAAAAHWBwIAAAAB1wcCAAAAAdgHAgAAAAHZBwIAAAAB2gcCAAAAAdsHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAECAAAAdwAgEgAArw8AIAMAAAB3ACASAACvDwAgEwAArg8AIAELAAD-FgAwAgAAAHcAIAsAAK4PACACAAAA-AwAIAsAAK0PACAzrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyQ0AIDcAAJENACBAAACMDQAgQgAAiA0AIEUAAIINACBYAACDDQAgWgAAhA0AIFsAAIUNACBcAACGDQAgXQAAhw0AIF4AAIkNACBfAACKDQAgYAAAjQ0AIGEAAI4NACBiAACPDQAgYwAAkA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHnBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIYwHAgCADQAhjwcCAMoMACGfByAAzAwAIaAHIADMDAAhoQcgAMwMACGiByAAzAwAIaMHIADMDAAhpAcgAMwMACG4BwEAyQwAIbkHAQDLDAAhugcBAMsMACG7BwEAywwAIb0HAAD6DL0HI74HAQDLDAAhwAcAAPsMwAcjwQcBAMsMACHCBwEAywwAIcMHAQDLDAAhxQcAAPwMxQcjxgcBAMsMACHHBwEAywwAIcgHIADMDAAhyQcgAMwMACHKBwgA_QwAIcwHAAD-DMwHIs0HQADNDAAhzwcAAP8Mzwci0AcBAMsMACHRByAAzAwAIdIHIADMDAAh0wcgAMwMACHUBwEAywwAIdUHAgCADQAh1gcCAMoMACHXBwIAygwAIdgHAgDKDAAh2QcCAMoMACHaBwIAgA0AIdsHAgCADQAh3AcCAIANACHdBwIAgA0AId4HQADNDAAhQzMAAMsNACA3AADADQAgQAAAuw0AIEIAALcNACBFAACxDQAgWAAAsg0AIFoAALMNACBbAAC0DQAgXAAAtQ0AIF0AALYNACBeAAC4DQAgXwAAuQ0AIGAAALwNACBhAAC9DQAgYgAAvg0AIGMAAL8NACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB1QYBAAAAAecGIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAGMBwIAAAABjwcCAAAAAZ8HIAAAAAGgByAAAAABoQcgAAAAAaIHIAAAAAGjByAAAAABpAcgAAAAAbgHAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8EHAQAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAccHAQAAAAHIByAAAAAByQcgAAAAAcoHCAAAAAHMBwAAAMwHAs0HQAAAAAHPBwAAAM8HAtAHAQAAAAHRByAAAAAB0gcgAAAAAdMHIAAAAAHUBwEAAAAB1QcCAAAAAdYHAgAAAAHXBwIAAAAB2AcCAAAAAdkHAgAAAAHaBwIAAAAB2wcCAAAAAdwHAgAAAAHdBwIAAAAB3gdAAAAAASozAQAAAAE4AAC1DgAgOgAAtg4AIDsAALcOACA8AAC4DgAgPQAAuQ4AID4AALoOACA_AAC7DgAgQAAAvQ4AIEEAAOMOACBCAAC-DgAgRQEAAAABWAAAwA4AIF0BAAAAAWUAAL8OACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAd4GAgAAAAHnBiAAAAAB9gYCAAAAAfkGAQAAAAGEBwEAAAABhQcCAAAAAYYHAgAAAAGIBwAAAIgHA4kHCAAAAAGKBwEAAAABiwcBAAAAAYwHAgAAAAGNBwIAAAABjgcBAAAAAY8HAgAAAAGQBwIAAAABkQdAAAAAAZIHQAAAAAGTB0AAAAABlAdAAAAAAZUHAAC0DgAglgcBAAAAAQIAAABUACASAAC4DwAgAwAAAFQAIBIAALgPACATAAC3DwAgAQsAAP0WADACAAAAVAAgCwAAtw8AIAIAAACIDgAgCwAAtg8AIB4zAQDJDAAhRQEAywwAIV0BAMsMACGvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdwGAQDJDAAh3gYCAMoMACHnBiAAzAwAIfYGAgCADQAh-QYBAMsMACGEBwEAywwAIYUHAgCADQAhhgcCAIANACGIBwAAig6IByOJBwgA_QwAIYoHAQDLDAAhiwcBAMsMACGMBwIAgA0AIY0HAgCADQAhjgcBAMsMACGPBwIAgA0AIZAHAgCADQAhkQdAAM0MACGSB0AAzQwAIZMHQADNDAAhlAdAAM0MACGVBwAAiw4AIJYHAQDJDAAhKjMBAMkMACE4AACNDgAgOgAAjg4AIDsAAI8OACA8AACQDgAgPQAAkQ4AID4AAJIOACA_AACTDgAgQAAAlQ4AIEEAAOEOACBCAACWDgAgRQEAywwAIVgAAJgOACBdAQDLDAAhZQAAlw4AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHeBgIAygwAIecGIADMDAAh9gYCAIANACH5BgEAywwAIYQHAQDLDAAhhQcCAIANACGGBwIAgA0AIYgHAACKDogHI4kHCAD9DAAhigcBAMsMACGLBwEAywwAIYwHAgCADQAhjQcCAIANACGOBwEAywwAIY8HAgCADQAhkAcCAIANACGRB0AAzQwAIZIHQADNDAAhkwdAAM0MACGUB0AAzQwAIZUHAACLDgAglgcBAMkMACEqMwEAAAABOAAAtQ4AIDoAALYOACA7AAC3DgAgPAAAuA4AID0AALkOACA-AAC6DgAgPwAAuw4AIEAAAL0OACBBAADjDgAgQgAAvg4AIEUBAAAAAVgAAMAOACBdAQAAAAFlAAC_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-Q8AIAMAAADfAQAgEgAA-Q8AIBMAAPcPACABCwAA6RYAMAIAAADfAQAgCwAA9w8AIAIAAADmDQAgCwAA9g8AIBSvBgIAygwAIbUGIADMDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHKBiAAzAwAIdwGAQDJDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8IHAQDLDAAhmAgBAMsMACGZCCAAzAwAIZoIIADMDAAhmwggAMwMACEZMwAA-A8AIDUAAOoNACBZAADsDQAgZgAA6w0AIGcAAO0NACCvBgIAygwAIbUGIADMDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHKBiAAzAwAIdwGAQDJDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8IHAQDLDAAhmAgBAMsMACGZCCAAzAwAIZoIIADMDAAhmwggAMwMACEHEgAA5BYAIBMAAOcWACDDCAAA5RYAIMQIAADmFgAgxQgAADsAIMYIAAA7ACDHCAAAPQAgGTMAAPoPACA1AADqDgAgWQAA7A4AIGYAAOsOACBnAADtDgAgrwYCAAAAAbUGIAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAABygYgAAAAAdwGAQAAAAH4BgEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPCBwEAAAABmAgBAAAAAZkIIAAAAAGaCCAAAAABmwggAAAAAQMSAADkFgAgwwgAAOUWACDHCAAAPQAgFTkAAOYOACBZAADnDgAgYAAAhRAAIK8GAgAAAAGyBgEAAAABswYBAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAeYGAgAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAdsHAgAAAAGRCCAAAAABkgggAAAAAZMIAQAAAAGUCAEAAAABlggAAACWCAOXCEAAAAABAgAAAEwAIBIAAIQQACADAAAATAAgEgAAhBAAIBMAAIIQACABCwAA4xYAMAIAAABMACALAACCEAAgAgAAAMkOACALAACBEAAgEq8GAgDKDAAhsgYBAMkMACGzBgEAyQwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIeYGAgCADQAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAh2wcCAIANACGRCCAAzAwAIZIIIADMDAAhkwgBAMsMACGUCAEAywwAIZYIAADLDpYII5cIQADNDAAhFTkAAM4OACBZAADPDgAgYAAAgxAAIK8GAgDKDAAhsgYBAMkMACGzBgEAyQwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIeYGAgCADQAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAh2wcCAIANACGRCCAAzAwAIZIIIADMDAAhkwgBAMsMACGUCAEAywwAIZYIAADLDpYII5cIQADNDAAhBxIAAN4WACATAADhFgAgwwgAAN8WACDECAAA4BYAIMUIAABOACDGCAAATgAgxwgAAN8BACAVOQAA5g4AIFkAAOcOACBgAACFEAAgrwYCAAAAAbIGAQAAAAGzBgEAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB5gYCAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAAB2wcCAAAAAZEIIAAAAAGSCCAAAAABkwgBAAAAAZQIAQAAAAGWCAAAAJYIA5cIQAAAAAEDEgAA3hYAIMMIAADfFgAgxwgAAN8BACAVNQAAhxAAIDkAAIsQACBZAACNEAAgaAAAiBAAIGsAAIkQACBuAACKEAAgbwAAjBAAIHEAAI4QACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAcIHAQAAAAGcCAEAAAABnQggAAAAAZ4IQAAAAAGfCAEAAAABoAggAAAAAaEIAQAAAAGiCEAAAAABBBIAAPsPADDDCAAA_A8AMMcIAADFDgAwyAgAAP4PACAEEgAA8A8AMMMIAADxDwAwxwgAAOINADDICAAA8w8AIAQSAADFDwAwwwgAAMYPADDHCAAAyQ8AMMgIAADIDwAgBBIAALkPADDDCAAAug8AMMcIAAC9DwAwyAgAALwPACAEEgAAsA8AMMMIAACxDwAwxwgAAIQOADDICAAAsw8AIAMSAADcFgAgwwgAAN0WACDHCAAAPQAgBBIAAKcPADDDCAAAqA8AMMcIAAD0DAAwyAgAAKoPACAEEgAAng8AMMMIAACfDwAwxwgAAPQMADDICAAAoQ8AIBU1AACHEAAgOQAAixAAIFkAAI0QACBoAACIEAAgawAAiRAAIG4AAIoQACBwAACZEAAgcQAAjhAAIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHcBgEAAAABwgcBAAAAAYoIAQAAAAGcCAEAAAABnQggAAAAAZ4IQAAAAAGfCAEAAAABoAggAAAAAaIIQAAAAAECAAAASAAgEgAAmBAAIAMAAABIACASAACYEAAgEwAAlhAAIAELAADbFgAwAgAAAEgAIAsAAJYQACACAAAAkw8AIAsAAJUQACANrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAIcIHAQDLDAAhiggBAMsMACGcCAEAywwAIZ0IIADMDAAhnghAAM0MACGfCAEAywwAIaAIIADMDAAhoghAAM0MACEVNQAAlg8AIDkAAJoPACBZAACcDwAgaAAAlw8AIGsAAJgPACBuAACZDwAgcAAAlxAAIHEAAJ0PACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdwGAQDJDAAhwgcBAMsMACGKCAEAywwAIZwIAQDLDAAhnQggAMwMACGeCEAAzQwAIZ8IAQDLDAAhoAggAMwMACGiCEAAzQwAIQcSAADWFgAgEwAA2RYAIMMIAADXFgAgxAgAANgWACDFCAAAOwAgxggAADsAIMcIAAA9ACAVNQAAhxAAIDkAAIsQACBZAACNEAAgaAAAiBAAIGsAAIkQACBuAACKEAAgcAAAmRAAIHEAAI4QACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAcIHAQAAAAGKCAEAAAABnAgBAAAAAZ0IIAAAAAGeCEAAAAABnwgBAAAAAaAIIAAAAAGiCEAAAAABAxIAANYWACDDCAAA1xYAIMcIAAA9ACAGrwYBAAAAAasIAQAAAAGsCEAAAAABrQggAAAAAa4IQAAAAAGvCEAAAAABAgAAADUAIBIAAKUQACADAAAANQAgEgAApRAAIBMAAKQQACABCwAA1RYAMAswAACMDAAgrAYAAK0MADCtBgAAQwAQrgYAAK0MADCvBgEAAAABqQgBAPIKACGrCAEAAAABrAhAAPYKACGtCCAA9AoAIa4IQAD1CgAhrwhAAPUKACECAAAANQAgCwAApBAAIAIAAACiEAAgCwAAoxAAIAqsBgAAoRAAMK0GAACiEAAQrgYAAKEQADCvBgEA8goAIakIAQDyCgAhqwgBAPIKACGsCEAA9goAIa0IIAD0CgAhrghAAPUKACGvCEAA9QoAIQqsBgAAoRAAMK0GAACiEAAQrgYAAKEQADCvBgEA8goAIakIAQDyCgAhqwgBAPIKACGsCEAA9goAIa0IIAD0CgAhrghAAPUKACGvCEAA9QoAIQavBgEAyQwAIasIAQDJDAAhrAhAAM4MACGtCCAAzAwAIa4IQADNDAAhrwhAAM0MACEGrwYBAMkMACGrCAEAyQwAIawIQADODAAhrQggAMwMACGuCEAAzQwAIa8IQADNDAAhBq8GAQAAAAGrCAEAAAABrAhAAAAAAa0IIAAAAAGuCEAAAAABrwhAAAAAAQavBgEAAAABuAZAAAAAAbkGQAAAAAGUCAEAAAABowgBAAAAAaQIAQAAAAECAAAAOQAgEgAAsBAAIAMAAAA5ACASAACwEAAgEwAArxAAIAouAAC0DAAgrAYAALMMADCtBgAANwAQrgYAALMMADCvBgEAAAABuAZAAPYKACG5BkAA9goAIZQIAQDyCgAhowgBAAAAAaQIAQDyCgAhAgAAADkAIAsAAK8QACACAAAArRAAIAsAAK4QACAJrAYAAKwQADCtBgAArRAAEK4GAACsEAAwrwYBAPIKACG4BkAA9goAIbkGQAD2CgAhlAgBAPIKACGjCAEA8goAIaQIAQDyCgAhCawGAACsEAAwrQYAAK0QABCuBgAArBAAMK8GAQDyCgAhuAZAAPYKACG5BkAA9goAIZQIAQDyCgAhowgBAPIKACGkCAEA8goAIQavBgEAyQwAIbgGQADODAAhuQZAAM4MACGUCAEAyQwAIaMIAQDJDAAhpAgBAMkMACEGrwYBAMkMACG4BkAAzgwAIbkGQADODAAhlAgBAMkMACGjCAEAyQwAIaQIAQDJDAAhBq8GAQAAAAG4BkAAAAABuQZAAAAAAZQIAQAAAAGjCAEAAAABpAgBAAAAAQMSAACmEAAwwwgAAKcQADDHCAAAqRAAMAQSAACaEAAwwwgAAJsQADDHCAAAnhAAMMgIAACdEAAgBBIAAI8QADDDCAAAkBAAMMcIAACPDwAwyAgAAJIQACAEEgAAiw8AMMMIAACMDwAwxwgAAI8PADDICAAAjg8AIAQSAACCDwAwwwgAAIMPADDHCAAAhA4AMMgIAACFDwAgBBIAAPkOADDDCAAA-g4AMMcIAACEDgAwyAgAAPwOACAEEgAA7g4AMMMIAADvDgAwxwgAAKgOADDICAAA8Q4AIAQSAADeDQAwwwgAAN8NADDHCAAA4g0AMMgIAADhDQAgBBIAANUNADDDCAAA1g0AMMcIAAD0DAAwyAgAANgNACAEEgAAzA0AMMMIAADNDQAwxwgAAPQMADDICAAAzw0AIAQSAADBDQAwwwgAAMINADDHCAAA9AwAMMgIAADEDQAgBBIAAPAMADDDCAAA8QwAMMcIAAD0DAAwyAgAAPMMACAEEgAA4gwAMMMIAADjDAAwxwgAAOYMADDICAAA5QwAIAMSAADQDAAgwwgAANEMACDHCAAAPQAgGC8AAL0VACAxAAC-FQAgMgAAvxUAIDMAAMMMACA4AAC1FQAgOQAA0BAAIGgAALsVACByAADAFQAgcwAAwBUAIHQAANAQACB1AAD1EQAgdgAA9REAIHcAAPURACB4AAD1EQAgeQAArBUAILAGAADDDAAgsgYAAMMMACCzBgAAwwwAIJQIAADDDAAgpAgAAMMMACCmCAAAwwwAIKgIAADDDAAgqQgAAMMMACCqCAAAwwwAIAAAAAAACxIAAMYQADATAADKEAAwwwgAAMcQADDECAAAyBAAMMUIAACEDgAwxggAAIQOADDHCAAAhA4AMMgIAADJEAAgyQgAAIQOADDKCAAAyxAAMMsIAACHDgAwKjMBAAAAATQAALwOACA4AAC1DgAgOgAAtg4AIDsAALcOACA8AAC4DgAgPgAAug4AID8AALsOACBAAAC9DgAgQQAA4w4AIEIAAL4OACBFAQAAAAFYAADADgAgXQEAAAABZQAAvw4AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHcBgEAAAAB3gYCAAAAAeAGAgAAAAHnBiAAAAAB9gYCAAAAAfkGAQAAAAGEBwEAAAABhQcCAAAAAYgHAAAAiAcDiQcIAAAAAYoHAQAAAAGLBwEAAAABjAcCAAAAAY0HAgAAAAGOBwEAAAABjwcCAAAAAZAHAgAAAAGRB0AAAAABkgdAAAAAAZMHQAAAAAGUB0AAAAABlQcAALQOACCWBwEAAAABAgAAAFQAIBIAAM4QACADAAAAVAAgEgAAzhAAIBMAAM0QACABCwAA1BYAMAIAAABUACALAADNEAAgAgAAAIgOACALAADMEAAgHjMBAMkMACFFAQDLDAAhXQEAywwAIa8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHeBgIAygwAIeAGAgCADQAh5wYgAMwMACH2BgIAgA0AIfkGAQDLDAAhhAcBAMsMACGFBwIAgA0AIYgHAACKDogHI4kHCAD9DAAhigcBAMsMACGLBwEAywwAIYwHAgCADQAhjQcCAIANACGOBwEAywwAIY8HAgCADQAhkAcCAIANACGRB0AAzQwAIZIHQADNDAAhkwdAAM0MACGUB0AAzQwAIZUHAACLDgAglgcBAMkMACEqMwEAyQwAITQAAJQOACA4AACNDgAgOgAAjg4AIDsAAI8OACA8AACQDgAgPgAAkg4AID8AAJMOACBAAACVDgAgQQAA4Q4AIEIAAJYOACBFAQDLDAAhWAAAmA4AIF0BAMsMACFlAACXDgAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAId4GAgDKDAAh4AYCAIANACHnBiAAzAwAIfYGAgCADQAh-QYBAMsMACGEBwEAywwAIYUHAgCADQAhiAcAAIoOiAcjiQcIAP0MACGKBwEAywwAIYsHAQDLDAAhjAcCAIANACGNBwIAgA0AIY4HAQDLDAAhjwcCAIANACGQBwIAgA0AIZEHQADNDAAhkgdAAM0MACGTB0AAzQwAIZQHQADNDAAhlQcAAIsOACCWBwEAyQwAISozAQAAAAE0AAC8DgAgOAAAtQ4AIDoAALYOACA7AAC3DgAgPAAAuA4AID4AALoOACA_AAC7DgAgQAAAvQ4AIEEAAOMOACBCAAC-DgAgRQEAAAABWAAAwA4AIF0BAAAAAWUAAL8OACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAd4GAgAAAAHgBgIAAAAB5wYgAAAAAfYGAgAAAAH5BgEAAAABhAcBAAAAAYUHAgAAAAGIBwAAAIgHA4kHCAAAAAGKBwEAAAABiwcBAAAAAYwHAgAAAAGNBwIAAAABjgcBAAAAAY8HAgAAAAGQBwIAAAABkQdAAAAAAZIHQAAAAAGTB0AAAAABlAdAAAAAAZUHAAC0DgAglgcBAAAAAQQSAADGEAAwwwgAAMcQADDHCAAAhA4AMMgIAADJEAAgAAAAAAAAAAAAAAUSAADPFgAgEwAA0hYAIMMIAADQFgAgxAgAANEWACDHCAAA4wEAIAMSAADPFgAgwwgAANAWACDHCAAA4wEAIAAAAAsSAADgEAAwEwAA5BAAMMMIAADhEAAwxAgAAOIQADDFCAAAyQ8AMMYIAADJDwAwxwgAAMkPADDICAAA4xAAIMkIAADJDwAwyggAAOUQADDLCAAAzA8AMAw0AADsDwAgRQEAAAABaQAA7w8AIGoAAOoPACBtAADtDwAgrwYBAAAAAbgGQAAAAAG5BkAAAAAB3AYBAAAAAd0GAQAAAAHfBoAAAAAB4AYBAAAAAQIAAADjAQAgEgAA6BAAIAMAAADjAQAgEgAA6BAAIBMAAOcQACABCwAAzhYAMAIAAADjAQAgCwAA5xAAIAIAAADNDwAgCwAA5hAAIAhFAQDLDAAhrwYBAMkMACG4BkAAzgwAIbkGQADODAAh3AYBAMkMACHdBgEAywwAId8GgAAAAAHgBgEAyQwAIQw0AADoDwAgRQEAywwAIWkAANAPACBqAADRDwAgbQAA0w8AIK8GAQDJDAAhuAZAAM4MACG5BkAAzgwAIdwGAQDJDAAh3QYBAMsMACHfBoAAAAAB4AYBAMkMACEMNAAA7A8AIEUBAAAAAWkAAO8PACBqAADqDwAgbQAA7Q8AIK8GAQAAAAG4BkAAAAABuQZAAAAAAdwGAQAAAAHdBgEAAAAB3waAAAAAAeAGAQAAAAEEEgAA4BAAMMMIAADhEAAwxwgAAMkPADDICAAA4xAAIAAAAAAAAAcSAADDFgAgEwAAzBYAIMMIAADEFgAgxAgAAMsWACDFCAAA4AgAIMYIAADgCAAgxwgAANoIACAHEgAAwRYAIBMAAMkWACDDCAAAwhYAIMQIAADIFgAgxQgAAOIIACDGCAAA4ggAIMcIAACBCQAgBxIAAL8WACATAADGFgAgwwgAAMAWACDECAAAxRYAIMUIAADmCAAgxggAAOYIACDHCAAAmQkAIAMSAADDFgAgwwgAAMQWACDHCAAA2ggAIAMSAADBFgAgwwgAAMIWACDHCAAAgQkAIAMSAAC_FgAgwwgAAMAWACDHCAAAmQkAIAAAAAAACxIAAPwQADATAACBEQAwwwgAAP0QADDECAAA_hAAMMUIAACAEQAwxggAAIARADDHCAAAgBEAMMgIAAD_EAAgyQgAAIARADDKCAAAghEAMMsIAACDEQAwHjoAAPQQACA8AADzEAAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHVBgEAAAAB3AYBAAAAAd4GAgAAAAHmBgEAAAAB5wYgAAAAAegGIAAAAAHpBiAAAAAB6gYgAAAAAesGIAAAAAHsBiAAAAAB7QYgAAAAAe4GAQAAAAHvBkAAAAAB8AZAAAAAAfEGQAAAAAHyBggAAAAB8wYCAAAAAfQGCAAAAAH1BgIAAAAB9gYCAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAQIAAADeCAAgEgAAhxEAIAMAAADeCAAgEgAAhxEAIBMAAIYRACABCwAAvhYAMCM6AACZCwAgPAAAmAsAIIsFAACaCwAgrAYAAJYLADCtBgAA3AgAEK4GAACWCwAwrwYCAAAAAbgGQAD2CgAhuQZAAPYKACHFBgEAAAAByAYBAPMKACHVBgEA8woAIdwGAQDyCgAh3gYCAJcLACHmBgEA8woAIecGIAD0CgAh6AYgAPQKACHpBiAA9AoAIeoGIAD0CgAh6wYgAPQKACHsBiAA9AoAIe0GIAD0CgAh7gYBAPMKACHvBkAA9QoAIfAGQAD1CgAh8QZAAPUKACHyBggA_QoAIfMGAgCXCwAh9AYIAP0KACH1BgIAlwsAIfYGAgCXCwAh9wYCAJcLACH4BgEA8woAIfkGAQDzCgAh-gZAAPUKACECAAAA3ggAIAsAAIYRACACAAAAhBEAIAsAAIURACAgrAYAAIMRADCtBgAAhBEAEK4GAACDEQAwrwYCAJQLACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdUGAQDzCgAh3AYBAPIKACHeBgIAlwsAIeYGAQDzCgAh5wYgAPQKACHoBiAA9AoAIekGIAD0CgAh6gYgAPQKACHrBiAA9AoAIewGIAD0CgAh7QYgAPQKACHuBgEA8woAIe8GQAD1CgAh8AZAAPUKACHxBkAA9QoAIfIGCAD9CgAh8wYCAJcLACH0BggA_QoAIfUGAgCXCwAh9gYCAJcLACH3BgIAlwsAIfgGAQDzCgAh-QYBAPMKACH6BkAA9QoAISCsBgAAgxEAMK0GAACEEQAQrgYAAIMRADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIcgGAQDzCgAh1QYBAPMKACHcBgEA8goAId4GAgCXCwAh5gYBAPMKACHnBiAA9AoAIegGIAD0CgAh6QYgAPQKACHqBiAA9AoAIesGIAD0CgAh7AYgAPQKACHtBiAA9AoAIe4GAQDzCgAh7wZAAPUKACHwBkAA9QoAIfEGQAD1CgAh8gYIAP0KACHzBgIAlwsAIfQGCAD9CgAh9QYCAJcLACH2BgIAlwsAIfcGAgCXCwAh-AYBAPMKACH5BgEA8woAIfoGQAD1CgAhHK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHVBgEAywwAIdwGAQDJDAAh3gYCAIANACHmBgEAywwAIecGIADMDAAh6AYgAMwMACHpBiAAzAwAIeoGIADMDAAh6wYgAMwMACHsBiAAzAwAIe0GIADMDAAh7gYBAMsMACHvBkAAzQwAIfAGQADNDAAh8QZAAM0MACHyBggAnA0AIfMGAgCADQAh9AYIAJwNACH1BgIAgA0AIfYGAgCADQAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhHjoAAPEQACA8AADwEAAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdUGAQDLDAAh3AYBAMkMACHeBgIAgA0AIeYGAQDLDAAh5wYgAMwMACHoBiAAzAwAIekGIADMDAAh6gYgAMwMACHrBiAAzAwAIewGIADMDAAh7QYgAMwMACHuBgEAywwAIe8GQADNDAAh8AZAAM0MACHxBkAAzQwAIfIGCACcDQAh8wYCAIANACH0BggAnA0AIfUGAgCADQAh9gYCAIANACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACEeOgAA9BAAIDwAAPMQACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAdUGAQAAAAHcBgEAAAAB3gYCAAAAAeYGAQAAAAHnBiAAAAAB6AYgAAAAAekGIAAAAAHqBiAAAAAB6wYgAAAAAewGIAAAAAHtBiAAAAAB7gYBAAAAAe8GQAAAAAHwBkAAAAAB8QZAAAAAAfIGCAAAAAHzBgIAAAAB9AYIAAAAAfUGAgAAAAH2BgIAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABBBIAAPwQADDDCAAA_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_AYAAMMMACD9BgAAwwwAIP4GAADDDAAg_wYAAMMMACADigUAAIkRACD3BgAAwwwAIPsGAADDDAAgAAAAAAAAAAAAAAsSAAC4EQAwEwAAvBEAMMMIAAC5EQAwxAgAALoRADDFCAAAhA4AMMYIAACEDgAwxwgAAIQOADDICAAAuxEAIMkIAACEDgAwyggAAL0RADDLCAAAhw4AMCozAQAAAAE0AAC8DgAgOAAAtQ4AIDoAALYOACA7AAC3DgAgPQAAuQ4AID4AALoOACA_AAC7DgAgQAAAvQ4AIEEAAOMOACBCAAC-DgAgRQEAAAABWAAAwA4AIF0BAAAAAWUAAL8OACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAd4GAgAAAAHgBgIAAAAB5wYgAAAAAfkGAQAAAAGEBwEAAAABhQcCAAAAAYYHAgAAAAGIBwAAAIgHA4kHCAAAAAGKBwEAAAABiwcBAAAAAYwHAgAAAAGNBwIAAAABjgcBAAAAAY8HAgAAAAGQBwIAAAABkQdAAAAAAZIHQAAAAAGTB0AAAAABlAdAAAAAAZUHAAC0DgAglgcBAAAAAQIAAABUACASAADAEQAgAwAAAFQAIBIAAMARACATAAC_EQAgAQsAALsWADACAAAAVAAgCwAAvxEAIAIAAACIDgAgCwAAvhEAIB4zAQDJDAAhRQEAywwAIV0BAMsMACGvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdwGAQDJDAAh3gYCAMoMACHgBgIAgA0AIecGIADMDAAh-QYBAMsMACGEBwEAywwAIYUHAgCADQAhhgcCAIANACGIBwAAig6IByOJBwgA_QwAIYoHAQDLDAAhiwcBAMsMACGMBwIAgA0AIY0HAgCADQAhjgcBAMsMACGPBwIAgA0AIZAHAgCADQAhkQdAAM0MACGSB0AAzQwAIZMHQADNDAAhlAdAAM0MACGVBwAAiw4AIJYHAQDJDAAhKjMBAMkMACE0AACUDgAgOAAAjQ4AIDoAAI4OACA7AACPDgAgPQAAkQ4AID4AAJIOACA_AACTDgAgQAAAlQ4AIEEAAOEOACBCAACWDgAgRQEAywwAIVgAAJgOACBdAQDLDAAhZQAAlw4AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHeBgIAygwAIeAGAgCADQAh5wYgAMwMACH5BgEAywwAIYQHAQDLDAAhhQcCAIANACGGBwIAgA0AIYgHAACKDogHI4kHCAD9DAAhigcBAMsMACGLBwEAywwAIYwHAgCADQAhjQcCAIANACGOBwEAywwAIY8HAgCADQAhkAcCAIANACGRB0AAzQwAIZIHQADNDAAhkwdAAM0MACGUB0AAzQwAIZUHAACLDgAglgcBAMkMACEqMwEAAAABNAAAvA4AIDgAALUOACA6AAC2DgAgOwAAtw4AID0AALkOACA-AAC6DgAgPwAAuw4AIEAAAL0OACBBAADjDgAgQgAAvg4AIEUBAAAAAVgAAMAOACBdAQAAAAFlAAC_DgAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdwGAQAAAAHeBgIAAAAB4AYCAAAAAecGIAAAAAH5BgEAAAABhAcBAAAAAYUHAgAAAAGGBwIAAAABiAcAAACIBwOJBwgAAAABigcBAAAAAYsHAQAAAAGMBwIAAAABjQcCAAAAAY4HAQAAAAGPBwIAAAABkAcCAAAAAZEHQAAAAAGSB0AAAAABkwdAAAAAAZQHQAAAAAGVBwAAtA4AIJYHAQAAAAEEEgAAuBEAMMMIAAC5EQAwxwgAAIQOADDICAAAuxEAIAAAAAAACxIAAMgRADATAADMEQAwwwgAAMkRADDECAAAyhEAMMUIAACEDgAwxggAAIQOADDHCAAAhA4AMMgIAADLEQAgyQgAAIQOADDKCAAAzREAMMsIAACHDgAwKjMBAAAAATQAALwOACA4AAC1DgAgOwAAtw4AIDwAALgOACA9AAC5DgAgPgAAug4AID8AALsOACBAAAC9DgAgQQAA4w4AIEIAAL4OACBFAQAAAAFYAADADgAgXQEAAAABZQAAvw4AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHcBgEAAAAB4AYCAAAAAecGIAAAAAH2BgIAAAAB-QYBAAAAAYQHAQAAAAGFBwIAAAABhgcCAAAAAYgHAAAAiAcDiQcIAAAAAYoHAQAAAAGLBwEAAAABjAcCAAAAAY0HAgAAAAGOBwEAAAABjwcCAAAAAZAHAgAAAAGRB0AAAAABkgdAAAAAAZMHQAAAAAGUB0AAAAABlQcAALQOACCWBwEAAAABAgAAAFQAIBIAANARACADAAAAVAAgEgAA0BEAIBMAAM8RACABCwAAuhYAMAIAAABUACALAADPEQAgAgAAAIgOACALAADOEQAgHjMBAMkMACFFAQDLDAAhXQEAywwAIa8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHgBgIAgA0AIecGIADMDAAh9gYCAIANACH5BgEAywwAIYQHAQDLDAAhhQcCAIANACGGBwIAgA0AIYgHAACKDogHI4kHCAD9DAAhigcBAMsMACGLBwEAywwAIYwHAgCADQAhjQcCAIANACGOBwEAywwAIY8HAgCADQAhkAcCAIANACGRB0AAzQwAIZIHQADNDAAhkwdAAM0MACGUB0AAzQwAIZUHAACLDgAglgcBAMkMACEqMwEAyQwAITQAAJQOACA4AACNDgAgOwAAjw4AIDwAAJAOACA9AACRDgAgPgAAkg4AID8AAJMOACBAAACVDgAgQQAA4Q4AIEIAAJYOACBFAQDLDAAhWAAAmA4AIF0BAMsMACFlAACXDgAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAIeAGAgCADQAh5wYgAMwMACH2BgIAgA0AIfkGAQDLDAAhhAcBAMsMACGFBwIAgA0AIYYHAgCADQAhiAcAAIoOiAcjiQcIAP0MACGKBwEAywwAIYsHAQDLDAAhjAcCAIANACGNBwIAgA0AIY4HAQDLDAAhjwcCAIANACGQBwIAgA0AIZEHQADNDAAhkgdAAM0MACGTB0AAzQwAIZQHQADNDAAhlQcAAIsOACCWBwEAyQwAISozAQAAAAE0AAC8DgAgOAAAtQ4AIDsAALcOACA8AAC4DgAgPQAAuQ4AID4AALoOACA_AAC7DgAgQAAAvQ4AIEEAAOMOACBCAAC-DgAgRQEAAAABWAAAwA4AIF0BAAAAAWUAAL8OACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAeAGAgAAAAHnBiAAAAAB9gYCAAAAAfkGAQAAAAGEBwEAAAABhQcCAAAAAYYHAgAAAAGIBwAAAIgHA4kHCAAAAAGKBwEAAAABiwcBAAAAAYwHAgAAAAGNBwIAAAABjgcBAAAAAY8HAgAAAAGQBwIAAAABkQdAAAAAAZIHQAAAAAGTB0AAAAABlAdAAAAAAZUHAAC0DgAglgcBAAAAAQQSAADIEQAwwwgAAMkRADDHCAAAhA4AMMgIAADLEQAgAAAAAAALEgAA2BEAMBMAANwRADDDCAAA2REAMMQIAADaEQAwxQgAAIQOADDGCAAAhA4AMMcIAACEDgAwyAgAANsRACDJCAAAhA4AMMoIAADdEQAwywgAAIcOADAqMwEAAAABNAAAvA4AIDgAALUOACA6AAC2DgAgPAAAuA4AID0AALkOACA-AAC6DgAgPwAAuw4AIEAAAL0OACBBAADjDgAgQgAAvg4AIEUBAAAAAVgAAMAOACBdAQAAAAFlAAC_DgAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdwGAQAAAAHeBgIAAAAB4AYCAAAAAecGIAAAAAH2BgIAAAAB-QYBAAAAAYQHAQAAAAGGBwIAAAABiAcAAACIBwOJBwgAAAABigcBAAAAAYsHAQAAAAGMBwIAAAABjQcCAAAAAY4HAQAAAAGPBwIAAAABkAcCAAAAAZEHQAAAAAGSB0AAAAABkwdAAAAAAZQHQAAAAAGVBwAAtA4AIJYHAQAAAAECAAAAVAAgEgAA4BEAIAMAAABUACASAADgEQAgEwAA3xEAIAELAAC5FgAwAgAAAFQAIAsAAN8RACACAAAAiA4AIAsAAN4RACAeMwEAyQwAIUUBAMsMACFdAQDLDAAhrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAId4GAgDKDAAh4AYCAIANACHnBiAAzAwAIfYGAgCADQAh-QYBAMsMACGEBwEAywwAIYYHAgCADQAhiAcAAIoOiAcjiQcIAP0MACGKBwEAywwAIYsHAQDLDAAhjAcCAIANACGNBwIAgA0AIY4HAQDLDAAhjwcCAIANACGQBwIAgA0AIZEHQADNDAAhkgdAAM0MACGTB0AAzQwAIZQHQADNDAAhlQcAAIsOACCWBwEAyQwAISozAQDJDAAhNAAAlA4AIDgAAI0OACA6AACODgAgPAAAkA4AID0AAJEOACA-AACSDgAgPwAAkw4AIEAAAJUOACBBAADhDgAgQgAAlg4AIEUBAMsMACFYAACYDgAgXQEAywwAIWUAAJcOACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdwGAQDJDAAh3gYCAMoMACHgBgIAgA0AIecGIADMDAAh9gYCAIANACH5BgEAywwAIYQHAQDLDAAhhgcCAIANACGIBwAAig6IByOJBwgA_QwAIYoHAQDLDAAhiwcBAMsMACGMBwIAgA0AIY0HAgCADQAhjgcBAMsMACGPBwIAgA0AIZAHAgCADQAhkQdAAM0MACGSB0AAzQwAIZMHQADNDAAhlAdAAM0MACGVBwAAiw4AIJYHAQDJDAAhKjMBAAAAATQAALwOACA4AAC1DgAgOgAAtg4AIDwAALgOACA9AAC5DgAgPgAAug4AID8AALsOACBAAAC9DgAgQQAA4w4AIEIAAL4OACBFAQAAAAFYAADADgAgXQEAAAABZQAAvw4AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHcBgEAAAAB3gYCAAAAAeAGAgAAAAHnBiAAAAAB9gYCAAAAAfkGAQAAAAGEBwEAAAABhgcCAAAAAYgHAAAAiAcDiQcIAAAAAYoHAQAAAAGLBwEAAAABjAcCAAAAAY0HAgAAAAGOBwEAAAABjwcCAAAAAZAHAgAAAAGRB0AAAAABkgdAAAAAAZMHQAAAAAGUB0AAAAABlQcAALQOACCWBwEAAAABBBIAANgRADDDCAAA2REAMMcIAACEDgAwyAgAANsRACAAAAAAAAAAAAsSAADrEQAwEwAA7xEAMMMIAADsEQAwxAgAAO0RADDFCAAA9AwAMMYIAAD0DAAwxwgAAPQMADDICAAA7hEAIMkIAAD0DAAwyggAAPARADDLCAAA9wwAMEMzAADLDQAgNAAAug0AIDcAAMANACBAAAC7DQAgQgAAtw0AIEUAALENACBYAACyDQAgWwAAtA0AIFwAALUNACBdAAC2DQAgXgAAuA0AIF8AALkNACBgAAC8DQAgYQAAvQ0AIGIAAL4NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHgBgIAAAAB5wYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAYwHAgAAAAGPBwIAAAABnwcgAAAAAaAHIAAAAAGhByAAAAABogcgAAAAAaMHIAAAAAGkByAAAAABuAcBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwQcBAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABxwcBAAAAAcgHIAAAAAHJByAAAAABygcIAAAAAcwHAAAAzAcCzQdAAAAAAc8HAAAAzwcC0AcBAAAAAdEHIAAAAAHSByAAAAAB0wcgAAAAAdQHAQAAAAHVBwIAAAAB1wcCAAAAAdgHAgAAAAHZBwIAAAAB2gcCAAAAAdsHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAECAAAAdwAgEgAA8xEAIAMAAAB3ACASAADzEQAgEwAA8hEAIAELAAC4FgAwAgAAAHcAIAsAAPIRACACAAAA-AwAIAsAAPERACAzrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIeAGAgCADQAh5wYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACGMBwIAgA0AIY8HAgDKDAAhnwcgAMwMACGgByAAzAwAIaEHIADMDAAhogcgAMwMACGjByAAzAwAIaQHIADMDAAhuAcBAMkMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8EHAQDLDAAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAhxwcBAMsMACHIByAAzAwAIckHIADMDAAhygcIAP0MACHMBwAA_gzMByLNB0AAzQwAIc8HAAD_DM8HItAHAQDLDAAh0QcgAMwMACHSByAAzAwAIdMHIADMDAAh1AcBAMsMACHVBwIAgA0AIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyQ0AIDQAAIsNACA3AACRDQAgQAAAjA0AIEIAAIgNACBFAACCDQAgWAAAgw0AIFsAAIUNACBcAACGDQAgXQAAhw0AIF4AAIkNACBfAACKDQAgYAAAjQ0AIGEAAI4NACBiAACPDQAgYwAAkA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHXBwIAygwAIdgHAgDKDAAh2QcCAMoMACHaBwIAgA0AIdsHAgCADQAh3AcCAIANACHdBwIAgA0AId4HQADNDAAhQzMAAMsNACA0AAC6DQAgNwAAwA0AIEAAALsNACBCAAC3DQAgRQAAsQ0AIFgAALINACBbAAC0DQAgXAAAtQ0AIF0AALYNACBeAAC4DQAgXwAAuQ0AIGAAALwNACBhAAC9DQAgYgAAvg0AIGMAAL8NACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB1QYBAAAAAeAGAgAAAAHnBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABjAcCAAAAAY8HAgAAAAGfByAAAAABoAcgAAAAAaEHIAAAAAGiByAAAAABowcgAAAAAaQHIAAAAAG4BwEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPBBwEAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAHHBwEAAAAByAcgAAAAAckHIAAAAAHKBwgAAAABzAcAAADMBwLNB0AAAAABzwcAAADPBwLQBwEAAAAB0QcgAAAAAdIHIAAAAAHTByAAAAAB1AcBAAAAAdUHAgAAAAHXBwIAAAAB2AcCAAAAAdkHAgAAAAHaBwIAAAAB2wcCAAAAAdwHAgAAAAHdBwIAAAAB3gdAAAAAAQQSAADrEQAwwwgAAOwRADDHCAAA9AwAMMgIAADuEQAgAAAAAAAACxIAAPwRADATAACAEgAwwwgAAP0RADDECAAA_hEAMMUIAAD0DAAwxggAAPQMADDHCAAA9AwAMMgIAAD_EQAgyQgAAPQMADDKCAAAgRIAMMsIAAD3DAAwQzMAAMsNACA0AAC6DQAgNwAAwA0AIEAAALsNACBCAAC3DQAgRQAAsQ0AIFgAALINACBaAACzDQAgWwAAtA0AIF0AALYNACBeAAC4DQAgXwAAuQ0AIGAAALwNACBhAAC9DQAgYgAAvg0AIGMAAL8NACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB1QYBAAAAAeAGAgAAAAHnBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABjAcCAAAAAY8HAgAAAAGfByAAAAABoAcgAAAAAaEHIAAAAAGiByAAAAABowcgAAAAAaQHIAAAAAG4BwEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPBBwEAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAHHBwEAAAAByAcgAAAAAckHIAAAAAHKBwgAAAABzAcAAADMBwLNB0AAAAABzwcAAADPBwLQBwEAAAAB0QcgAAAAAdIHIAAAAAHTByAAAAAB1AcBAAAAAdUHAgAAAAHWBwIAAAAB1wcCAAAAAdkHAgAAAAHaBwIAAAAB2wcCAAAAAdwHAgAAAAHdBwIAAAAB3gdAAAAAAQIAAAB3ACASAACEEgAgAwAAAHcAIBIAAIQSACATAACDEgAgAQsAALcWADACAAAAdwAgCwAAgxIAIAIAAAD4DAAgCwAAghIAIDOvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdUGAQDLDAAh4AYCAIANACHnBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIYwHAgCADQAhjwcCAMoMACGfByAAzAwAIaAHIADMDAAhoQcgAMwMACGiByAAzAwAIaMHIADMDAAhpAcgAMwMACG4BwEAyQwAIbkHAQDLDAAhugcBAMsMACG7BwEAywwAIb0HAAD6DL0HI74HAQDLDAAhwAcAAPsMwAcjwQcBAMsMACHCBwEAywwAIcMHAQDLDAAhxQcAAPwMxQcjxgcBAMsMACHHBwEAywwAIcgHIADMDAAhyQcgAMwMACHKBwgA_QwAIcwHAAD-DMwHIs0HQADNDAAhzwcAAP8Mzwci0AcBAMsMACHRByAAzAwAIdIHIADMDAAh0wcgAMwMACHUBwEAywwAIdUHAgCADQAh1gcCAMoMACHXBwIAygwAIdkHAgDKDAAh2gcCAIANACHbBwIAgA0AIdwHAgCADQAh3QcCAIANACHeB0AAzQwAIUMzAADJDQAgNAAAiw0AIDcAAJENACBAAACMDQAgQgAAiA0AIEUAAIINACBYAACDDQAgWgAAhA0AIFsAAIUNACBdAACHDQAgXgAAiQ0AIF8AAIoNACBgAACNDQAgYQAAjg0AIGIAAI8NACBjAACQDQAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIeAGAgCADQAh5wYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACGMBwIAgA0AIY8HAgDKDAAhnwcgAMwMACGgByAAzAwAIaEHIADMDAAhogcgAMwMACGjByAAzAwAIaQHIADMDAAhuAcBAMkMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8EHAQDLDAAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAhxwcBAMsMACHIByAAzAwAIckHIADMDAAhygcIAP0MACHMBwAA_gzMByLNB0AAzQwAIc8HAAD_DM8HItAHAQDLDAAh0QcgAMwMACHSByAAzAwAIdMHIADMDAAh1AcBAMsMACHVBwIAgA0AIdYHAgDKDAAh1wcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyw0AIDQAALoNACA3AADADQAgQAAAuw0AIEIAALcNACBFAACxDQAgWAAAsg0AIFoAALMNACBbAAC0DQAgXQAAtg0AIF4AALgNACBfAAC5DQAgYAAAvA0AIGEAAL0NACBiAAC-DQAgYwAAvw0AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHVBgEAAAAB4AYCAAAAAecGIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAGMBwIAAAABjwcCAAAAAZ8HIAAAAAGgByAAAAABoQcgAAAAAaIHIAAAAAGjByAAAAABpAcgAAAAAbgHAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8EHAQAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAccHAQAAAAHIByAAAAAByQcgAAAAAcoHCAAAAAHMBwAAAMwHAs0HQAAAAAHPBwAAAM8HAtAHAQAAAAHRByAAAAAB0gcgAAAAAdMHIAAAAAHUBwEAAAAB1QcCAAAAAdYHAgAAAAHXBwIAAAAB2QcCAAAAAdoHAgAAAAHbBwIAAAAB3AcCAAAAAd0HAgAAAAHeB0AAAAABBBIAAPwRADDDCAAA_REAMMcIAAD0DAAwyAgAAP8RACAAAAAAAAsSAACMEgAwEwAAkBIAMMMIAACNEgAwxAgAAI4SADDFCAAA9AwAMMYIAAD0DAAwxwgAAPQMADDICAAAjxIAIMkIAAD0DAAwyggAAJESADDLCAAA9wwAMEMzAADLDQAgNAAAug0AIDcAAMANACBAAAC7DQAgQgAAtw0AIEUAALENACBYAACyDQAgWgAAsw0AIFwAALUNACBdAAC2DQAgXgAAuA0AIF8AALkNACBgAAC8DQAgYQAAvQ0AIGIAAL4NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHgBgIAAAAB5wYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAYwHAgAAAAGPBwIAAAABnwcgAAAAAaAHIAAAAAGhByAAAAABogcgAAAAAaMHIAAAAAGkByAAAAABuAcBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwQcBAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABxwcBAAAAAcgHIAAAAAHJByAAAAABygcIAAAAAcwHAAAAzAcCzQdAAAAAAc8HAAAAzwcC0AcBAAAAAdEHIAAAAAHSByAAAAAB0wcgAAAAAdQHAQAAAAHVBwIAAAAB1gcCAAAAAdgHAgAAAAHZBwIAAAAB2gcCAAAAAdsHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAECAAAAdwAgEgAAlBIAIAMAAAB3ACASAACUEgAgEwAAkxIAIAELAAC2FgAwAgAAAHcAIAsAAJMSACACAAAA-AwAIAsAAJISACAzrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIeAGAgCADQAh5wYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACGMBwIAgA0AIY8HAgDKDAAhnwcgAMwMACGgByAAzAwAIaEHIADMDAAhogcgAMwMACGjByAAzAwAIaQHIADMDAAhuAcBAMkMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8EHAQDLDAAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAhxwcBAMsMACHIByAAzAwAIckHIADMDAAhygcIAP0MACHMBwAA_gzMByLNB0AAzQwAIc8HAAD_DM8HItAHAQDLDAAh0QcgAMwMACHSByAAzAwAIdMHIADMDAAh1AcBAMsMACHVBwIAgA0AIdYHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyQ0AIDQAAIsNACA3AACRDQAgQAAAjA0AIEIAAIgNACBFAACCDQAgWAAAgw0AIFoAAIQNACBcAACGDQAgXQAAhw0AIF4AAIkNACBfAACKDQAgYAAAjQ0AIGEAAI4NACBiAACPDQAgYwAAkA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdgHAgDKDAAh2QcCAMoMACHaBwIAgA0AIdsHAgCADQAh3AcCAIANACHdBwIAgA0AId4HQADNDAAhQzMAAMsNACA0AAC6DQAgNwAAwA0AIEAAALsNACBCAAC3DQAgRQAAsQ0AIFgAALINACBaAACzDQAgXAAAtQ0AIF0AALYNACBeAAC4DQAgXwAAuQ0AIGAAALwNACBhAAC9DQAgYgAAvg0AIGMAAL8NACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB1QYBAAAAAeAGAgAAAAHnBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABjAcCAAAAAY8HAgAAAAGfByAAAAABoAcgAAAAAaEHIAAAAAGiByAAAAABowcgAAAAAaQHIAAAAAG4BwEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPBBwEAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAHHBwEAAAAByAcgAAAAAckHIAAAAAHKBwgAAAABzAcAAADMBwLNB0AAAAABzwcAAADPBwLQBwEAAAAB0QcgAAAAAdIHIAAAAAHTByAAAAAB1AcBAAAAAdUHAgAAAAHWBwIAAAAB2AcCAAAAAdkHAgAAAAHaBwIAAAAB2wcCAAAAAdwHAgAAAAHdBwIAAAAB3gdAAAAAAQQSAACMEgAwwwgAAI0SADDHCAAA9AwAMMgIAACPEgAgAAAAAAALEgAAnBIAMBMAAKASADDDCAAAnRIAMMQIAACeEgAwxQgAAPQMADDGCAAA9AwAMMcIAAD0DAAwyAgAAJ8SACDJCAAA9AwAMMoIAAChEgAwywgAAPcMADBDMwAAyw0AIDQAALoNACA3AADADQAgQAAAuw0AIEIAALcNACBFAACxDQAgWAAAsg0AIFoAALMNACBbAAC0DQAgXAAAtQ0AIF4AALgNACBfAAC5DQAgYAAAvA0AIGEAAL0NACBiAAC-DQAgYwAAvw0AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHVBgEAAAAB4AYCAAAAAecGIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAGMBwIAAAABjwcCAAAAAZ8HIAAAAAGgByAAAAABoQcgAAAAAaIHIAAAAAGjByAAAAABpAcgAAAAAbgHAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8EHAQAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAccHAQAAAAHIByAAAAAByQcgAAAAAcoHCAAAAAHMBwAAAMwHAs0HQAAAAAHPBwAAAM8HAtAHAQAAAAHRByAAAAAB0gcgAAAAAdMHIAAAAAHUBwEAAAAB1QcCAAAAAdYHAgAAAAHXBwIAAAAB2AcCAAAAAdoHAgAAAAHbBwIAAAAB3AcCAAAAAd0HAgAAAAHeB0AAAAABAgAAAHcAIBIAAKQSACADAAAAdwAgEgAApBIAIBMAAKMSACABCwAAtRYAMAIAAAB3ACALAACjEgAgAgAAAPgMACALAACiEgAgM68GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHaBwIAgA0AIdsHAgCADQAh3AcCAIANACHdBwIAgA0AId4HQADNDAAhQzMAAMkNACA0AACLDQAgNwAAkQ0AIEAAAIwNACBCAACIDQAgRQAAgg0AIFgAAIMNACBaAACEDQAgWwAAhQ0AIFwAAIYNACBeAACJDQAgXwAAig0AIGAAAI0NACBhAACODQAgYgAAjw0AIGMAAJANACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdUGAQDLDAAh4AYCAIANACHnBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIYwHAgCADQAhjwcCAMoMACGfByAAzAwAIaAHIADMDAAhoQcgAMwMACGiByAAzAwAIaMHIADMDAAhpAcgAMwMACG4BwEAyQwAIbkHAQDLDAAhugcBAMsMACG7BwEAywwAIb0HAAD6DL0HI74HAQDLDAAhwAcAAPsMwAcjwQcBAMsMACHCBwEAywwAIcMHAQDLDAAhxQcAAPwMxQcjxgcBAMsMACHHBwEAywwAIcgHIADMDAAhyQcgAMwMACHKBwgA_QwAIcwHAAD-DMwHIs0HQADNDAAhzwcAAP8Mzwci0AcBAMsMACHRByAAzAwAIdIHIADMDAAh0wcgAMwMACHUBwEAywwAIdUHAgCADQAh1gcCAMoMACHXBwIAygwAIdgHAgDKDAAh2gcCAIANACHbBwIAgA0AIdwHAgCADQAh3QcCAIANACHeB0AAzQwAIUMzAADLDQAgNAAAug0AIDcAAMANACBAAAC7DQAgQgAAtw0AIEUAALENACBYAACyDQAgWgAAsw0AIFsAALQNACBcAAC1DQAgXgAAuA0AIF8AALkNACBgAAC8DQAgYQAAvQ0AIGIAAL4NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHgBgIAAAAB5wYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAYwHAgAAAAGPBwIAAAABnwcgAAAAAaAHIAAAAAGhByAAAAABogcgAAAAAaMHIAAAAAGkByAAAAABuAcBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwQcBAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABxwcBAAAAAcgHIAAAAAHJByAAAAABygcIAAAAAcwHAAAAzAcCzQdAAAAAAc8HAAAAzwcC0AcBAAAAAdEHIAAAAAHSByAAAAAB0wcgAAAAAdQHAQAAAAHVBwIAAAAB1gcCAAAAAdcHAgAAAAHYBwIAAAAB2gcCAAAAAdsHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAEEEgAAnBIAMMMIAACdEgAwxwgAAPQMADDICAAAnxIAIAAAAAAACxIAAKwSADATAACwEgAwwwgAAK0SADDECAAArhIAMMUIAAD0DAAwxggAAPQMADDHCAAA9AwAMMgIAACvEgAgyQgAAPQMADDKCAAAsRIAMMsIAAD3DAAwQzMAAMsNACA0AAC6DQAgNwAAwA0AIEAAALsNACBFAACxDQAgWAAAsg0AIFoAALMNACBbAAC0DQAgXAAAtQ0AIF0AALYNACBeAAC4DQAgXwAAuQ0AIGAAALwNACBhAAC9DQAgYgAAvg0AIGMAAL8NACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB1QYBAAAAAeAGAgAAAAHnBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABjAcCAAAAAZ8HIAAAAAGgByAAAAABoQcgAAAAAaIHIAAAAAGjByAAAAABpAcgAAAAAbgHAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8EHAQAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAccHAQAAAAHIByAAAAAByQcgAAAAAcoHCAAAAAHMBwAAAMwHAs0HQAAAAAHPBwAAAM8HAtAHAQAAAAHRByAAAAAB0gcgAAAAAdMHIAAAAAHUBwEAAAAB1QcCAAAAAdYHAgAAAAHXBwIAAAAB2AcCAAAAAdkHAgAAAAHaBwIAAAAB2wcCAAAAAdwHAgAAAAHdBwIAAAAB3gdAAAAAAQIAAAB3ACASAAC0EgAgAwAAAHcAIBIAALQSACATAACzEgAgAQsAALQWADACAAAAdwAgCwAAsxIAIAIAAAD4DAAgCwAAshIAIDOvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdUGAQDLDAAh4AYCAIANACHnBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIYwHAgCADQAhnwcgAMwMACGgByAAzAwAIaEHIADMDAAhogcgAMwMACGjByAAzAwAIaQHIADMDAAhuAcBAMkMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8EHAQDLDAAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAhxwcBAMsMACHIByAAzAwAIckHIADMDAAhygcIAP0MACHMBwAA_gzMByLNB0AAzQwAIc8HAAD_DM8HItAHAQDLDAAh0QcgAMwMACHSByAAzAwAIdMHIADMDAAh1AcBAMsMACHVBwIAgA0AIdYHAgDKDAAh1wcCAMoMACHYBwIAygwAIdkHAgDKDAAh2gcCAIANACHbBwIAgA0AIdwHAgCADQAh3QcCAIANACHeB0AAzQwAIUMzAADJDQAgNAAAiw0AIDcAAJENACBAAACMDQAgRQAAgg0AIFgAAIMNACBaAACEDQAgWwAAhQ0AIFwAAIYNACBdAACHDQAgXgAAiQ0AIF8AAIoNACBgAACNDQAgYQAAjg0AIGIAAI8NACBjAACQDQAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIeAGAgCADQAh5wYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACGMBwIAgA0AIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyw0AIDQAALoNACA3AADADQAgQAAAuw0AIEUAALENACBYAACyDQAgWgAAsw0AIFsAALQNACBcAAC1DQAgXQAAtg0AIF4AALgNACBfAAC5DQAgYAAAvA0AIGEAAL0NACBiAAC-DQAgYwAAvw0AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHVBgEAAAAB4AYCAAAAAecGIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAGMBwIAAAABnwcgAAAAAaAHIAAAAAGhByAAAAABogcgAAAAAaMHIAAAAAGkByAAAAABuAcBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwQcBAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABxwcBAAAAAcgHIAAAAAHJByAAAAABygcIAAAAAcwHAAAAzAcCzQdAAAAAAc8HAAAAzwcC0AcBAAAAAdEHIAAAAAHSByAAAAAB0wcgAAAAAdQHAQAAAAHVBwIAAAAB1gcCAAAAAdcHAgAAAAHYBwIAAAAB2QcCAAAAAdoHAgAAAAHbBwIAAAAB3AcCAAAAAd0HAgAAAAHeB0AAAAABBBIAAKwSADDDCAAArRIAMMcIAAD0DAAwyAgAAK8SACAAAAAAAAUSAACuFgAgEwAAshYAIMMIAACvFgAgxAgAALEWACDHCAAA2gIAIAsSAAC9EgAwEwAAwRIAMMMIAAC-EgAwxAgAAL8SADDFCAAA9AwAMMYIAAD0DAAwxwgAAPQMADDICAAAwBIAIMkIAAD0DAAwyggAAMISADDLCAAA9wwAMEMzAADLDQAgNAAAug0AIDcAAMANACBAAAC7DQAgQgAAtw0AIEUAALENACBYAACyDQAgWgAAsw0AIFsAALQNACBcAAC1DQAgXQAAtg0AIF8AALkNACBgAAC8DQAgYQAAvQ0AIGIAAL4NACBjAAC_DQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHgBgIAAAAB5wYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAYwHAgAAAAGPBwIAAAABnwcgAAAAAaAHIAAAAAGhByAAAAABogcgAAAAAaMHIAAAAAGkByAAAAABuAcBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwQcBAAAAAcIHAQAAAAHDBwEAAAABxQcAAADFBwPGBwEAAAABxwcBAAAAAcgHIAAAAAHJByAAAAABygcIAAAAAcwHAAAAzAcCzQdAAAAAAc8HAAAAzwcC0AcBAAAAAdEHIAAAAAHSByAAAAAB0wcgAAAAAdQHAQAAAAHWBwIAAAAB1wcCAAAAAdgHAgAAAAHZBwIAAAAB2gcCAAAAAdsHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAECAAAAdwAgEgAAxRIAIAMAAAB3ACASAADFEgAgEwAAxBIAIAELAACwFgAwAgAAAHcAIAsAAMQSACACAAAA-AwAIAsAAMMSACAzrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHVBgEAywwAIeAGAgCADQAh5wYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACGMBwIAgA0AIY8HAgDKDAAhnwcgAMwMACGgByAAzAwAIaEHIADMDAAhogcgAMwMACGjByAAzAwAIaQHIADMDAAhuAcBAMkMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8EHAQDLDAAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAhxwcBAMsMACHIByAAzAwAIckHIADMDAAhygcIAP0MACHMBwAA_gzMByLNB0AAzQwAIc8HAAD_DM8HItAHAQDLDAAh0QcgAMwMACHSByAAzAwAIdMHIADMDAAh1AcBAMsMACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFDMwAAyQ0AIDQAAIsNACA3AACRDQAgQAAAjA0AIEIAAIgNACBFAACCDQAgWAAAgw0AIFoAAIQNACBbAACFDQAgXAAAhg0AIF0AAIcNACBfAACKDQAgYAAAjQ0AIGEAAI4NACBiAACPDQAgYwAAkA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1gcCAMoMACHXBwIAygwAIdgHAgDKDAAh2QcCAMoMACHaBwIAgA0AIdsHAgCADQAh3AcCAIANACHdBwIAgA0AId4HQADNDAAhQzMAAMsNACA0AAC6DQAgNwAAwA0AIEAAALsNACBCAAC3DQAgRQAAsQ0AIFgAALINACBaAACzDQAgWwAAtA0AIFwAALUNACBdAAC2DQAgXwAAuQ0AIGAAALwNACBhAAC9DQAgYgAAvg0AIGMAAL8NACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB1QYBAAAAAeAGAgAAAAHnBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABjAcCAAAAAY8HAgAAAAGfByAAAAABoAcgAAAAAaEHIAAAAAGiByAAAAABowcgAAAAAaQHIAAAAAG4BwEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPBBwEAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAHHBwEAAAAByAcgAAAAAckHIAAAAAHKBwgAAAABzAcAAADMBwLNB0AAAAABzwcAAADPBwLQBwEAAAAB0QcgAAAAAdIHIAAAAAHTByAAAAAB1AcBAAAAAdYHAgAAAAHXBwIAAAAB2AcCAAAAAdkHAgAAAAHaBwIAAAAB2wcCAAAAAdwHAgAAAAHdBwIAAAAB3gdAAAAAAQMSAACuFgAgwwgAAK8WACDHCAAA2gIAIAQSAAC9EgAwwwgAAL4SADDHCAAA9AwAMMgIAADAEgAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoSAACgEwAwEwAAoxMAMMMIAAChEwAwxAgAAKITADDFCAAAkhMAMMYIAACSEwAwxwgAAJITADDJCAAAkhMAMMoIAACkEwAwywgAAJUTADAKEgAAjxMAMBMAAJMTADDDCAAAkBMAMMQIAACREwAwxQgAAJITADDGCAAAkhMAMMcIAACSEwAwyQgAAJITADDKCAAAlBMAMMsIAACVEwAwBRIAAJQWACATAACsFgAgwwgAAJUWACDECAAAqxYAIMcIAACKAQAgBxIAAJIWACATAACpFgAgwwgAAJMWACDECAAAqBYAIMUIAACPAQAgxggAAI8BACDHCAAAtgQAIAsSAAD_EgAwEwAAhBMAMMMIAACAEwAwxAgAAIETADDFCAAAgxMAMMYIAACDEwAwxwgAAIMTADDICAAAghMAIMkIAACDEwAwyggAAIUTADDLCAAAhhMAMAsSAADxEgAwEwAA9hIAMMMIAADyEgAwxAgAAPMSADDFCAAA9RIAMMYIAAD1EgAwxwgAAPUSADDICAAA9BIAIMkIAAD1EgAwyggAAPcSADDLCAAA-BIAMAsSAADoEgAwEwAA7BIAMMMIAADpEgAwxAgAAOoSADDFCAAAlg0AMMYIAACWDQAwxwgAAJYNADDICAAA6xIAIMkIAACWDQAwyggAAO0SADDLCAAAmQ0AMCk2AACiDQAgRAAAow4AIFcAAKQNACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAdQGAgAAAAHVBgEAAAAB7AYgAAAAAe0GIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAG3BwIAAAAB3wcIAAAAAeAHAQAAAAHhBwEAAAAB4gcCAAAAAeMHAQAAAAHkBwEAAAAB5QcIAAAAAeYHCAAAAAHnBwgAAAAB6AcIAAAAAekHCAAAAAHqBwgAAAAB6wcCAAAAAewHAgAAAAHtBwEAAAAB7gcIAAAAAe8HIAAAAAHwByAAAAAB8QcgAAAAAfIHIAAAAAHzByAAAAAB9AcgAAAAAfYHAgAAAAH3BwEAAAAB-AdAAAAAAQIAAAB_ACASAADwEgAgAwAAAH8AIBIAAPASACATAADvEgAgAQsAAKcWADACAAAAfwAgCwAA7xIAIAIAAACaDQAgCwAA7hIAICavBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1AYCAIANACHVBgEAywwAIewGIADMDAAh7QYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACG3BwIAgA0AId8HCAD9DAAh4AcBAMsMACHhBwEAywwAIeIHAgCADQAh4wcBAMsMACHkBwEAywwAIeUHCAD9DAAh5gcIAP0MACHnBwgA_QwAIegHCAD9DAAh6QcIAP0MACHqBwgA_QwAIesHAgDKDAAh7AcCAMoMACHtBwEAywwAIe4HCACcDQAh7wcgAMwMACHwByAAzAwAIfEHIADMDAAh8gcgAMwMACHzByAAzAwAIfQHIADMDAAh9gcCAMoMACH3BwEAyQwAIfgHQADNDAAhKTYAAJ4NACBEAAChDgAgVwAAoA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHUBgIAgA0AIdUGAQDLDAAh7AYgAMwMACHtBiAAzAwAIfgGAQDLDAAh-QYBAMsMACH6BkAAzQwAIbcHAgCADQAh3wcIAP0MACHgBwEAywwAIeEHAQDLDAAh4gcCAIANACHjBwEAywwAIeQHAQDLDAAh5QcIAP0MACHmBwgA_QwAIecHCAD9DAAh6AcIAP0MACHpBwgA_QwAIeoHCAD9DAAh6wcCAMoMACHsBwIAygwAIe0HAQDLDAAh7gcIAJwNACHvByAAzAwAIfAHIADMDAAh8QcgAMwMACHyByAAzAwAIfMHIADMDAAh9AcgAMwMACH2BwIAygwAIfcHAQDJDAAh-AdAAM0MACEpNgAAog0AIEQAAKMOACBXAACkDQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHUBgIAAAAB1QYBAAAAAewGIAAAAAHtBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABtwcCAAAAAd8HCAAAAAHgBwEAAAAB4QcBAAAAAeIHAgAAAAHjBwEAAAAB5AcBAAAAAeUHCAAAAAHmBwgAAAAB5wcIAAAAAegHCAAAAAHpBwgAAAAB6gcIAAAAAesHAgAAAAHsBwIAAAAB7QcBAAAAAe4HCAAAAAHvByAAAAAB8AcgAAAAAfEHIAAAAAHyByAAAAAB8wcgAAAAAfQHIAAAAAH2BwIAAAAB9wcBAAAAAfgHQAAAAAEJTgAA_hIAIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAH4BgEAAAABhwgCAAAAAYgIAgAAAAGKCAEAAAABAgAAAJwBACASAAD9EgAgAwAAAJwBACASAAD9EgAgEwAA-xIAIAELAACmFgAwDk4AAPwLACBPAAD9CwAgrAYAAPsLADCtBgAAmgEAEK4GAAD7CwAwrwYCAAAAAbgGQAD2CgAhuQZAAPYKACHFBgEAAAAB-AYBAPMKACGHCAIAlAsAIYgIAgCUCwAhiQgCAJcLACGKCAEA8woAIQIAAACcAQAgCwAA-xIAIAIAAAD5EgAgCwAA-hIAIAysBgAA-BIAMK0GAAD5EgAQrgYAAPgSADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIfgGAQDzCgAhhwgCAJQLACGICAIAlAsAIYkIAgCXCwAhiggBAPMKACEMrAYAAPgSADCtBgAA-RIAEK4GAAD4EgAwrwYCAJQLACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACH4BgEA8woAIYcIAgCUCwAhiAgCAJQLACGJCAIAlwsAIYoIAQDzCgAhCK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh-AYBAMsMACGHCAIAygwAIYgIAgDKDAAhiggBAMsMACEJTgAA_BIAIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh-AYBAMsMACGHCAIAygwAIYgIAgDKDAAhiggBAMsMACEFEgAAoRYAIBMAAKQWACDDCAAAohYAIMQIAACjFgAgxwgAAIAFACAJTgAA_hIAIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAH4BgEAAAABhwgCAAAAAYgIAgAAAAGKCAEAAAABAxIAAKEWACDDCAAAohYAIMcIAACABQAgCk4AAI4TACBTAACNEwAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAfgGAQAAAAGICAIAAAABjAgCAAAAAY0IAgAAAAECAAAAlQEAIBIAAIwTACADAAAAlQEAIBIAAIwTACATAACJEwAgAQsAAKAWADAPTgAA-gsAIE8AAP0LACBTAAD_CwAgrAYAAP4LADCtBgAAkwEAEK4GAAD-CwAwrwYCAAAAAbgGQAD2CgAhuQZAAPYKACHFBgEAAAAB-AYBAPMKACGICAIAlwsAIYkIAgCXCwAhjAgCAJQLACGNCAIAlAsAIQIAAACVAQAgCwAAiRMAIAIAAACHEwAgCwAAiBMAIAysBgAAhhMAMK0GAACHEwAQrgYAAIYTADCvBgIAlAsAIbgGQAD2CgAhuQZAAPYKACHFBgEA8goAIfgGAQDzCgAhiAgCAJcLACGJCAIAlwsAIYwIAgCUCwAhjQgCAJQLACEMrAYAAIYTADCtBgAAhxMAEK4GAACGEwAwrwYCAJQLACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACH4BgEA8woAIYgIAgCXCwAhiQgCAJcLACGMCAIAlAsAIY0IAgCUCwAhCK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh-AYBAMsMACGICAIAgA0AIYwIAgDKDAAhjQgCAMoMACEKTgAAixMAIFMAAIoTACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIfgGAQDLDAAhiAgCAIANACGMCAIAygwAIY0IAgDKDAAhBRIAAJgWACATAACeFgAgwwgAAJkWACDECAAAnRYAIMcIAACiAQAgBxIAAJYWACATAACbFgAgwwgAAJcWACDECAAAmhYAIMUIAACYAQAgxggAAJgBACDHCAAAgAUAIApOAACOEwAgUwAAjRMAIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAH4BgEAAAABiAgCAAAAAYwIAgAAAAGNCAIAAAABAxIAAJgWACDDCAAAmRYAIMcIAACiAQAgAxIAAJYWACDDCAAAlxYAIMcIAACABQAgHEcAAJoTACBLAACbEwAgTAAAnBMAIFEAAJ0TACBUAACeEwAgVQAAnxMAIK8GAgAAAAGxBgEAAAABtwZAAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHcBgEAAAAB-QcBAAAAAfoHAQAAAAH7BwIAAAAB_AcCAAAAAf0HAQAAAAH-BwEAAAAB_wcBAAAAAYAIAgAAAAGBCAgAAAABgggIAAAAAYMIIAAAAAGECCAAAAABhQgCAAAAAYYIAQAAAAECAAAAhQEAIBIAAJkTACADAAAAhQEAIBIAAJkTACATAACYEwAgIEYAANULACBHAADVCwAgSwAAhAwAIEwAAIUMACBRAADPCwAgVAAAzgsAIFUAAOoLACCsBgAAggwAMK0GAACDAQAQrgYAAIIMADCvBgIAAAABsQYBAAAAAbcGQAD1CgAhuAZAAPYKACG5BkAA9goAIcUGAQAAAAHIBgEA8woAIdwGAQDyCgAh-QcBAPMKACH6BwEA8woAIfsHAgCUCwAh_AcCAJcLACH9BwEA8woAIf4HAQDzCgAh_wcBAPMKACGACAIAlwsAIYEICACDDAAhgggIAIMMACGDCCAA9AoAIYQIIAD0CgAhhQgCAJQLACGGCAEA8woAIQIAAACFAQAgCwAAmBMAIAIAAACWEwAgCwAAlxMAIBmsBgAAlRMAMK0GAACWEwAQrgYAAJUTADCvBgIAlAsAIbEGAQDzCgAhtwZAAPUKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdwGAQDyCgAh-QcBAPMKACH6BwEA8woAIfsHAgCUCwAh_AcCAJcLACH9BwEA8woAIf4HAQDzCgAh_wcBAPMKACGACAIAlwsAIYEICACDDAAhgggIAIMMACGDCCAA9AoAIYQIIAD0CgAhhQgCAJQLACGGCAEA8woAIRmsBgAAlRMAMK0GAACWEwAQrgYAAJUTADCvBgIAlAsAIbEGAQDzCgAhtwZAAPUKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIdwGAQDyCgAh-QcBAPMKACH6BwEA8woAIfsHAgCUCwAh_AcCAJcLACH9BwEA8woAIf4HAQDzCgAh_wcBAPMKACGACAIAlwsAIYEICACDDAAhgggIAIMMACGDCCAA9AoAIYQIIAD0CgAhhQgCAJQLACGGCAEA8woAIRavBgIAygwAIbEGAQDLDAAhtwZAAM0MACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdwGAQDJDAAh-QcBAMsMACH6BwEAywwAIfsHAgDKDAAh_AcCAIANACH9BwEAywwAIf4HAQDLDAAh_wcBAMsMACGACAIAgA0AIYEICAD9DAAhgggIAP0MACGDCCAAzAwAIYQIIADMDAAhhQgCAMoMACGGCAEAywwAIRxHAADiEgAgSwAA4xIAIEwAAOQSACBRAADlEgAgVAAA5hIAIFUAAOcSACCvBgIAygwAIbEGAQDLDAAhtwZAAM0MACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdwGAQDJDAAh-QcBAMsMACH6BwEAywwAIfsHAgDKDAAh_AcCAIANACH9BwEAywwAIf4HAQDLDAAh_wcBAMsMACGACAIAgA0AIYEICAD9DAAhgggIAP0MACGDCCAAzAwAIYQIIADMDAAhhQgCAMoMACGGCAEAywwAIRxHAACaEwAgSwAAmxMAIEwAAJwTACBRAACdEwAgVAAAnhMAIFUAAJ8TACCvBgIAAAABsQYBAAAAAbcGQAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAAB3AYBAAAAAfkHAQAAAAH6BwEAAAAB-wcCAAAAAfwHAgAAAAH9BwEAAAAB_gcBAAAAAf8HAQAAAAGACAIAAAABgQgIAAAAAYIICAAAAAGDCCAAAAABhAggAAAAAYUIAgAAAAGGCAEAAAABAxIAAI8TADDDCAAAkBMAMMcIAACSEwAwAxIAAJQWACDDCAAAlRYAIMcIAACKAQAgAxIAAJIWACDDCAAAkxYAIMcIAAC2BAAgBBIAAP8SADDDCAAAgBMAMMcIAACDEwAwyAgAAIITACAEEgAA8RIAMMMIAADyEgAwxwgAAPUSADDICAAA9BIAIAQSAADoEgAwwwgAAOkSADDHCAAAlg0AMMgIAADrEgAgHEYAAKgTACBLAACbEwAgTAAAnBMAIFEAAJ0TACBUAACeEwAgVQAAnxMAIK8GAgAAAAGxBgEAAAABtwZAAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHcBgEAAAAB-QcBAAAAAfoHAQAAAAH7BwIAAAAB_AcCAAAAAf0HAQAAAAH-BwEAAAAB_wcBAAAAAYAIAgAAAAGBCAgAAAABgggIAAAAAYMIIAAAAAGECCAAAAABhQgCAAAAAYYIAQAAAAECAAAAhQEAIBIAAKcTACADAAAAhQEAIBIAAKcTACATAACmEwAgAgAAAIUBACALAACmEwAgAgAAAJYTACALAAClEwAgFq8GAgDKDAAhsQYBAMsMACG3BkAAzQwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh3AYBAMkMACH5BwEAywwAIfoHAQDLDAAh-wcCAMoMACH8BwIAgA0AIf0HAQDLDAAh_gcBAMsMACH_BwEAywwAIYAIAgCADQAhgQgIAP0MACGCCAgA_QwAIYMIIADMDAAhhAggAMwMACGFCAIAygwAIYYIAQDLDAAhHEYAAOESACBLAADjEgAgTAAA5BIAIFEAAOUSACBUAADmEgAgVQAA5xIAIK8GAgDKDAAhsQYBAMsMACG3BkAAzQwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh3AYBAMkMACH5BwEAywwAIfoHAQDLDAAh-wcCAMoMACH8BwIAgA0AIf0HAQDLDAAh_gcBAMsMACH_BwEAywwAIYAIAgCADQAhgQgIAP0MACGCCAgA_QwAIYMIIADMDAAhhAggAMwMACGFCAIAygwAIYYIAQDLDAAhHEYAAKgTACBLAACbEwAgTAAAnBMAIFEAAJ0TACBUAACeEwAgVQAAnxMAIK8GAgAAAAGxBgEAAAABtwZAAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHcBgEAAAAB-QcBAAAAAfoHAQAAAAH7BwIAAAAB_AcCAAAAAf0HAQAAAAH-BwEAAAAB_wcBAAAAAYAIAgAAAAGBCAgAAAABgggIAAAAAYMIIAAAAAGECCAAAAABhQgCAAAAAYYIAQAAAAEDEgAAoBMAMMMIAAChEwAwxwgAAJITADAAAAAAAAcSAACNFgAgEwAAkBYAIMMIAACOFgAgxAgAAI8WACDFCAAAgwEAIMYIAACDAQAgxwgAAIUBACADEgAAjRYAIMMIAACOFgAgxwgAAIUBACAAAAAAAAsSAADaEwAwEwAA3hMAMMMIAADbEwAwxAgAANwTADDFCAAA9RIAMMYIAAD1EgAwxwgAAPUSADDICAAA3RMAIMkIAAD1EgAwyggAAN8TADDLCAAA-BIAMAsSAADREwAwEwAA1RMAMMMIAADSEwAwxAgAANMTADDFCAAAgxMAMMYIAACDEwAwxwgAAIMTADDICAAA1BMAIMkIAACDEwAwyggAANYTADDLCAAAhhMAMAsSAAC4EwAwEwAAvRMAMMMIAAC5EwAwxAgAALoTADDFCAAAvBMAMMYIAAC8EwAwxwgAALwTADDICAAAuxMAIMkIAAC8EwAwyggAAL4TADDLCAAAvxMAMA1NAADQEwAgrwYCAAAAAbUGIAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAABygYgAAAAAdUGAQAAAAHcBgEAAAAB-AYBAAAAAY4IAgAAAAGPCAIAAAABAgAAAKIBACASAADPEwAgAwAAAKIBACASAADPEwAgEwAAwhMAIAELAACMFgAwEk0AAM8LACBOAAD6CwAgrAYAAPkLADCtBgAAoAEAEK4GAAD5CwAwrwYCAAAAAbUGIAD0CgAhuAZAAPYKACG5BkAA9goAIcUGAQAAAAHIBgEA8woAIcoGIAD0CgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhiAgCAJcLACGOCAIAlAsAIY8IAgCUCwAhAgAAAKIBACALAADCEwAgAgAAAMATACALAADBEwAgEKwGAAC_EwAwrQYAAMATABCuBgAAvxMAMK8GAgCUCwAhtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIcoGIAD0CgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhiAgCAJcLACGOCAIAlAsAIY8IAgCUCwAhEKwGAAC_EwAwrQYAAMATABCuBgAAvxMAMK8GAgCUCwAhtQYgAPQKACG4BkAA9goAIbkGQAD2CgAhxQYBAPIKACHIBgEA8woAIcoGIAD0CgAh1QYBAPMKACHcBgEA8goAIfgGAQDzCgAhiAgCAJcLACGOCAIAlAsAIY8IAgCUCwAhDK8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIcoGIADMDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhjggCAMoMACGPCAIAygwAIQ1NAADDEwAgrwYCAMoMACG1BiAAzAwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAhygYgAMwMACHVBgEAywwAIdwGAQDJDAAh-AYBAMsMACGOCAIAygwAIY8IAgDKDAAhCxIAAMQTADATAADIEwAwwwgAAMUTADDECAAAxhMAMMUIAACDEwAwxggAAIMTADDHCAAAgxMAMMgIAADHEwAgyQgAAIMTADDKCAAAyRMAMMsIAACGEwAwCk4AAI4TACBPAADOEwAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAfgGAQAAAAGICAIAAAABiQgCAAAAAYwIAgAAAAECAAAAlQEAIBIAAM0TACADAAAAlQEAIBIAAM0TACATAADLEwAgAQsAAIsWADACAAAAlQEAIAsAAMsTACACAAAAhxMAIAsAAMoTACAIrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACH4BgEAywwAIYgIAgCADQAhiQgCAIANACGMCAIAygwAIQpOAACLEwAgTwAAzBMAIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh-AYBAMsMACGICAIAgA0AIYkIAgCADQAhjAgCAMoMACEHEgAAhhYAIBMAAIkWACDDCAAAhxYAIMQIAACIFgAgxQgAAIMBACDGCAAAgwEAIMcIAACFAQAgCk4AAI4TACBPAADOEwAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAfgGAQAAAAGICAIAAAABiQgCAAAAAYwIAgAAAAEDEgAAhhYAIMMIAACHFgAgxwgAAIUBACANTQAA0BMAIK8GAgAAAAG1BiAAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAcoGIAAAAAHVBgEAAAAB3AYBAAAAAfgGAQAAAAGOCAIAAAABjwgCAAAAAQQSAADEEwAwwwgAAMUTADDHCAAAgxMAMMgIAADHEwAgCk8AAM4TACBTAACNEwAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAfgGAQAAAAGJCAIAAAABjAgCAAAAAY0IAgAAAAECAAAAlQEAIBIAANkTACADAAAAlQEAIBIAANkTACATAADYEwAgAQsAAIUWADACAAAAlQEAIAsAANgTACACAAAAhxMAIAsAANcTACAIrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACH4BgEAywwAIYkIAgCADQAhjAgCAMoMACGNCAIAygwAIQpPAADMEwAgUwAAihMAIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh-AYBAMsMACGJCAIAgA0AIYwIAgDKDAAhjQgCAMoMACEKTwAAzhMAIFMAAI0TACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB-AYBAAAAAYkIAgAAAAGMCAIAAAABjQgCAAAAAQlPAACvEwAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAfgGAQAAAAGHCAIAAAABiQgCAAAAAYoIAQAAAAECAAAAnAEAIBIAAOITACADAAAAnAEAIBIAAOITACATAADhEwAgAQsAAIQWADACAAAAnAEAIAsAAOETACACAAAA-RIAIAsAAOATACAIrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACH4BgEAywwAIYcIAgDKDAAhiQgCAIANACGKCAEAywwAIQlPAACuEwAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACH4BgEAywwAIYcIAgDKDAAhiQgCAIANACGKCAEAywwAIQlPAACvEwAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAfgGAQAAAAGHCAIAAAABiQgCAAAAAYoIAQAAAAEEEgAA2hMAMMMIAADbEwAwxwgAAPUSADDICAAA3RMAIAQSAADREwAwwwgAANITADDHCAAAgxMAMMgIAADUEwAgBBIAALgTADDDCAAAuRMAMMcIAAC8EwAwyAgAALsTACAAAAAAAAAAAAAAAAAABxIAAP8VACATAACCFgAgwwgAAIAWACDECAAAgRYAIMUIAACYAQAgxggAAJgBACDHCAAAgAUAIAMSAAD_FQAgwwgAAIAWACDHCAAAgAUAIAAAAAAACxIAAPsTADATAAD_EwAwwwgAAPwTADDECAAA_RMAMMUIAACSEwAwxggAAJITADDHCAAAkhMAMMgIAAD-EwAgyQgAAJITADDKCAAAgBQAMMsIAACVEwAwG0YAAKgTACBHAACaEwAgSwAAmxMAIFEAAJ0TACBUAACeEwAgVQAAnxMAIK8GAgAAAAGxBgEAAAABtwZAAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHcBgEAAAAB-QcBAAAAAfoHAQAAAAH7BwIAAAAB_QcBAAAAAf4HAQAAAAH_BwEAAAABgAgCAAAAAYEICAAAAAGCCAgAAAABgwggAAAAAYQIIAAAAAGFCAIAAAABhggBAAAAAQIAAACFAQAgEgAAgxQAIAMAAACFAQAgEgAAgxQAIBMAAIIUACABCwAA_hUAMAIAAACFAQAgCwAAghQAIAIAAACWEwAgCwAAgRQAIBWvBgIAygwAIbEGAQDLDAAhtwZAAM0MACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdwGAQDJDAAh-QcBAMsMACH6BwEAywwAIfsHAgDKDAAh_QcBAMsMACH-BwEAywwAIf8HAQDLDAAhgAgCAIANACGBCAgA_QwAIYIICAD9DAAhgwggAMwMACGECCAAzAwAIYUIAgDKDAAhhggBAMsMACEbRgAA4RIAIEcAAOISACBLAADjEgAgUQAA5RIAIFQAAOYSACBVAADnEgAgrwYCAMoMACGxBgEAywwAIbcGQADNDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHcBgEAyQwAIfkHAQDLDAAh-gcBAMsMACH7BwIAygwAIf0HAQDLDAAh_gcBAMsMACH_BwEAywwAIYAIAgCADQAhgQgIAP0MACGCCAgA_QwAIYMIIADMDAAhhAggAMwMACGFCAIAygwAIYYIAQDLDAAhG0YAAKgTACBHAACaEwAgSwAAmxMAIFEAAJ0TACBUAACeEwAgVQAAnxMAIK8GAgAAAAGxBgEAAAABtwZAAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHcBgEAAAAB-QcBAAAAAfoHAQAAAAH7BwIAAAAB_QcBAAAAAf4HAQAAAAH_BwEAAAABgAgCAAAAAYEICAAAAAGCCAgAAAABgwggAAAAAYQIIAAAAAGFCAIAAAABhggBAAAAAQQSAAD7EwAwwwgAAPwTADDHCAAAkhMAMMgIAAD-EwAgAAAAAAAABRIAAPgVACATAAD8FQAgwwgAAPkVACDECAAA-xUAIMcIAACHBAAgCxIAAI0UADATAACRFAAwwwgAAI4UADDECAAAjxQAMMUIAACSEwAwxggAAJITADDHCAAAkhMAMMgIAACQFAAgyQgAAJITADDKCAAAkhQAMMsIAACVEwAwG0YAAKgTACBHAACaEwAgTAAAnBMAIFEAAJ0TACBUAACeEwAgVQAAnxMAIK8GAgAAAAGxBgEAAAABtwZAAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHcBgEAAAAB-QcBAAAAAfoHAQAAAAH8BwIAAAAB_QcBAAAAAf4HAQAAAAH_BwEAAAABgAgCAAAAAYEICAAAAAGCCAgAAAABgwggAAAAAYQIIAAAAAGFCAIAAAABhggBAAAAAQIAAACFAQAgEgAAlRQAIAMAAACFAQAgEgAAlRQAIBMAAJQUACABCwAA-hUAMAIAAACFAQAgCwAAlBQAIAIAAACWEwAgCwAAkxQAIBWvBgIAygwAIbEGAQDLDAAhtwZAAM0MACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdwGAQDJDAAh-QcBAMsMACH6BwEAywwAIfwHAgCADQAh_QcBAMsMACH-BwEAywwAIf8HAQDLDAAhgAgCAIANACGBCAgA_QwAIYIICAD9DAAhgwggAMwMACGECCAAzAwAIYUIAgDKDAAhhggBAMsMACEbRgAA4RIAIEcAAOISACBMAADkEgAgUQAA5RIAIFQAAOYSACBVAADnEgAgrwYCAMoMACGxBgEAywwAIbcGQADNDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHcBgEAyQwAIfkHAQDLDAAh-gcBAMsMACH8BwIAgA0AIf0HAQDLDAAh_gcBAMsMACH_BwEAywwAIYAIAgCADQAhgQgIAP0MACGCCAgA_QwAIYMIIADMDAAhhAggAMwMACGFCAIAygwAIYYIAQDLDAAhG0YAAKgTACBHAACaEwAgTAAAnBMAIFEAAJ0TACBUAACeEwAgVQAAnxMAIK8GAgAAAAGxBgEAAAABtwZAAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHcBgEAAAAB-QcBAAAAAfoHAQAAAAH8BwIAAAAB_QcBAAAAAf4HAQAAAAH_BwEAAAABgAgCAAAAAYEICAAAAAGCCAgAAAABgwggAAAAAYQIIAAAAAGFCAIAAAABhggBAAAAAQMSAAD4FQAgwwgAAPkVACDHCAAAhwQAIAQSAACNFAAwwwgAAI4UADDHCAAAkhMAMMgIAACQFAAgAAAAAAALEgAAnhQAMBMAAKMUADDDCAAAnxQAMMQIAACgFAAwxQgAAKIUADDGCAAAohQAMMcIAACiFAAwyAgAAKEUACDJCAAAohQAMMoIAACkFAAwywgAAKUUADAKSgAAlxQAIK8GAgAAAAG1BiAAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAdUGAQAAAAHcBgEAAAAB-AYBAAAAAQIAAACKAQAgEgAAqRQAIAMAAACKAQAgEgAAqRQAIBMAAKgUACABCwAA9xUAMA9JAACBDAAgSgAA1QsAIKwGAACADAAwrQYAAIgBABCuBgAAgAwAMK8GAgAAAAG1BiAA9AoAIbgGQAD2CgAhuQZAAPYKACHFBgEAAAAByAYBAPMKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACGQCAIAlAsAIQIAAACKAQAgCwAAqBQAIAIAAACmFAAgCwAApxQAIA2sBgAApRQAMK0GAACmFAAQrgYAAKUUADCvBgIAlAsAIbUGIAD0CgAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACGQCAIAlAsAIQ2sBgAApRQAMK0GAACmFAAQrgYAAKUUADCvBgIAlAsAIbUGIAD0CgAhuAZAAPYKACG5BkAA9goAIcUGAQDyCgAhyAYBAPMKACHVBgEA8woAIdwGAQDyCgAh-AYBAPMKACGQCAIAlAsAIQmvBgIAygwAIbUGIADMDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHVBgEAywwAIdwGAQDJDAAh-AYBAMsMACEKSgAAjBQAIK8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdUGAQDLDAAh3AYBAMkMACH4BgEAywwAIQpKAACXFAAgrwYCAAAAAbUGIAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAAB1QYBAAAAAdwGAQAAAAH4BgEAAAABBBIAAJ4UADDDCAAAnxQAMMcIAACiFAAwyAgAAKEUACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcSAADyFQAgEwAA9RUAIMMIAADzFQAgxAgAAPQVACDFCAAARgAgxggAAEYAIMcIAABIACADEgAA8hUAIMMIAADzFQAgxwgAAEgAIAAAAAAACxIAAM0UADATAADRFAAwwwgAAM4UADDECAAAzxQAMMUIAACEDgAwxggAAIQOADDHCAAAhA4AMMgIAADQFAAgyQgAAIQOADDKCAAA0hQAMMsIAACHDgAwKjMBAAAAATQAALwOACA4AAC1DgAgOgAAtg4AIDsAALcOACA8AAC4DgAgPQAAuQ4AID4AALoOACA_AAC7DgAgQAAAvQ4AIEEAAOMOACBCAAC-DgAgRQEAAAABWAAAwA4AIF0BAAAAAa8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHcBgEAAAAB3gYCAAAAAeAGAgAAAAHnBiAAAAAB9gYCAAAAAfkGAQAAAAGEBwEAAAABhQcCAAAAAYYHAgAAAAGIBwAAAIgHA4kHCAAAAAGKBwEAAAABiwcBAAAAAYwHAgAAAAGNBwIAAAABjgcBAAAAAY8HAgAAAAGRB0AAAAABkgdAAAAAAZMHQAAAAAGUB0AAAAABlQcAALQOACCWBwEAAAABAgAAAFQAIBIAANUUACADAAAAVAAgEgAA1RQAIBMAANQUACABCwAA8RUAMAIAAABUACALAADUFAAgAgAAAIgOACALAADTFAAgHjMBAMkMACFFAQDLDAAhXQEAywwAIa8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHeBgIAygwAIeAGAgCADQAh5wYgAMwMACH2BgIAgA0AIfkGAQDLDAAhhAcBAMsMACGFBwIAgA0AIYYHAgCADQAhiAcAAIoOiAcjiQcIAP0MACGKBwEAywwAIYsHAQDLDAAhjAcCAIANACGNBwIAgA0AIY4HAQDLDAAhjwcCAIANACGRB0AAzQwAIZIHQADNDAAhkwdAAM0MACGUB0AAzQwAIZUHAACLDgAglgcBAMkMACEqMwEAyQwAITQAAJQOACA4AACNDgAgOgAAjg4AIDsAAI8OACA8AACQDgAgPQAAkQ4AID4AAJIOACA_AACTDgAgQAAAlQ4AIEEAAOEOACBCAACWDgAgRQEAywwAIVgAAJgOACBdAQDLDAAhrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAId4GAgDKDAAh4AYCAIANACHnBiAAzAwAIfYGAgCADQAh-QYBAMsMACGEBwEAywwAIYUHAgCADQAhhgcCAIANACGIBwAAig6IByOJBwgA_QwAIYoHAQDLDAAhiwcBAMsMACGMBwIAgA0AIY0HAgCADQAhjgcBAMsMACGPBwIAgA0AIZEHQADNDAAhkgdAAM0MACGTB0AAzQwAIZQHQADNDAAhlQcAAIsOACCWBwEAyQwAISozAQAAAAE0AAC8DgAgOAAAtQ4AIDoAALYOACA7AAC3DgAgPAAAuA4AID0AALkOACA-AAC6DgAgPwAAuw4AIEAAAL0OACBBAADjDgAgQgAAvg4AIEUBAAAAAVgAAMAOACBdAQAAAAGvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAd4GAgAAAAHgBgIAAAAB5wYgAAAAAfYGAgAAAAH5BgEAAAABhAcBAAAAAYUHAgAAAAGGBwIAAAABiAcAAACIBwOJBwgAAAABigcBAAAAAYsHAQAAAAGMBwIAAAABjQcCAAAAAY4HAQAAAAGPBwIAAAABkQdAAAAAAZIHQAAAAAGTB0AAAAABlAdAAAAAAZUHAAC0DgAglgcBAAAAAQQSAADNFAAwwwgAAM4UADDHCAAAhA4AMMgIAADQFAAgAAAAAAALEgAA9BQAMBMAAPgUADDDCAAA9RQAMMQIAAD2FAAwxQgAAIQOADDGCAAAhA4AMMcIAACEDgAwyAgAAPcUACDJCAAAhA4AMMoIAAD5FAAwywgAAIcOADALEgAA6BQAMBMAAO0UADDDCAAA6RQAMMQIAADqFAAwxQgAAOwUADDGCAAA7BQAMMcIAADsFAAwyAgAAOsUACDJCAAA7BQAMMoIAADuFAAwywgAAO8UADALEgAA3xQAMBMAAOMUADDDCAAA4BQAMMQIAADhFAAwxQgAAJYNADDGCAAAlg0AMMcIAACWDQAwyAgAAOIUACDJCAAAlg0AMMoIAADkFAAwywgAAJkNADApNgAAog0AIEQAAKMOACBWAACjDQAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHUBgIAAAAB1QYBAAAAAewGIAAAAAHtBiAAAAAB-AYBAAAAAfkGAQAAAAH6BkAAAAABtwcCAAAAAd8HCAAAAAHgBwEAAAAB4QcBAAAAAeIHAgAAAAHjBwEAAAAB5AcBAAAAAeUHCAAAAAHmBwgAAAAB5wcIAAAAAegHCAAAAAHpBwgAAAAB6gcIAAAAAesHAgAAAAHsBwIAAAAB7QcBAAAAAe4HCAAAAAHvByAAAAAB8AcgAAAAAfEHIAAAAAHyByAAAAAB8wcgAAAAAfQHIAAAAAH1BwIAAAAB9wcBAAAAAfgHQAAAAAECAAAAfwAgEgAA5xQAIAMAAAB_ACASAADnFAAgEwAA5hQAIAELAADwFQAwAgAAAH8AIAsAAOYUACACAAAAmg0AIAsAAOUUACAmrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdQGAgCADQAh1QYBAMsMACHsBiAAzAwAIe0GIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhtwcCAIANACHfBwgA_QwAIeAHAQDLDAAh4QcBAMsMACHiBwIAgA0AIeMHAQDLDAAh5AcBAMsMACHlBwgA_QwAIeYHCAD9DAAh5wcIAP0MACHoBwgA_QwAIekHCAD9DAAh6gcIAP0MACHrBwIAygwAIewHAgDKDAAh7QcBAMsMACHuBwgAnA0AIe8HIADMDAAh8AcgAMwMACHxByAAzAwAIfIHIADMDAAh8wcgAMwMACH0ByAAzAwAIfUHAgDKDAAh9wcBAMkMACH4B0AAzQwAISk2AACeDQAgRAAAoQ4AIFYAAJ8NACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1AYCAIANACHVBgEAywwAIewGIADMDAAh7QYgAMwMACH4BgEAywwAIfkGAQDLDAAh-gZAAM0MACG3BwIAgA0AId8HCAD9DAAh4AcBAMsMACHhBwEAywwAIeIHAgCADQAh4wcBAMsMACHkBwEAywwAIeUHCAD9DAAh5gcIAP0MACHnBwgA_QwAIegHCAD9DAAh6QcIAP0MACHqBwgA_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_AAC7DgAgQAAAvQ4AIEEAAOMOACBFAQAAAAFYAADADgAgXQEAAAABZQAAvw4AIK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHcBgEAAAAB3gYCAAAAAeAGAgAAAAHnBiAAAAAB9gYCAAAAAfkGAQAAAAGEBwEAAAABhQcCAAAAAYYHAgAAAAGIBwAAAIgHA4kHCAAAAAGKBwEAAAABiwcBAAAAAYwHAgAAAAGNBwIAAAABjgcBAAAAAZAHAgAAAAGRB0AAAAABkgdAAAAAAZMHQAAAAAGUB0AAAAABlQcAALQOACCWBwEAAAABAgAAAFQAIBIAAPwUACADAAAAVAAgEgAA_BQAIBMAAPsUACABCwAA7hUAMAIAAABUACALAAD7FAAgAgAAAIgOACALAAD6FAAgHjMBAMkMACFFAQDLDAAhXQEAywwAIa8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHeBgIAygwAIeAGAgCADQAh5wYgAMwMACH2BgIAgA0AIfkGAQDLDAAhhAcBAMsMACGFBwIAgA0AIYYHAgCADQAhiAcAAIoOiAcjiQcIAP0MACGKBwEAywwAIYsHAQDLDAAhjAcCAIANACGNBwIAgA0AIY4HAQDLDAAhkAcCAIANACGRB0AAzQwAIZIHQADNDAAhkwdAAM0MACGUB0AAzQwAIZUHAACLDgAglgcBAMkMACEqMwEAyQwAITQAAJQOACA4AACNDgAgOgAAjg4AIDsAAI8OACA8AACQDgAgPQAAkQ4AID4AAJIOACA_AACTDgAgQAAAlQ4AIEEAAOEOACBFAQDLDAAhWAAAmA4AIF0BAMsMACFlAACXDgAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAId4GAgDKDAAh4AYCAIANACHnBiAAzAwAIfYGAgCADQAh-QYBAMsMACGEBwEAywwAIYUHAgCADQAhhgcCAIANACGIBwAAig6IByOJBwgA_QwAIYoHAQDLDAAhiwcBAMsMACGMBwIAgA0AIY0HAgCADQAhjgcBAMsMACGQBwIAgA0AIZEHQADNDAAhkgdAAM0MACGTB0AAzQwAIZQHQADNDAAhlQcAAIsOACCWBwEAyQwAISozAQAAAAE0AAC8DgAgOAAAtQ4AIDoAALYOACA7AAC3DgAgPAAAuA4AID0AALkOACA-AAC6DgAgPwAAuw4AIEAAAL0OACBBAADjDgAgRQEAAAABWAAAwA4AIF0BAAAAAWUAAL8OACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAd4GAgAAAAHgBgIAAAAB5wYgAAAAAfYGAgAAAAH5BgEAAAABhAcBAAAAAYUHAgAAAAGGBwIAAAABiAcAAACIBwOJBwgAAAABigcBAAAAAYsHAQAAAAGMBwIAAAABjQcCAAAAAY4HAQAAAAGQBwIAAAABkQdAAAAAAZIHQAAAAAGTB0AAAAABlAdAAAAAAZUHAAC0DgAglgcBAAAAAQQSAAD0FAAwwwgAAPUUADDHCAAAhA4AMMgIAAD3FAAgBBIAAOgUADDDCAAA6RQAMMcIAADsFAAwyAgAAOsUACAEEgAA3xQAMMMIAADgFAAwxwgAAJYNADDICAAA4hQAIAAAAAAAChIAAIYVADATAACKFQAwwwgAAIcVADDECAAAiBUAMMUIAACJFQAwxggAAIkVADDHCAAAiRUAMMkIAACJFQAwyggAAIsVADDLCAAAjBUAMB4xAACSFQAgMgAAshAAIDMBAAAAATgAALcQACA5AAC1EAAgaAAAuBAAIHIAALMQACBzAAC0EAAgdAAAthAAIHUAALkQACB2AAC6EAAgdwAAuxAAIHgAALwQACB5AAC9EAAgrwYBAAAAAbAGAgAAAAGyBgEAAAABswYBAAAAAbgGQAAAAAG5BkAAAAABxgcBAAAAAZEIIAAAAAGUCAEAAAABpAgBAAAAAaUIAQAAAAGmCAEAAAABpwggAAAAAagIAQAAAAGpCAEAAAABqggBAAAAAQIAAAA9ACASAACRFQAgAwAAAD0AIBIAAJEVACATAACPFQAgIi8AAK8MACAxAACwDAAgMgAAsQwAIDMBAPMKACE4AAChDAAgOQAA_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_AcAAMMMACD9BwAAwwwAIP4HAADDDAAg_wcAAMMMACCACAAAwwwAIIYIAADDDAAgBk0AAOcTACBOAACjFQAgyAYAAMMMACDVBgAAwwwAIPgGAADDDAAgiAgAAMMMACAESAAAqxQAIMgGAADDDAAg1QYAAMMMACD4BgAAwwwAIAVJAACmFQAgSgAAhRQAIMgGAADDDAAg1QYAAMMMACD4BgAAwwwAIARKAACFFAAgyAYAAMMMACDVBgAAwwwAIPgGAADDDAAgLDMAAL8QACA0AACeFQAgNwAAvxAAIEAAALMVACBCAACxFQAgRQAArBUAIFgAAIEVACBaAACtFQAgWwAArhUAIFwAAK8VACBdAACwFQAgXgAAshUAIF8AAL8QACBgAAC0FQAgYQAAnhUAIGIAALQVACBjAAC_EAAg1QYAAMMMACDgBgAAwwwAIPgGAADDDAAg-QYAAMMMACD6BgAAwwwAIIwHAADDDAAguQcAAMMMACC6BwAAwwwAILsHAADDDAAgvQcAAMMMACC-BwAAwwwAIMAHAADDDAAgwQcAAMMMACDCBwAAwwwAIMMHAADDDAAgxQcAAMMMACDGBwAAwwwAIMcHAADDDAAgzQcAAMMMACDQBwAAwwwAINQHAADDDAAg1QcAAMMMACDaBwAAwwwAINsHAADDDAAg3AcAAMMMACDdBwAAwwwAIN4HAADDDAAgITQAAJ4VACA4AAC1FQAgOgAAthUAIDsAALcVACA8AAC4FQAgPQAAuRUAID4AAL8QACA_AAC_EAAgQAAAsxUAIEEAALQVACBCAACrFQAgRQAAwwwAIFgAAIEVACBdAADDDAAgZQAAuhUAIOAGAADDDAAg9gYAAMMMACD5BgAAwwwAIIQHAADDDAAghQcAAMMMACCGBwAAwwwAIIgHAADDDAAgigcAAMMMACCLBwAAwwwAIIwHAADDDAAgjQcAAMMMACCOBwAAwwwAII8HAADDDAAgkAcAAMMMACCRBwAAwwwAIJIHAADDDAAgkwcAAMMMACCUBwAAwwwAIAs5AADQEAAgVQAAgRUAIGQAAIAVACDIBgAAwwwAIPgGAADDDAAguQcAAMMMACC6BwAAwwwAILsHAADDDAAgvQcAAMMMACC-BwAAwwwAIMAHAADDDAAgAARZAAD1EQAgyAYAAMMMACDVBgAAwwwAIPgGAADDDAAgBFkAAPURACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACAFWQAA9REAIMgGAADDDAAg1QYAAMMMACD4BgAAwwwAIIIHAADDDAAgBFkAAPURACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACAEWQAA9REAIMgGAADDDAAg1QYAAMMMACD4BgAAwwwAIARCAACrFQAgWQAA9REAINUGAADDDAAg-AYAAMMMACAPNAAAnhUAIDkAANAQACBZAAD1EQAgYAAAtBUAIOAGAADDDAAg5gYAAMMMACDCBwAAwwwAIMMHAADDDAAgxQcAAMMMACDGBwAAwwwAINsHAADDDAAgkwgAAMMMACCUCAAAwwwAIJYIAADDDAAglwgAAMMMACAQMwAAvxAAIDQAAJ4VACA1AACiFQAgWQAA9REAIGYAANAQACBnAAD1EQAgyAYAAMMMACD4BgAAwwwAILkHAADDDAAgugcAAMMMACC7BwAAwwwAIL0HAADDDAAgvgcAAMMMACDABwAAwwwAIMIHAADDDAAgmAgAAMMMACAABDkAANAQACDIBgAAwwwAINUGAADDDAAg-AYAAMMMACAEOQAA0BAAIPgGAADDDAAgnQcAAMMMACCeBwAAwwwAIAQ5AADQEAAgyAYAAMMMACDVBgAAwwwAIPgGAADDDAAgBDMAAMMMACA5AADQEAAgyAYAAMMMACDJBgAAwwwAIAQ5AADQEAAgyAYAAMMMACDVBgAAwwwAIPgGAADDDAAgAAAABDAAAL8QACC0BgAAwwwAILYGAADDDAAgtwYAAMMMACAAAAAAAAAAAAUSAADfFQAgEwAA4hUAIMMIAADgFQAgxAgAAOEVACDHCAAAAQAgAxIAAN8VACDDCAAA4BUAIMcIAAABACAAAAAByQgAAAC7CAIByQgAAAC9CAILEgAAzxUAMBMAANQVADDDCAAA0BUAMMQIAADRFQAwxQgAANMVADDGCAAA0xUAMMcIAADTFQAwyAgAANIVACDJCAAA0xUAMMoIAADVFQAwywgAANYVADALrwYBAAAAAbgGQAAAAAGxCAEAAAABsggBAAAAAbMIAgAAAAG0CAIAAAABtQgCAAAAAbYIAgAAAAG3CAIAAAABuAiAAAAAAbkIAgAAAAECAAAABQAgEgAA2hUAIAMAAAAFACASAADaFQAgEwAA2RUAIAELAADeFQAwEAMAAMIMACCsBgAAwQwAMK0GAAADABCuBgAAwQwAMK8GAQAAAAG4BkAA9goAIbAIAQDyCgAhsQgBAPIKACGyCAEA8goAIbMIAgCUCwAhtAgCAJQLACG1CAIAlAsAIbYIAgCUCwAhtwgCAJQLACG4CAAAiQsAILkIAgCUCwAhAgAAAAUAIAsAANkVACACAAAA1xUAIAsAANgVACAPrAYAANYVADCtBgAA1xUAEK4GAADWFQAwrwYBAPIKACG4BkAA9goAIbAIAQDyCgAhsQgBAPIKACGyCAEA8goAIbMIAgCUCwAhtAgCAJQLACG1CAIAlAsAIbYIAgCUCwAhtwgCAJQLACG4CAAAiQsAILkIAgCUCwAhD6wGAADWFQAwrQYAANcVABCuBgAA1hUAMK8GAQDyCgAhuAZAAPYKACGwCAEA8goAIbEIAQDyCgAhsggBAPIKACGzCAIAlAsAIbQIAgCUCwAhtQgCAJQLACG2CAIAlAsAIbcIAgCUCwAhuAgAAIkLACC5CAIAlAsAIQuvBgEAyQwAIbgGQADODAAhsQgBAMkMACGyCAEAyQwAIbMIAgDKDAAhtAgCAMoMACG1CAIAygwAIbYIAgDKDAAhtwgCAMoMACG4CIAAAAABuQgCAMoMACELrwYBAMkMACG4BkAAzgwAIbEIAQDJDAAhsggBAMkMACGzCAIAygwAIbQIAgDKDAAhtQgCAMoMACG2CAIAygwAIbcIAgDKDAAhuAiAAAAAAbkIAgDKDAAhC68GAQAAAAG4BkAAAAABsQgBAAAAAbIIAQAAAAGzCAIAAAABtAgCAAAAAbUIAgAAAAG2CAIAAAABtwgCAAAAAbgIgAAAAAG5CAIAAAABBBIAAM8VADDDCAAA0BUAMMcIAADTFQAwyAgAANIVACAABAQAANwVACC-CAAAwwwAIL8IAADDDAAgwAgAAMMMACALrwYBAAAAAbgGQAAAAAGxCAEAAAABsggBAAAAAbMIAgAAAAG0CAIAAAABtQgCAAAAAbYIAgAAAAG3CAIAAAABuAiAAAAAAbkIAgAAAAEJPAAAAL0IAq8GAQAAAAG4BkAAAAABuQZAAAAAAbsIAAAAuwgCvQgBAAAAAb4IQAAAAAG_CEAAAAABwAgBAAAAAQIAAAABACASAADfFQAgAwAAAAkAIBIAAN8VACATAADjFQAgCwAAAAkAIAsAAOMVACA8AADNFb0IIq8GAQDJDAAhuAZAAM4MACG5BkAAzgwAIbsIAADMFbsIIr0IAQDJDAAhvghAAM0MACG_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-AYBAAAAAQIAAACHBAAgEgAA-BUAIBWvBgIAAAABsQYBAAAAAbcGQAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAAB3AYBAAAAAfkHAQAAAAH6BwEAAAAB_AcCAAAAAf0HAQAAAAH-BwEAAAAB_wcBAAAAAYAIAgAAAAGBCAgAAAABgggIAAAAAYMIIAAAAAGECCAAAAABhQgCAAAAAYYIAQAAAAEDAAAAigQAIBIAAPgVACATAAD9FQAgCwAAAIoEACALAAD9FQAgrwYCAMoMACG1BiAAzAwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhCa8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdUGAQDLDAAh3AYBAMkMACH4BgEAywwAIRWvBgIAAAABsQYBAAAAAbcGQAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAAB3AYBAAAAAfkHAQAAAAH6BwEAAAAB-wcCAAAAAf0HAQAAAAH-BwEAAAAB_wcBAAAAAYAIAgAAAAGBCAgAAAABgggIAAAAAYMIIAAAAAGECCAAAAABhQgCAAAAAYYIAQAAAAEMUAAA4xMAIFEAAOQTACCvBgIAAAABtQYgAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHVBgEAAAAB3AYBAAAAAfgGAQAAAAGLCCAAAAABAgAAAIAFACASAAD_FQAgAwAAAJgBACASAAD_FQAgEwAAgxYAIA4AAACYAQAgCwAAgxYAIFAAALUTACBRAAC2EwAgrwYCAMoMACG1BiAAzAwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhiwggAMwMACEMUAAAtRMAIFEAALYTACCvBgIAygwAIbUGIADMDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHVBgEAywwAIdwGAQDJDAAh-AYBAMsMACGLCCAAzAwAIQivBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB-AYBAAAAAYcIAgAAAAGJCAIAAAABiggBAAAAAQivBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB-AYBAAAAAYkIAgAAAAGMCAIAAAABjQgCAAAAARxGAACoEwAgRwAAmhMAIEsAAJsTACBMAACcEwAgVAAAnhMAIFUAAJ8TACCvBgIAAAABsQYBAAAAAbcGQAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAAB3AYBAAAAAfkHAQAAAAH6BwEAAAAB-wcCAAAAAfwHAgAAAAH9BwEAAAAB_gcBAAAAAf8HAQAAAAGACAIAAAABgQgIAAAAAYIICAAAAAGDCCAAAAABhAggAAAAAYUIAgAAAAGGCAEAAAABAgAAAIUBACASAACGFgAgAwAAAIMBACASAACGFgAgEwAAihYAIB4AAACDAQAgCwAAihYAIEYAAOESACBHAADiEgAgSwAA4xIAIEwAAOQSACBUAADmEgAgVQAA5xIAIK8GAgDKDAAhsQYBAMsMACG3BkAAzQwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh3AYBAMkMACH5BwEAywwAIfoHAQDLDAAh-wcCAMoMACH8BwIAgA0AIf0HAQDLDAAh_gcBAMsMACH_BwEAywwAIYAIAgCADQAhgQgIAP0MACGCCAgA_QwAIYMIIADMDAAhhAggAMwMACGFCAIAygwAIYYIAQDLDAAhHEYAAOESACBHAADiEgAgSwAA4xIAIEwAAOQSACBUAADmEgAgVQAA5xIAIK8GAgDKDAAhsQYBAMsMACG3BkAAzQwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh3AYBAMkMACH5BwEAywwAIfoHAQDLDAAh-wcCAMoMACH8BwIAgA0AIf0HAQDLDAAh_gcBAMsMACH_BwEAywwAIYAIAgCADQAhgQgIAP0MACGCCAgA_QwAIYMIIADMDAAhhAggAMwMACGFCAIAygwAIYYIAQDLDAAhCK8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAH4BgEAAAABiAgCAAAAAYkIAgAAAAGMCAIAAAABDK8GAgAAAAG1BiAAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAcoGIAAAAAHVBgEAAAAB3AYBAAAAAfgGAQAAAAGOCAIAAAABjwgCAAAAARxGAACoEwAgRwAAmhMAIEsAAJsTACBMAACcEwAgUQAAnRMAIFUAAJ8TACCvBgIAAAABsQYBAAAAAbcGQAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAAB3AYBAAAAAfkHAQAAAAH6BwEAAAAB-wcCAAAAAfwHAgAAAAH9BwEAAAAB_gcBAAAAAf8HAQAAAAGACAIAAAABgQgIAAAAAYIICAAAAAGDCCAAAAABhAggAAAAAYUIAgAAAAGGCAEAAAABAgAAAIUBACASAACNFgAgAwAAAIMBACASAACNFgAgEwAAkRYAIB4AAACDAQAgCwAAkRYAIEYAAOESACBHAADiEgAgSwAA4xIAIEwAAOQSACBRAADlEgAgVQAA5xIAIK8GAgDKDAAhsQYBAMsMACG3BkAAzQwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh3AYBAMkMACH5BwEAywwAIfoHAQDLDAAh-wcCAMoMACH8BwIAgA0AIf0HAQDLDAAh_gcBAMsMACH_BwEAywwAIYAIAgCADQAhgQgIAP0MACGCCAgA_QwAIYMIIADMDAAhhAggAMwMACGFCAIAygwAIYYIAQDLDAAhHEYAAOESACBHAADiEgAgSwAA4xIAIEwAAOQSACBRAADlEgAgVQAA5xIAIK8GAgDKDAAhsQYBAMsMACG3BkAAzQwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh3AYBAMkMACH5BwEAywwAIfoHAQDLDAAh-wcCAMoMACH8BwIAgA0AIf0HAQDLDAAh_gcBAMsMACH_BwEAywwAIYAIAgCADQAhgQgIAP0MACGCCAgA_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_RQAIFUAAP8UACCvBgIAAAABtQYgAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHcBgEAAAAB-AYBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDAgAAANoCACASAACuFgAgM68GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHVBgEAAAAB4AYCAAAAAecGIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAGMBwIAAAABjwcCAAAAAZ8HIAAAAAGgByAAAAABoQcgAAAAAaIHIAAAAAGjByAAAAABpAcgAAAAAbgHAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8EHAQAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAccHAQAAAAHIByAAAAAByQcgAAAAAcoHCAAAAAHMBwAAAMwHAs0HQAAAAAHPBwAAAM8HAtAHAQAAAAHRByAAAAAB0gcgAAAAAdMHIAAAAAHUBwEAAAAB1gcCAAAAAdcHAgAAAAHYBwIAAAAB2QcCAAAAAdoHAgAAAAHbBwIAAAAB3AcCAAAAAd0HAgAAAAHeB0AAAAABAwAAAG4AIBIAAK4WACATAACzFgAgEgAAAG4AIAsAALMWACA5AADcFAAgVQAA3hQAIK8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdwGAQDJDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHIxA5AADcFAAgVQAA3hQAIK8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdwGAQDJDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-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_QYBAAAAAf4GAQAAAAH_BgEAAAABgAcgAAAAAYEHIAAAAAECAAAAgQkAIBIAAMEWACALrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHKBiAAAAAB1QYBAAAAAdwGAQAAAAH4BgEAAAABggcBAAAAAYMHIAAAAAECAAAA2ggAIBIAAMMWACADAAAA5ggAIBIAAL8WACATAADHFgAgBQAAAOYIACALAADHFgAgrwYCAMoMACH3BgEAywwAIfsGAQDLDAAhA68GAgDKDAAh9wYBAMsMACH7BgEAywwAIQMAAADiCAAgEgAAwRYAIBMAAMoWACAQAAAA4ggAIAsAAMoWACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAh_AYBAMsMACH9BgEAywwAIf4GAQDLDAAh_wYBAMsMACGAByAAzAwAIYEHIADMDAAhDq8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHVBgEAywwAIdwGAQDJDAAh-AYBAMsMACH8BgEAywwAIf0GAQDLDAAh_gYBAMsMACH_BgEAywwAIYAHIADMDAAhgQcgAMwMACEDAAAA4AgAIBIAAMMWACATAADNFgAgDQAAAOAIACALAADNFgAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIcoGIADMDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhggcBAMsMACGDByAAzAwAIQuvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAhygYgAMwMACHVBgEAywwAIdwGAQDJDAAh-AYBAMsMACGCBwEAywwAIYMHIADMDAAhCEUBAAAAAa8GAQAAAAG4BkAAAAABuQZAAAAAAdwGAQAAAAHdBgEAAAAB3waAAAAAAeAGAQAAAAENNAAA7A8AIDoAAOsPACBFAQAAAAFpAADvDwAgagAA6g8AIK8GAQAAAAG4BkAAAAABuQZAAAAAAdwGAQAAAAHdBgEAAAAB3gYBAAAAAd8GgAAAAAHgBgEAAAABAgAAAOMBACASAADPFgAgAwAAAOEBACASAADPFgAgEwAA0xYAIA8AAADhAQAgCwAA0xYAIDQAAOgPACA6AADSDwAgRQEAywwAIWkAANAPACBqAADRDwAgrwYBAMkMACG4BkAAzgwAIbkGQADODAAh3AYBAMkMACHdBgEAywwAId4GAQDJDAAh3waAAAAAAeAGAQDJDAAhDTQAAOgPACA6AADSDwAgRQEAywwAIWkAANAPACBqAADRDwAgrwYBAMkMACG4BkAAzgwAIbkGQADODAAh3AYBAMkMACHdBgEAywwAId4GAQDJDAAh3waAAAAAAeAGAQDJDAAhHjMBAAAAAUUBAAAAAV0BAAAAAa8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHcBgEAAAAB3gYCAAAAAeAGAgAAAAHnBiAAAAAB9gYCAAAAAfkGAQAAAAGEBwEAAAABhQcCAAAAAYgHAAAAiAcDiQcIAAAAAYoHAQAAAAGLBwEAAAABjAcCAAAAAY0HAgAAAAGOBwEAAAABjwcCAAAAAZAHAgAAAAGRB0AAAAABkgdAAAAAAZMHQAAAAAGUB0AAAAABlQcAALQOACCWBwEAAAABBq8GAQAAAAGrCAEAAAABrAhAAAAAAa0IIAAAAAGuCEAAAAABrwhAAAAAAR4vAACxEAAgMQAAkhUAIDIAALIQACAzAQAAAAE4AAC3EAAgOQAAtRAAIGgAALgQACByAACzEAAgdAAAthAAIHUAALkQACB2AAC6EAAgdwAAuxAAIHgAALwQACB5AAC9EAAgrwYBAAAAAbAGAgAAAAGyBgEAAAABswYBAAAAAbgGQAAAAAG5BkAAAAABxgcBAAAAAZEIIAAAAAGUCAEAAAABpAgBAAAAAaUIAQAAAAGmCAEAAAABpwggAAAAAagIAQAAAAGpCAEAAAABqggBAAAAAQIAAAA9ACASAADWFgAgAwAAADsAIBIAANYWACATAADaFgAgIAAAADsAIAsAANoWACAvAADVDAAgMQAAkBUAIDIAANYMACAzAQDLDAAhOAAA2wwAIDkAANkMACBoAADcDAAgcgAA1wwAIHQAANoMACB1AADdDAAgdgAA3gwAIHcAAN8MACB4AADgDAAgeQAA4QwAIK8GAQDJDAAhsAYCAIANACGyBgEAywwAIbMGAQDLDAAhuAZAAM4MACG5BkAAzgwAIcYHAQDJDAAhkQggAMwMACGUCAEAywwAIaQIAQDLDAAhpQgBAMkMACGmCAEAywwAIacIIADMDAAhqAgBAMsMACGpCAEAywwAIaoIAQDLDAAhHi8AANUMACAxAACQFQAgMgAA1gwAIDMBAMsMACE4AADbDAAgOQAA2QwAIGgAANwMACByAADXDAAgdAAA2gwAIHUAAN0MACB2AADeDAAgdwAA3wwAIHgAAOAMACB5AADhDAAgrwYBAMkMACGwBgIAgA0AIbIGAQDLDAAhswYBAMsMACG4BkAAzgwAIbkGQADODAAhxgcBAMkMACGRCCAAzAwAIZQIAQDLDAAhpAgBAMsMACGlCAEAyQwAIaYIAQDLDAAhpwggAMwMACGoCAEAywwAIakIAQDLDAAhqggBAMsMACENrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdwGAQAAAAHCBwEAAAABiggBAAAAAZwIAQAAAAGdCCAAAAABnghAAAAAAZ8IAQAAAAGgCCAAAAABoghAAAAAAR4vAACxEAAgMQAAkhUAIDIAALIQACAzAQAAAAE4AAC3EAAgOQAAtRAAIGgAALgQACBzAAC0EAAgdAAAthAAIHUAALkQACB2AAC6EAAgdwAAuxAAIHgAALwQACB5AAC9EAAgrwYBAAAAAbAGAgAAAAGyBgEAAAABswYBAAAAAbgGQAAAAAG5BkAAAAABxgcBAAAAAZEIIAAAAAGUCAEAAAABpAgBAAAAAaUIAQAAAAGmCAEAAAABpwggAAAAAagIAQAAAAGpCAEAAAABqggBAAAAAQIAAAA9ACASAADcFgAgGjMAAPoPACA0AADpDgAgWQAA7A4AIGYAAOsOACBnAADtDgAgrwYCAAAAAbUGIAAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAABygYgAAAAAdwGAQAAAAHgBgIAAAAB-AYBAAAAAbkHAQAAAAG6BwEAAAABuwcBAAAAAb0HAAAAvQcDvgcBAAAAAcAHAAAAwAcDwgcBAAAAAZgIAQAAAAGZCCAAAAABmgggAAAAAZsIIAAAAAECAAAA3wEAIBIAAN4WACADAAAATgAgEgAA3hYAIBMAAOIWACAcAAAATgAgCwAA4hYAIDMAAPgPACA0AADpDQAgWQAA7A0AIGYAAOsNACBnAADtDQAgrwYCAMoMACG1BiAAzAwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAhygYgAMwMACHcBgEAyQwAIeAGAgDKDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-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-hYAIA8AAADhAQAgCwAA-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_QwAIYoHAQDLDAAhiwcBAMsMACGMBwIAgA0AIY0HAgCADQAhjgcBAMsMACGPBwIAgA0AIZAHAgCADQAhkQdAAM0MACGSB0AAzQwAIZMHQADNDAAhlAdAAM0MACGVBwAAiw4AIJYHAQDJDAAhCq8GAQAAAAG4BkAAAAABuQZAAAAAAc4GgAAAAAHPBoAAAAAB0AYBAAAAAdEGgAAAAAHSBgABAAAB0wYBAAAAAdQGAQAAAAEWNQAAhxAAIDkAAIsQACBZAACNEAAgawAAiRAAIG4AAIoQACBvAACMEAAgcAAAmRAAIHEAAI4QACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAcIHAQAAAAGKCAEAAAABnAgBAAAAAZ0IIAAAAAGeCEAAAAABnwgBAAAAAaAIIAAAAAGhCAEAAAABoghAAAAAAQIAAABIACASAACMFwAgFjkAAIsQACBZAACNEAAgaAAAiBAAIGsAAIkQACBuAACKEAAgbwAAjBAAIHAAAJkQACBxAACOEAAgrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdwGAQAAAAHCBwEAAAABiggBAAAAAZwIAQAAAAGdCCAAAAABnghAAAAAAZ8IAQAAAAGgCCAAAAABoQgBAAAAAaIIQAAAAAECAAAASAAgEgAAjhcAIBozAAD6DwAgNAAA6Q4AIDUAAOoOACBZAADsDgAgZwAA7Q4AIK8GAgAAAAG1BiAAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAcoGIAAAAAHcBgEAAAAB4AYCAAAAAfgGAQAAAAG5BwEAAAABugcBAAAAAbsHAQAAAAG9BwAAAL0HA74HAQAAAAHABwAAAMAHA8IHAQAAAAGYCAEAAAABmQggAAAAAZoIIAAAAAGbCCAAAAABAgAAAN8BACASAACQFwAgAwAAAE4AIBIAAJAXACATAACUFwAgHAAAAE4AIAsAAJQXACAzAAD4DwAgNAAA6Q0AIDUAAOoNACBZAADsDQAgZwAA7Q0AIK8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIcoGIADMDAAh3AYBAMkMACHgBgIAygwAIfgGAQDLDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPCBwEAywwAIZgIAQDLDAAhmQggAMwMACGaCCAAzAwAIZsIIADMDAAhGjMAAPgPACA0AADpDQAgNQAA6g0AIFkAAOwNACBnAADtDQAgrwYCAMoMACG1BiAAzAwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAhygYgAMwMACHcBgEAyQwAIeAGAgDKDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8IHAQDLDAAhmAgBAMsMACGZCCAAzAwAIZoIIADMDAAhmwggAMwMACEeMwEAAAABRQEAAAABXQEAAAABrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdwGAQAAAAHeBgIAAAAB4AYCAAAAAecGIAAAAAH2BgIAAAAB-QYBAAAAAYQHAQAAAAGFBwIAAAABhgcCAAAAAYgHAAAAiAcDiQcIAAAAAYoHAQAAAAGLBwEAAAABjQcCAAAAAY4HAQAAAAGPBwIAAAABkAcCAAAAAZEHQAAAAAGSB0AAAAABkwdAAAAAAZQHQAAAAAGVBwAAtA4AIJYHAQAAAAEzrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAHgBgIAAAAB5wYgAAAAAfgGAQAAAAH5BgEAAAAB-gZAAAAAAY8HAgAAAAGfByAAAAABoAcgAAAAAaEHIAAAAAGiByAAAAABowcgAAAAAaQHIAAAAAG4BwEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwPBBwEAAAABwgcBAAAAAcMHAQAAAAHFBwAAAMUHA8YHAQAAAAHHBwEAAAAByAcgAAAAAckHIAAAAAHKBwgAAAABzAcAAADMBwLNB0AAAAABzwcAAADPBwLQBwEAAAAB0QcgAAAAAdIHIAAAAAHTByAAAAAB1AcBAAAAAdUHAgAAAAHWBwIAAAAB1wcCAAAAAdgHAgAAAAHZBwIAAAAB2gcCAAAAAdsHAgAAAAHcBwIAAAAB3QcCAAAAAd4HQAAAAAEDAAAARgAgEgAAjhcAIBMAAJkXACAYAAAARgAgCwAAmRcAIDkAAJoPACBZAACcDwAgaAAAlw8AIGsAAJgPACBuAACZDwAgbwAAmw8AIHAAAJcQACBxAACdDwAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAIcIHAQDLDAAhiggBAMsMACGcCAEAywwAIZ0IIADMDAAhnghAAM0MACGfCAEAywwAIaAIIADMDAAhoQgBAMsMACGiCEAAzQwAIRY5AACaDwAgWQAAnA8AIGgAAJcPACBrAACYDwAgbgAAmQ8AIG8AAJsPACBwAACXEAAgcQAAnQ8AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHCBwEAywwAIYoIAQDLDAAhnAgBAMsMACGdCCAAzAwAIZ4IQADNDAAhnwgBAMsMACGgCCAAzAwAIaEIAQDLDAAhoghAAM0MACESrwYCAAAAAbIGAQAAAAGzBgEAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB4AYCAAAAAeYGAgAAAAHCBwEAAAABwwcBAAAAAcUHAAAAxQcDxgcBAAAAAZEIIAAAAAGSCCAAAAABkwgBAAAAAZQIAQAAAAGWCAAAAJYIA5cIQAAAAAEIrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHVBgEAAAAB3AYBAAAAAfgGAQAAAAECAAAA8gIAIBIAAJsXACAQVQAA_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_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFEMwAAyQ0AIDQAAIsNACA3AACRDQAgQAAAjA0AIEIAAIgNACBFAACCDQAgWgAAhA0AIFsAAIUNACBcAACGDQAgXQAAhw0AIF4AAIkNACBfAACKDQAgYAAAjQ0AIGEAAI4NACBiAACPDQAgYwAAkA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACEmrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAcgGAQAAAAHVBgEAAAAB7AYgAAAAAe0GIAAAAAH4BgEAAAAB-QYBAAAAAfoGQAAAAAG3BwIAAAAB3wcIAAAAAeAHAQAAAAHhBwEAAAAB4gcCAAAAAeMHAQAAAAHkBwEAAAAB5QcIAAAAAeYHCAAAAAHnBwgAAAAB6AcIAAAAAekHCAAAAAHqBwgAAAAB6wcCAAAAAewHAgAAAAHtBwEAAAAB7gcIAAAAAe8HIAAAAAHwByAAAAAB8QcgAAAAAfIHIAAAAAHzByAAAAAB9AcgAAAAAfUHAgAAAAH2BwIAAAAB9wcBAAAAAfgHQAAAAAEDAAAAzAEAIBIAAJsXACATAAC9FwAgCgAAAMwBACALAAC9FwAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdUGAQDLDAAh3AYBAMkMACH4BgEAywwAIQivBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAh1QYBAMsMACHcBgEAyQwAIfgGAQDLDAAhAwAAAG4AIBIAAJ0XACATAADAFwAgEgAAAG4AIAsAAMAXACBVAADeFAAgZAAA3RQAIK8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdwGAQDJDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHIxBVAADeFAAgZAAA3RQAIK8GAgDKDAAhtQYgAMwMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHIBgEAywwAIdwGAQDJDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHIwMAAABKACASAACfFwAgEwAAwxcAIBgAAABKACALAADDFwAgNAAAzQ4AIFkAAM8OACBgAACDEAAgrwYCAMoMACGyBgEAyQwAIbMGAQDJDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh4AYCAIANACHmBgIAgA0AIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIdsHAgCADQAhkQggAMwMACGSCCAAzAwAIZMIAQDLDAAhlAgBAMsMACGWCAAAyw6WCCOXCEAAzQwAIRY0AADNDgAgWQAAzw4AIGAAAIMQACCvBgIAygwAIbIGAQDJDAAhswYBAMkMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHgBgIAgA0AIeYGAgCADQAhwgcBAMsMACHDBwEAywwAIcUHAAD8DMUHI8YHAQDLDAAh2wcCAIANACGRCCAAzAwAIZIIIADMDAAhkwgBAMsMACGUCAEAywwAIZYIAADLDpYII5cIQADNDAAhAwAAAEYAIBIAAKEXACATAADGFwAgGAAAAEYAIAsAAMYXACA1AACWDwAgWQAAnA8AIGgAAJcPACBrAACYDwAgbgAAmQ8AIG8AAJsPACBwAACXEAAgcQAAnQ8AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHCBwEAywwAIYoIAQDLDAAhnAgBAMsMACGdCCAAzAwAIZ4IQADNDAAhnwgBAMsMACGgCCAAzAwAIaEIAQDLDAAhoghAAM0MACEWNQAAlg8AIFkAAJwPACBoAACXDwAgawAAmA8AIG4AAJkPACBvAACbDwAgcAAAlxAAIHEAAJ0PACCvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIdwGAQDJDAAhwgcBAMsMACGKCAEAywwAIZwIAQDLDAAhnQggAMwMACGeCEAAzQwAIZ8IAQDLDAAhoAggAMwMACGhCAEAywwAIaIIQADNDAAhAwAAADsAIBIAAKMXACATAADJFwAgIAAAADsAIAsAAMkXACAvAADVDAAgMQAAkBUAIDIAANYMACAzAQDLDAAhOAAA2wwAIDkAANkMACBoAADcDAAgcgAA1wwAIHMAANgMACB1AADdDAAgdgAA3gwAIHcAAN8MACB4AADgDAAgeQAA4QwAIK8GAQDJDAAhsAYCAIANACGyBgEAywwAIbMGAQDLDAAhuAZAAM4MACG5BkAAzgwAIcYHAQDJDAAhkQggAMwMACGUCAEAywwAIaQIAQDLDAAhpQgBAMkMACGmCAEAywwAIacIIADMDAAhqAgBAMsMACGpCAEAywwAIaoIAQDLDAAhHi8AANUMACAxAACQFQAgMgAA1gwAIDMBAMsMACE4AADbDAAgOQAA2QwAIGgAANwMACByAADXDAAgcwAA2AwAIHUAAN0MACB2AADeDAAgdwAA3wwAIHgAAOAMACB5AADhDAAgrwYBAMkMACGwBgIAgA0AIbIGAQDLDAAhswYBAMsMACG4BkAAzgwAIbkGQADODAAhxgcBAMkMACGRCCAAzAwAIZQIAQDLDAAhpAgBAMsMACGlCAEAyQwAIaYIAQDLDAAhpwggAMwMACGoCAEAywwAIakIAQDLDAAhqggBAMsMACEDAAAAOwAgEgAApRcAIBMAAMwXACAgAAAAOwAgCwAAzBcAIC8AANUMACAxAACQFQAgMgAA1gwAIDMBAMsMACE4AADbDAAgaAAA3AwAIHIAANcMACBzAADYDAAgdAAA2gwAIHUAAN0MACB2AADeDAAgdwAA3wwAIHgAAOAMACB5AADhDAAgrwYBAMkMACGwBgIAgA0AIbIGAQDLDAAhswYBAMsMACG4BkAAzgwAIbkGQADODAAhxgcBAMkMACGRCCAAzAwAIZQIAQDLDAAhpAgBAMsMACGlCAEAyQwAIaYIAQDLDAAhpwggAMwMACGoCAEAywwAIakIAQDLDAAhqggBAMsMACEeLwAA1QwAIDEAAJAVACAyAADWDAAgMwEAywwAITgAANsMACBoAADcDAAgcgAA1wwAIHMAANgMACB0AADaDAAgdQAA3QwAIHYAAN4MACB3AADfDAAgeAAA4AwAIHkAAOEMACCvBgEAyQwAIbAGAgCADQAhsgYBAMsMACGzBgEAywwAIbgGQADODAAhuQZAAM4MACHGBwEAyQwAIZEIIADMDAAhlAgBAMsMACGkCAEAywwAIaUIAQDJDAAhpggBAMsMACGnCCAAzAwAIagIAQDLDAAhqQgBAMsMACGqCAEAywwAIQMAAABlACASAACnFwAgEwAAzxcAIA0AAABlACALAADPFwAgMwEAywwAITcBAMkMACGvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcYGAQDJDAAhxwYBAMkMACHIBgEAywwAIckGCACcDQAhygYgAMwMACELMwEAywwAITcBAMkMACGvBgIAygwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcYGAQDJDAAhxwYBAMkMACHIBgEAywwAIckGCACcDQAhygYgAMwMA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-AYBAAAAAQIAAADqBgAgEgAA_BcAIAqvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAcoGIAAAAAHVBgEAAAAB3AYBAAAAAfgGAQAAAAGCBwEAAAABAgAAAJwHACASAAD-FwAgCa8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHIBgEAAAABygYgAAAAAdUGAQAAAAHcBgEAAAAB-AYBAAAAAQIAAACDBwAgEgAAgBgAIAmvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAcoGIAAAAAHVBgEAAAAB3AYBAAAAAfgGAQAAAAECAAAAtQcAIBIAAIIYACAeLwAAsRAAIDEAAJIVACAyAACyEAAgMwEAAAABOAAAtxAAIDkAALUQACBoAAC4EAAgcgAAsxAAIHMAALQQACB0AAC2EAAgdQAAuRAAIHYAALoQACB3AAC7EAAgeAAAvBAAIK8GAQAAAAGwBgIAAAABsgYBAAAAAbMGAQAAAAG4BkAAAAABuQZAAAAAAcYHAQAAAAGRCCAAAAABlAgBAAAAAaQIAQAAAAGlCAEAAAABpggBAAAAAacIIAAAAAGoCAEAAAABqQgBAAAAAaoIAQAAAAECAAAAPQAgEgAAhBgAIAMAAAA7ACASAACEGAAgEwAAiBgAICAAAAA7ACALAACIGAAgLwAA1QwAIDEAAJAVACAyAADWDAAgMwEAywwAITgAANsMACA5AADZDAAgaAAA3AwAIHIAANcMACBzAADYDAAgdAAA2gwAIHUAAN0MACB2AADeDAAgdwAA3wwAIHgAAOAMACCvBgEAyQwAIbAGAgCADQAhsgYBAMsMACGzBgEAywwAIbgGQADODAAhuQZAAM4MACHGBwEAyQwAIZEIIADMDAAhlAgBAMsMACGkCAEAywwAIaUIAQDJDAAhpggBAMsMACGnCCAAzAwAIagIAQDLDAAhqQgBAMsMACGqCAEAywwAIR4vAADVDAAgMQAAkBUAIDIAANYMACAzAQDLDAAhOAAA2wwAIDkAANkMACBoAADcDAAgcgAA1wwAIHMAANgMACB0AADaDAAgdQAA3QwAIHYAAN4MACB3AADfDAAgeAAA4AwAIK8GAQDJDAAhsAYCAIANACGyBgEAywwAIbMGAQDLDAAhuAZAAM4MACG5BkAAzgwAIcYHAQDJDAAhkQggAMwMACGUCAEAywwAIaQIAQDLDAAhpQgBAMkMACGmCAEAywwAIacIIADMDAAhqAgBAMsMACGpCAEAywwAIaoIAQDLDAAhEEUBAAAAAa8GAgAAAAG4BkAAAAABuQZAAAAAAcUGAQAAAAHVBgEAAAAB-AYBAAAAAa4HAQAAAAGvBwEAAAABsAcgAAAAAbEHIAAAAAGyByAAAAABswcgAAAAAbQHIAAAAAG1ByAAAAABtgcgAAAAARA5AAD9FAAgZAAA_hQAIK8GAgAAAAG1BiAAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAdwGAQAAAAH4BgEAAAABuQcBAAAAAboHAQAAAAG7BwEAAAABvQcAAAC9BwO-BwEAAAABwAcAAADABwMCAAAA2gIAIBIAAIoYACAcRgAAqBMAIEcAAJoTACBLAACbEwAgTAAAnBMAIFEAAJ0TACBUAACeEwAgrwYCAAAAAbEGAQAAAAG3BkAAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAByAYBAAAAAdwGAQAAAAH5BwEAAAAB-gcBAAAAAfsHAgAAAAH8BwIAAAAB_QcBAAAAAf4HAQAAAAH_BwEAAAABgAgCAAAAAYEICAAAAAGCCAgAAAABgwggAAAAAYQIIAAAAAGFCAIAAAABhggBAAAAAQIAAACFAQAgEgAAjBgAICszAQAAAAE0AAC8DgAgOAAAtQ4AIDoAALYOACA7AAC3DgAgPAAAuA4AID0AALkOACA-AAC6DgAgPwAAuw4AIEAAAL0OACBBAADjDgAgQgAAvg4AIEUBAAAAAV0BAAAAAWUAAL8OACCvBgIAAAABuAZAAAAAAbkGQAAAAAHFBgEAAAAB3AYBAAAAAd4GAgAAAAHgBgIAAAAB5wYgAAAAAfYGAgAAAAH5BgEAAAABhAcBAAAAAYUHAgAAAAGGBwIAAAABiAcAAACIBwOJBwgAAAABigcBAAAAAYsHAQAAAAGMBwIAAAABjQcCAAAAAY4HAQAAAAGPBwIAAAABkAcCAAAAAZEHQAAAAAGSB0AAAAABkwdAAAAAAZQHQAAAAAGVBwAAtA4AIJYHAQAAAAECAAAAVAAgEgAAjhgAIAMAAABuACASAACKGAAgEwAAkhgAIBIAAABuACALAACSGAAgOQAA3BQAIGQAAN0UACCvBgIAygwAIbUGIADMDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHcBgEAyQwAIfgGAQDLDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByMQOQAA3BQAIGQAAN0UACCvBgIAygwAIbUGIADMDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHcBgEAyQwAIfgGAQDLDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByMDAAAAgwEAIBIAAIwYACATAACVGAAgHgAAAIMBACALAACVGAAgRgAA4RIAIEcAAOISACBLAADjEgAgTAAA5BIAIFEAAOUSACBUAADmEgAgrwYCAMoMACGxBgEAywwAIbcGQADNDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHcBgEAyQwAIfkHAQDLDAAh-gcBAMsMACH7BwIAygwAIfwHAgCADQAh_QcBAMsMACH-BwEAywwAIf8HAQDLDAAhgAgCAIANACGBCAgA_QwAIYIICAD9DAAhgwggAMwMACGECCAAzAwAIYUIAgDKDAAhhggBAMsMACEcRgAA4RIAIEcAAOISACBLAADjEgAgTAAA5BIAIFEAAOUSACBUAADmEgAgrwYCAMoMACGxBgEAywwAIbcGQADNDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHcBgEAyQwAIfkHAQDLDAAh-gcBAMsMACH7BwIAygwAIfwHAgCADQAh_QcBAMsMACH-BwEAywwAIf8HAQDLDAAhgAgCAIANACGBCAgA_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-gy9ByO-BwEAywwAIcAHAAD7DMAHI8IHAQDLDAAhmAgBAMsMACGZCCAAzAwAIZoIIADMDAAhmwggAMwMACEDAAAARgAgEgAA7hcAIBMAAKUYACAYAAAARgAgCwAApRgAIDUAAJYPACA5AACaDwAgWQAAnA8AIGgAAJcPACBrAACYDwAgbgAAmQ8AIG8AAJsPACBwAACXEAAgrwYCAMoMACG4BkAAzgwAIbkGQADODAAhxQYBAMkMACHcBgEAyQwAIcIHAQDLDAAhiggBAMsMACGcCAEAywwAIZ0IIADMDAAhnghAAM0MACGfCAEAywwAIaAIIADMDAAhoQgBAMsMACGiCEAAzQwAIRY1AACWDwAgOQAAmg8AIFkAAJwPACBoAACXDwAgawAAmA8AIG4AAJkPACBvAACbDwAgcAAAlxAAIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh3AYBAMkMACHCBwEAywwAIYoIAQDLDAAhnAgBAMsMACGdCCAAzAwAIZ4IQADNDAAhnwgBAMsMACGgCCAAzAwAIaEIAQDLDAAhoghAAM0MACEDAAAATgAgEgAA8BcAIBMAAKgYACAcAAAATgAgCwAAqBgAIDMAAPgPACA0AADpDQAgNQAA6g0AIGYAAOsNACBnAADtDQAgrwYCAMoMACG1BiAAzAwAIbgGQADODAAhuQZAAM4MACHFBgEAyQwAIcgGAQDLDAAhygYgAMwMACHcBgEAyQwAIeAGAgDKDAAh-AYBAMsMACG5BwEAywwAIboHAQDLDAAhuwcBAMsMACG9BwAA-gy9ByO-BwEAywwAIcAHAAD7DMAHI8IHAQDLDAAhmAgBAMsMACGZCCAAzAwAIZoIIADMDAAhmwggAMwMACEaMwAA-A8AIDQAAOkNACA1AADqDQAgZgAA6w0AIGcAAO0NACCvBgIAygwAIbUGIADMDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAhyAYBAMsMACHKBiAAzAwAIdwGAQDJDAAh4AYCAMoMACH4BgEAywwAIbkHAQDLDAAhugcBAMsMACG7BwEAywwAIb0HAAD6DL0HI74HAQDLDAAhwAcAAPsMwAcjwgcBAMsMACGYCAEAywwAIZkIIADMDAAhmgggAMwMACGbCCAAzAwAIQMAAABKACASAADyFwAgEwAAqxgAIBgAAABKACALAACrGAAgNAAAzQ4AIDkAAM4OACBgAACDEAAgrwYCAMoMACGyBgEAyQwAIbMGAQDJDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh4AYCAIANACHmBgIAgA0AIcIHAQDLDAAhwwcBAMsMACHFBwAA_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_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACFEMwAAyQ0AIDQAAIsNACA3AACRDQAgQAAAjA0AIEIAAIgNACBYAACDDQAgWgAAhA0AIFsAAIUNACBcAACGDQAgXQAAhw0AIF4AAIkNACBfAACKDQAgYAAAjQ0AIGEAAI4NACBiAACPDQAgYwAAkA0AIK8GAgDKDAAhuAZAAM4MACG5BkAAzgwAIcUGAQDJDAAh1QYBAMsMACHgBgIAgA0AIecGIADMDAAh-AYBAMsMACH5BgEAywwAIfoGQADNDAAhjAcCAIANACGPBwIAygwAIZ8HIADMDAAhoAcgAMwMACGhByAAzAwAIaIHIADMDAAhowcgAMwMACGkByAAzAwAIbgHAQDJDAAhuQcBAMsMACG6BwEAywwAIbsHAQDLDAAhvQcAAPoMvQcjvgcBAMsMACHABwAA-wzAByPBBwEAywwAIcIHAQDLDAAhwwcBAMsMACHFBwAA_AzFByPGBwEAywwAIccHAQDLDAAhyAcgAMwMACHJByAAzAwAIcoHCAD9DAAhzAcAAP4MzAcizQdAAM0MACHPBwAA_wzPByLQBwEAywwAIdEHIADMDAAh0gcgAMwMACHTByAAzAwAIdQHAQDLDAAh1QcCAIANACHWBwIAygwAIdcHAgDKDAAh2AcCAMoMACHZBwIAygwAIdoHAgCADQAh2wcCAIANACHcBwIAgA0AId0HAgCADQAh3gdAAM0MACEQRQEAAAABrwYCAAAAAbgGQAAAAAG5BkAAAAABxQYBAAAAAdUGAQAAAAH4BgEAAAABrgcBAAAAAbAHIAAAAAGxByAAAAABsgcgAAAAAbMHIAAAAAG0ByAAAAABtQcgAAAAAbYHIAAAAAG3BwIAAAABAgQGAgUAAwEDAAEBBAcAAAAAAwUACBgACRkACgAAAAMFAAgYAAkZAAoBAwABAQMAAQUFAA8YABIZABMqABArABEAAAAAAAUFAA8YABIZABMqABArABEBMAAWDwUAUy86FzFBGTJFFTiDAh45gQIdaIQCHHJJGnOAAhp0ggIddYUCKXaGAil3hwIpeIgCKXmJAioCBQAYLj4WAS4_AAEwQhYKBQBSNU0bOfQBHVn3ASlo4AEca-QBTG7yAVFv9QEWcPYBFnH4ASkFBQBLNNkBGjnaAR1Z2wEpYE8cBwUASjNQFjQAGjVRG1nTASlmVR1n1AEpDgUASTRrGjhZHjoAHzteITxiIz1mJT5pFj9qFkBsG0FtHEJvJ1jQAStlzQFHAjYAHTdaFgIFACA5Wx0BOVwAAgUAIjlfHQE5YAACBQAkOWMdATlkAAIFACY5Zx0BOWgABAUARjlwHVXIAStkdCgDBQBFQgAnWXgpEgUARDPEARY0vQEaN8MBFkC-ARtCAEJFfCpYgAErWgA6WwA8XAA-XQBAXrsBKF-8ARZgvwEcYcABGmLBARxjwgEWAkMAFkQAKQQ2ggEdRIEBKVYALFcAJwgFADlGhgEsR4cBLEsALUyQATFRlgEzVKoBNlWrASsDBQAwSQAuSo0BLAIFAC9IiwEtAUiMAQABSo4BAAIFADJKkQEsAUqSAQADTqkBNU-oASxTADQDBQA4TZcBM06ZATUEBQA3UJ0BNlGfATNSowE0Ak4ANU-eASwDUKQBAFGlAQBSpgEAAU2nAQAFRqwBAEetAQBRrgEAVK8BAFWwAQACBQA7WbEBKQFZsgEAAgUAPVmzASkBWbQBAAIFAD9ZtQEpAVm2AQACBQBBWbcBKQFZuAEAAgUAQ1m5ASkBWboBAAJFxQEAWMYBAAFZxwEAAznJAQBVywEAZMoBAAIFAEg5zgEdATnPAQACONEBAFjSAQAENdUBAFnXAQBm1gEAZ9gBAAI53AEAWd0BAAYFAFA0ABo6AE1p5QFMauYBTG3sAU8CBQBOa-cBTAFr6AEAAWwATAJq7QEAbe4BAAE08wEaBzX5AQA5_QEAWf4BAGj6AQBr-wEAbvwBAHH_AQANL4oCADKLAgA4kAIAOY4CAGiRAgByjAIAc40CAHSPAgB1kgIAdpMCAHeUAgB4lQIAeZYCAAEwABYBMAAWAwUAVxgAWBkAWQAAAAMFAFcYAFgZAFkBMbYCGQExvAIZBQUAXhgAYRkAYioAXysAYAAAAAAABQUAXhgAYRkAYioAXysAYAAAAwUAZxgAaBkAaQAAAAMFAGcYAGgZAGkAAAUFAG4YAHEZAHIqAG8rAHAAAAAAAAUFAG4YAHEZAHIqAG8rAHAAAAUFAHcYAHoZAHsqAHgrAHkAAAAAAAUFAHcYAHoZAHsqAHgrAHkBNJQDGgE0mgMaBQUAgAEYAIMBGQCEASoAgQErAIIBAAAAAAAFBQCAARgAgwEZAIQBKgCBASsAggECb6wDFnCtAxYCb7MDFnC0AxYFBQCJARgAjAEZAI0BKgCKASsAiwEAAAAAAAUFAIkBGACMARkAjQEqAIoBKwCLAQIzxgMWNAAaAjPMAxY0ABoFBQCSARgAlQEZAJYBKgCTASsAlAEAAAAAAAUFAJIBGACVARkAlgEqAJMBKwCUAQI03wMaYN4DHAI05gMaYOUDHAUFAJsBGACeARkAnwEqAJwBKwCdAQAAAAAABQUAmwEYAJ4BGQCfASoAnAErAJ0BAAAABQUApQEYAKgBGQCpASoApgErAKcBAAAAAAAFBQClARgAqAEZAKkBKgCmASsApwEAAAUFAK4BGACxARkAsgEqAK8BKwCwAQAAAAAABQUArgEYALEBGQCyASoArwErALABAUkALgFJAC4FBQC3ARgAugEZALsBKgC4ASsAuQEAAAAAAAUFALcBGAC6ARkAuwEqALgBKwC5AQAABQUAwAEYAMMBGQDEASoAwQErAMIBAAAAAAAFBQDAARgAwwEZAMQBKgDBASsAwgEBTtgENQFO3gQ1BQUAyQEYAMwBGQDNASoAygErAMsBAAAAAAAFBQDJARgAzAEZAM0BKgDKASsAywEDTvEENU_wBCxTADQDTvgENU_3BCxTADQFBQDSARgA1QEZANYBKgDTASsA1AEAAAAAAAUFANIBGADVARkA1gEqANMBKwDUAQAABQUA2wEYAN4BGQDfASoA3AErAN0BAAAAAAAFBQDbARgA3gEZAN8BKgDcASsA3QECTgA1T6IFLAJOADVPqAUsBQUA5AEYAOcBGQDoASoA5QErAOYBAAAAAAAFBQDkARgA5wEZAOgBKgDlASsA5gECSwAtTLoFMQJLAC1MwAUxBQUA7QEYAPABGQDxASoA7gErAO8BAAAAAAAFBQDtARgA8AEZAPEBKgDuASsA7wEENtMFHUTSBSlWACxXACcENtoFHUTZBSlWACxXACcFBQD2ARgA-QEZAPoBKgD3ASsA-AEAAAAAAAUFAPYBGAD5ARkA-gEqAPcBKwD4AQ8z9QUWNO4FGjf0BRZA7wUbQgBCWgA6WwA8XAA-XQBAXuwFKF_tBRZg8AUcYfEFGmLyBRxj8wUWDzOEBhY0_QUaN4MGFkD-BRtCAEJaADpbADxcAD5dAEBe-wUoX_wFFmD_BRxhgAYaYoEGHGOCBhYFBQD_ARgAggIZAIMCKgCAAisAgQIAAAAAAAUFAP8BGACCAhkAgwIqAIACKwCBAgJDABZEACkCQwAWRAApBQUAiAIYAIsCGQCMAioAiQIrAIoCAAAAAAAFBQCIAhgAiwIZAIwCKgCJAisAigIAAAAFBQCSAhgAlQIZAJYCKgCTAisAlAIAAAAAAAUFAJICGACVAhkAlgIqAJMCKwCUAgFCACcBQgAnBQUAmwIYAJ4CGQCfAioAnAIrAJ0CAAAAAAAFBQCbAhgAngIZAJ8CKgCcAisAnQIAAAUFAKQCGACnAhkAqAIqAKUCKwCmAgAAAAAABQUApAIYAKcCGQCoAioApQIrAKYCAAAFBQCtAhgAsAIZALECKgCuAisArwIAAAAAAAUFAK0CGACwAhkAsQIqAK4CKwCvAgAABQUAtgIYALkCGQC6AioAtwIrALgCAAAAAAAFBQC2AhgAuQIZALoCKgC3AisAuAIAAAUFAL8CGADCAhkAwwIqAMACKwDBAgAAAAAABQUAvwIYAMICGQDDAioAwAIrAMECAAAFBQDIAhgAywIZAMwCKgDJAisAygIAAAAAAAUFAMgCGADLAhkAzAIqAMkCKwDKAgAAAAMFANICGADTAhkA1AIAAAADBQDSAhgA0wIZANQCAAAFBQDZAhgA3AIZAN0CKgDaAisA2wIAAAAAAAUFANkCGADcAhkA3QIqANoCKwDbAgAABQUA4gIYAOUCGQDmAioA4wIrAOQCAAAAAAAFBQDiAhgA5QIZAOYCKgDjAisA5AIAAAUFAOsCGADuAhkA7wIqAOwCKwDtAgAAAAAABQUA6wIYAO4CGQDvAioA7AIrAO0CCzS_CBo6AB87ugghPLsIIz28CCU-vQgWP74IFkDACBtBwQgcQsIIJ2XDCEcLNM4IGjoAHzvJCCE8yggjPcsIJT7MCBY_zQgWQM8IG0HQCBxC0QgnZdIIRwUFAPQCGAD3AhkA-AIqAPUCKwD2AgAAAAAABQUA9AIYAPcCGQD4AioA9QIrAPYCAgUAgAOKBd8I-wIDOuMI_AI84Qj6AosF5wj-AgIFAP0CigXkCPsCAYoF5QgAAgUA_wKKBegI-wIBigXpCAABigXqCAAAAAUFAIQDGACHAxkAiAMqAIUDKwCGAwAAAAAABQUAhAMYAIcDGQCIAyoAhQMrAIYDAAAFBQCNAxgAkAMZAJEDKgCOAysAjwMAAAAAAAUFAI0DGACQAxkAkQMqAI4DKwCPAwAABQUAlgMYAJkDGQCaAyoAlwMrAJgDAAAAAAAFBQCWAxgAmQMZAJoDKgCXAysAmAMDOrwJ_AI8uwn6AosFvQn-AgM6xAn8AjzDCfoCiwXFCf4CBQUAnwMYAKIDGQCjAyoAoAMrAKEDAAAAAAAFBQCfAxgAogMZAKMDKgCgAysAoQMAAAMFAKgDGACpAxkAqgMAAAADBQCoAxgAqQMZAKoDAWwATAFsAEwDBQCvAxgAsAMZALEDAAAAAwUArwMYALADGQCxAwM0ABo6AE1phgpMAzQAGjoATWmMCkwDBQC2AxgAtwMZALgDAAAAAwUAtgMYALcDGQC4AwI2AB03ngoWAjYAHTekChYDBQC9AxgAvgMZAL8DAAAAAwUAvQMYAL4DGQC_AwAABQUAxAMYAMcDGQDIAyoAxQMrAMYDAAAAAAAFBQDEAxgAxwMZAMgDKgDFAysAxgMAAAUFAM0DGADQAxkA0QMqAM4DKwDPAwAAAAAABQUAzQMYANADGQDRAyoAzgMrAM8DBgIBBwgBCAsBCQwBCg0BDA8BDREEDhIFDxQBEBYEERcGFBgBFRkBFhoEGh0HGx4LHB8CHSACHiECHyICICMCISUCIicEIygMJCoCJSwEJi0NJy4CKC8CKTAELDMOLTQUejYVe5cCFXyYAhV9mQIVfpoCFX-cAhWAAZ4CBIEBnwJUggGhAhWDAaMCBIQBpAJVhQGlAhWGAaYCFYcBpwIEiAGqAlaJAasCWooBrAIWiwGtAhaMAa4CFo0BrwIWjgGwAhaPAbICFpABtAIEkQG1AluSAbgCFpMBugIElAG7AlyVAb0CFpYBvgIWlwG_AgSYAcICXZkBwwJjmgHEAhebAcUCF5wBxgIXnQHHAheeAcgCF58BygIXoAHMAgShAc0CZKIBzwIXowHRAgSkAdICZaUB0wIXpgHUAhenAdUCBKgB2AJmqQHZAmqqAdsCJ6sB3AInrAHeAietAd8CJ64B4AInrwHiAiewAeQCBLEB5QJrsgHnAiezAekCBLQB6gJstQHrAie2AewCJ7cB7QIEuAHwAm25AfECc7oB8wJHuwH0Ake8AfYCR70B9wJHvgH4Ake_AfoCR8AB_AIEwQH9AnTCAf8CR8MBgQMExAGCA3XFAYMDR8YBhANHxwGFAwTIAYgDdskBiQN8ygGKA1HLAYsDUcwBjANRzQGNA1HOAY4DUc8BkANR0AGSAwTRAZMDfdIBlgNR0wGYAwTUAZkDftUBmwNR1gGcA1HXAZ0DBNgBoAN_2QGhA4UB2gGiAxrbAaMDGtwBpAMa3QGlAxreAaYDGt8BqAMa4AGqAwThAasDhgHiAa8DGuMBsQME5AGyA4cB5QG1AxrmAbYDGucBtwME6AG6A4gB6QG7A44B6gG8AxzrAb0DHOwBvgMc7QG_AxzuAcADHO8BwgMc8AHEAwTxAcUDjwHyAcgDHPMBygME9AHLA5AB9QHNAxz2Ac4DHPcBzwME-AHSA5EB-QHTA5cB-gHUAxv7AdUDG_wB1gMb_QHXAxv-AdgDG_8B2gMbgALcAwSBAt0DmAGCAuEDG4MC4wMEhALkA5kBhQLnAxuGAugDG4cC6QMEiALsA5oBiQLtA6ABigLvA6EBiwLwA6EBjALzA6EBjQL0A6EBjgL1A6EBjwL3A6EBkAL5AwSRAvoDogGSAvwDoQGTAv4DBJQC_wOjAZUCgAShAZYCgQShAZcCggQEmAKFBKQBmQKGBKoBmgKIBC6bAokELpwCjAQunQKNBC6eAo4ELp8CkAQuoAKSBAShApMEqwGiApUELqMClwQEpAKYBKwBpQKZBC6mApoELqcCmwQEqAKeBK0BqQKfBLMBqgKgBC2rAqEELawCogQtrQKjBC2uAqQELa8CpgQtsAKoBASxAqkEtAGyAqsELbMCrQQEtAKuBLUBtQKvBC22ArAELbcCsQQEuAK0BLYBuQK1BLwBugK3BDG7ArgEMbwCugQxvQK7BDG-ArwEMb8CvgQxwALABATBAsEEvQHCAsMEMcMCxQQExALGBL4BxQLHBDHGAsgEMccCyQQEyALMBL8ByQLNBMUBygLOBDTLAs8ENMwC0AQ0zQLRBDTOAtIENM8C1AQ00ALWBATRAtcExgHSAtoENNMC3AQE1ALdBMcB1QLfBDTWAuAENNcC4QQE2ALkBMgB2QLlBM4B2gLmBDPbAucEM9wC6AQz3QLpBDPeAuoEM98C7AQz4ALuBAThAu8EzwHiAvMEM-MC9QQE5AL2BNAB5QL5BDPmAvoEM-cC-wQE6AL-BNEB6QL_BNcB6gKBBTXrAoIFNewChAU17QKFBTXuAoYFNe8CiAU18AKKBQTxAosF2AHyAo0FNfMCjwUE9AKQBdkB9QKRBTX2ApIFNfcCkwUE-AKWBdoB-QKXBeAB-gKYBTb7ApkFNvwCmgU2_QKbBTb-ApwFNv8CngU2gAOgBQSBA6EF4QGCA6QFNoMDpgUEhAOnBeIBhQOpBTaGA6oFNocDqwUEiAOuBeMBiQOvBekBigOwBSyLA7EFLIwDsgUsjQOzBSyOA7QFLI8DtgUskAO4BQSRA7kF6gGSA7wFLJMDvgUElAO_BesBlQPBBSyWA8IFLJcDwwUEmAPGBewBmQPHBfIBmgPIBSubA8kFK5wDygUrnQPLBSueA8wFK58DzgUroAPQBQShA9EF8wGiA9UFK6MD1wUEpAPYBfQBpQPbBSumA9wFK6cD3QUEqAPgBfUBqQPhBfsBqgPiBSmrA-MFKawD5AUprQPlBSmuA-YFKa8D6AUpsAPqBQSxA-sF_AGyA_cFKbMD-QUEtAP6Bf0BtQOFBim2A4YGKbcDhwYEuAOKBv4BuQOLBoQCugOMBiq7A40GKrwDjgYqvQOPBiq-A5AGKr8DkgYqwAOUBgTBA5UGhQLCA5cGKsMDmQYExAOaBoYCxQObBirGA5wGKscDnQYEyAOgBocCyQOhBo0CygOjBo4CywOkBo4CzAOnBo4CzQOoBo4CzgOpBo4CzwOrBo4C0AOtBgTRA64GjwLSA7AGjgLTA7IGBNQDswaQAtUDtAaOAtYDtQaOAtcDtgYE2AO5BpEC2QO6BpcC2gO7BijbA7wGKNwDvQYo3QO-BijeA78GKN8DwQYo4APDBgThA8QGmALiA8YGKOMDyAYE5APJBpkC5QPKBijmA8sGKOcDzAYE6APPBpoC6QPQBqAC6gPSBkLrA9MGQuwD1gZC7QPXBkLuA9gGQu8D2gZC8APcBgTxA90GoQLyA98GQvMD4QYE9APiBqIC9QPjBkL2A-QGQvcD5QYE-APoBqMC-QPpBqkC-gPrBkD7A-wGQPwD7wZA_QPwBkD-A_EGQP8D8wZAgAT1BgSBBPYGqgKCBPgGQIME-gYEhAT7BqsChQT8BkCGBP0GQIcE_gYEiASBB6wCiQSCB7ICigSEBzyLBIUHPIwEiAc8jQSJBzyOBIoHPI8EjAc8kASOBwSRBI8HswKSBJEHPJMEkwcElASUB7QClQSVBzyWBJYHPJcElwcEmASaB7UCmQSbB7sCmgSdBz6bBJ4HPpwEoQc-nQSiBz6eBKMHPp8EpQc-oASnBwShBKgHvAKiBKoHPqMErAcEpAStB70CpQSuBz6mBK8HPqcEsAcEqASzB74CqQS0B8QCqgS2BzqrBLcHOqwEugc6rQS7BzquBLwHOq8Evgc6sATABwSxBMEHxQKyBMMHOrMExQcEtATGB8YCtQTHBzq2BMgHOrcEyQcEuATMB8cCuQTNB80CugTPB84CuwTQB84CvATTB84CvQTUB84CvgTVB84CvwTXB84CwATZBwTBBNoHzwLCBNwHzgLDBN4HBMQE3wfQAsUE4AfOAsYE4QfOAscE4gcEyATlB9ECyQTmB9UCygToByHLBOkHIcwE6wchzQTsByHOBO0HIc8E7wch0ATxBwTRBPIH1gLSBPQHIdME9gcE1AT3B9cC1QT4ByHWBPkHIdcE-gcE2AT9B9gC2QT-B94C2gSACB_bBIEIH9wEhAgf3QSFCB_eBIYIH98EiAgf4ASKCAThBIsI3wLiBI0IH-MEjwgE5ASQCOAC5QSRCB_mBJIIH-cEkwgE6ASWCOEC6QSXCOcC6gSZCCPrBJoII-wEnAgj7QSdCCPuBJ4II-8EoAgj8ASiCATxBKMI6ALyBKUII_MEpwgE9ASoCOkC9QSpCCP2BKoII_cEqwgE-ASuCOoC-QSvCPAC-gSwCB37BLEIHfwEsggd_QSzCB3-BLQIHf8EtggdgAW4CASBBbkI8QKCBcUIHYMFxwgEhAXICPIChQXTCB2GBdQIHYcF1QgEiAXYCPMCiQXZCPkCjAXbCPoCjQXrCPoCjgXtCPoCjwXuCPoCkAXvCPoCkQXxCPoCkgXzCASTBfQIgQOUBfYI-gKVBfgIBJYF-QiCA5cF-gj6ApgF-wj6ApkF_AgEmgX_CIMDmwWACYkDnAWCCfwCnQWDCfwCngWFCfwCnwWGCfwCoAWHCfwCoQWJCfwCogWLCQSjBYwJigOkBY4J_AKlBZAJBKYFkQmLA6cFkgn8AqgFkwn8AqkFlAkEqgWXCYwDqwWYCZIDrAWaCf4CrQWbCf4CrgWdCf4CrwWeCf4CsAWfCf4CsQWhCf4CsgWjCQSzBaQJkwO0BaYJ_gK1BagJBLYFqQmUA7cFqgn-ArgFqwn-ArkFrAkEugWvCZUDuwWwCZsDvAWxCfsCvQWyCfsCvgWzCfsCvwW0CfsCwAW1CfsCwQW3CfsCwgW5CQTDBboJnAPEBb8J-wLFBcEJBMYFwgmdA8cFxgn7AsgFxwn7AskFyAkEygXLCZ4DywXMCaQDzAXOCU3NBc8JTc4F0glNzwXTCU3QBdQJTdEF1glN0gXYCQTTBdkJpQPUBdsJTdUF3QkE1gXeCaYD1wXfCU3YBeAJTdkF4QkE2gXkCacD2wXlCasD3AXmCU_dBecJT94F6AlP3wXpCU_gBeoJT-EF7AlP4gXuCQTjBe8JrAPkBfEJT-UF8wkE5gX0Ca0D5wX1CU_oBfYJT-kF9wkE6gX6Ca4D6wX7CbID7AX8CUztBf0JTO4F_glM7wX_CUzwBYAKTPEFggpM8gWECgTzBYUKswP0BYgKTPUFigoE9gWLCrQD9wWNCkz4BY4KTPkFjwoE-gWSCrUD-wWTCrkD_AWUCh79BZUKHv4Flgoe_wWXCh6ABpgKHoEGmgoeggacCgSDBp0KugOEBqAKHoUGogoEhgajCrsDhwalCh6IBqYKHokGpwoEigaqCrwDiwarCsADjAatCiWNBq4KJY4GsAoljwaxCiWQBrIKJZEGtAolkga2CgSTBrcKwQOUBrkKJZUGuwoElga8CsIDlwa9CiWYBr4KJZkGvwoEmgbCCsMDmwbDCskDnAbFChmdBsYKGZ4GyAoZnwbJChmgBsoKGaEGzAoZogbOCgSjBs8KygOkBtEKGaUG0woEpgbUCssDpwbVChmoBtYKGakG1woEqgbaCswDqwbbCtID" +} async function decodeBase64AsWasm(wasmBase64: string): Promise { const { Buffer } = await import('node:buffer') @@ -59,9 +67,11 @@ export interface PrismaClientConstructor { * Type-safe database client for TypeScript * @example * ``` - * const prisma = new PrismaClient() - * // Fetch zero or more Sessions - * const sessions = await prisma.session.findMany() + * const prisma = new PrismaClient({ + * adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }) + * }) + * // Fetch zero or more SyncJobRuns + * const syncJobRuns = await prisma.syncJobRun.findMany() * ``` * * Read more in our [docs](https://pris.ly/d/client). @@ -81,9 +91,11 @@ export interface PrismaClientConstructor { * Type-safe database client for TypeScript * @example * ``` - * const prisma = new PrismaClient() - * // Fetch zero or more Sessions - * const sessions = await prisma.session.findMany() + * const prisma = new PrismaClient({ + * adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }) + * }) + * // Fetch zero or more SyncJobRuns + * const syncJobRuns = await prisma.syncJobRun.findMany() * ``` * * Read more in our [docs](https://pris.ly/d/client). @@ -166,7 +178,7 @@ export interface PrismaClient< * ]) * ``` * - * Read more in our [docs](https://www.prisma.io/docs/concepts/components/prisma-client/transactions). + * Read more in our [docs](https://www.prisma.io/docs/orm/prisma-client/queries/transactions). */ $transaction

[]>(arg: [...P], options?: { isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise> @@ -177,6 +189,26 @@ export interface PrismaClient< }>> /** + * `prisma.syncJobRun`: Exposes CRUD operations for the **SyncJobRun** model. + * Example usage: + * ```ts + * // Fetch zero or more SyncJobRuns + * const syncJobRuns = await prisma.syncJobRun.findMany() + * ``` + */ + get syncJobRun(): Prisma.SyncJobRunDelegate; + + /** + * `prisma.syncStepLog`: Exposes CRUD operations for the **SyncStepLog** model. + * Example usage: + * ```ts + * // Fetch zero or more SyncStepLogs + * const syncStepLogs = await prisma.syncStepLog.findMany() + * ``` + */ + get syncStepLog(): Prisma.SyncStepLogDelegate; + + /** * `prisma.session`: Exposes CRUD operations for the **Session** model. * Example usage: * ```ts @@ -206,6 +238,26 @@ export interface PrismaClient< */ get role(): Prisma.RoleDelegate; + /** + * `prisma.corporateLocation`: Exposes CRUD operations for the **CorporateLocation** model. + * Example usage: + * ```ts + * // Fetch zero or more CorporateLocations + * const corporateLocations = await prisma.corporateLocation.findMany() + * ``` + */ + get corporateLocation(): Prisma.CorporateLocationDelegate; + + /** + * `prisma.internalDepartment`: Exposes CRUD operations for the **InternalDepartment** model. + * Example usage: + * ```ts + * // Fetch zero or more InternalDepartments + * const internalDepartments = await prisma.internalDepartment.findMany() + * ``` + */ + get internalDepartment(): Prisma.InternalDepartmentDelegate; + /** * `prisma.unifiSite`: Exposes CRUD operations for the **UnifiSite** model. * Example usage: @@ -226,6 +278,106 @@ export interface PrismaClient< */ get company(): Prisma.CompanyDelegate; + /** + * `prisma.companyAddress`: Exposes CRUD operations for the **CompanyAddress** model. + * Example usage: + * ```ts + * // Fetch zero or more CompanyAddresses + * const companyAddresses = await prisma.companyAddress.findMany() + * ``` + */ + get companyAddress(): Prisma.CompanyAddressDelegate; + + /** + * `prisma.contact`: Exposes CRUD operations for the **Contact** model. + * Example usage: + * ```ts + * // Fetch zero or more Contacts + * const contacts = await prisma.contact.findMany() + * ``` + */ + get contact(): Prisma.ContactDelegate; + + /** + * `prisma.catalogItemType`: Exposes CRUD operations for the **CatalogItemType** model. + * Example usage: + * ```ts + * // Fetch zero or more CatalogItemTypes + * const catalogItemTypes = await prisma.catalogItemType.findMany() + * ``` + */ + get catalogItemType(): Prisma.CatalogItemTypeDelegate; + + /** + * `prisma.catalogCategory`: Exposes CRUD operations for the **CatalogCategory** model. + * Example usage: + * ```ts + * // Fetch zero or more CatalogCategories + * const catalogCategories = await prisma.catalogCategory.findMany() + * ``` + */ + get catalogCategory(): Prisma.CatalogCategoryDelegate; + + /** + * `prisma.catalogSubcategory`: Exposes CRUD operations for the **CatalogSubcategory** model. + * Example usage: + * ```ts + * // Fetch zero or more CatalogSubcategories + * const catalogSubcategories = await prisma.catalogSubcategory.findMany() + * ``` + */ + get catalogSubcategory(): Prisma.CatalogSubcategoryDelegate; + + /** + * `prisma.catalogManufacturer`: Exposes CRUD operations for the **CatalogManufacturer** model. + * Example usage: + * ```ts + * // Fetch zero or more CatalogManufacturers + * const catalogManufacturers = await prisma.catalogManufacturer.findMany() + * ``` + */ + get catalogManufacturer(): Prisma.CatalogManufacturerDelegate; + + /** + * `prisma.warehouseBin`: Exposes CRUD operations for the **WarehouseBin** model. + * Example usage: + * ```ts + * // Fetch zero or more WarehouseBins + * const warehouseBins = await prisma.warehouseBin.findMany() + * ``` + */ + get warehouseBin(): Prisma.WarehouseBinDelegate; + + /** + * `prisma.productInventory`: Exposes CRUD operations for the **ProductInventory** model. + * Example usage: + * ```ts + * // Fetch zero or more ProductInventories + * const productInventories = await prisma.productInventory.findMany() + * ``` + */ + get productInventory(): Prisma.ProductInventoryDelegate; + + /** + * `prisma.warehouse`: Exposes CRUD operations for the **Warehouse** model. + * Example usage: + * ```ts + * // Fetch zero or more Warehouses + * const warehouses = await prisma.warehouse.findMany() + * ``` + */ + get warehouse(): Prisma.WarehouseDelegate; + + /** + * `prisma.minimumStockByWarehouse`: Exposes CRUD operations for the **MinimumStockByWarehouse** model. + * Example usage: + * ```ts + * // Fetch zero or more MinimumStockByWarehouses + * const minimumStockByWarehouses = await prisma.minimumStockByWarehouse.findMany() + * ``` + */ + get minimumStockByWarehouse(): Prisma.MinimumStockByWarehouseDelegate; + /** * `prisma.catalogItem`: Exposes CRUD operations for the **CatalogItem** model. * Example usage: @@ -236,6 +388,146 @@ export interface PrismaClient< */ get catalogItem(): Prisma.CatalogItemDelegate; + /** + * `prisma.productData`: Exposes CRUD operations for the **ProductData** model. + * Example usage: + * ```ts + * // Fetch zero or more ProductData + * const productData = await prisma.productData.findMany() + * ``` + */ + get productData(): Prisma.ProductDataDelegate; + + /** + * `prisma.serviceTicket`: Exposes CRUD operations for the **ServiceTicket** model. + * Example usage: + * ```ts + * // Fetch zero or more ServiceTickets + * const serviceTickets = await prisma.serviceTicket.findMany() + * ``` + */ + get serviceTicket(): Prisma.ServiceTicketDelegate; + + /** + * `prisma.serviceTicketNote`: Exposes CRUD operations for the **ServiceTicketNote** model. + * Example usage: + * ```ts + * // Fetch zero or more ServiceTicketNotes + * const serviceTicketNotes = await prisma.serviceTicketNote.findMany() + * ``` + */ + get serviceTicketNote(): Prisma.ServiceTicketNoteDelegate; + + /** + * `prisma.serviceTicketType`: Exposes CRUD operations for the **ServiceTicketType** model. + * Example usage: + * ```ts + * // Fetch zero or more ServiceTicketTypes + * const serviceTicketTypes = await prisma.serviceTicketType.findMany() + * ``` + */ + get serviceTicketType(): Prisma.ServiceTicketTypeDelegate; + + /** + * `prisma.serviceTicketBoard`: Exposes CRUD operations for the **ServiceTicketBoard** model. + * Example usage: + * ```ts + * // Fetch zero or more ServiceTicketBoards + * const serviceTicketBoards = await prisma.serviceTicketBoard.findMany() + * ``` + */ + get serviceTicketBoard(): Prisma.ServiceTicketBoardDelegate; + + /** + * `prisma.serviceTicketLocation`: Exposes CRUD operations for the **ServiceTicketLocation** model. + * Example usage: + * ```ts + * // Fetch zero or more ServiceTicketLocations + * const serviceTicketLocations = await prisma.serviceTicketLocation.findMany() + * ``` + */ + get serviceTicketLocation(): Prisma.ServiceTicketLocationDelegate; + + /** + * `prisma.serviceTicketSource`: Exposes CRUD operations for the **ServiceTicketSource** model. + * Example usage: + * ```ts + * // Fetch zero or more ServiceTicketSources + * const serviceTicketSources = await prisma.serviceTicketSource.findMany() + * ``` + */ + get serviceTicketSource(): Prisma.ServiceTicketSourceDelegate; + + /** + * `prisma.serviceTicketImpact`: Exposes CRUD operations for the **ServiceTicketImpact** model. + * Example usage: + * ```ts + * // Fetch zero or more ServiceTicketImpacts + * const serviceTicketImpacts = await prisma.serviceTicketImpact.findMany() + * ``` + */ + get serviceTicketImpact(): Prisma.ServiceTicketImpactDelegate; + + /** + * `prisma.serviceTicketPriority`: Exposes CRUD operations for the **ServiceTicketPriority** model. + * Example usage: + * ```ts + * // Fetch zero or more ServiceTicketPriorities + * const serviceTicketPriorities = await prisma.serviceTicketPriority.findMany() + * ``` + */ + get serviceTicketPriority(): Prisma.ServiceTicketPriorityDelegate; + + /** + * `prisma.serviceTicketSeverity`: Exposes CRUD operations for the **ServiceTicketSeverity** model. + * Example usage: + * ```ts + * // Fetch zero or more ServiceTicketSeverities + * const serviceTicketSeverities = await prisma.serviceTicketSeverity.findMany() + * ``` + */ + get serviceTicketSeverity(): Prisma.ServiceTicketSeverityDelegate; + + /** + * `prisma.serviceTicketFinalData`: Exposes CRUD operations for the **ServiceTicketFinalData** model. + * Example usage: + * ```ts + * // Fetch zero or more ServiceTicketFinalData + * const serviceTicketFinalData = await prisma.serviceTicketFinalData.findMany() + * ``` + */ + get serviceTicketFinalData(): Prisma.ServiceTicketFinalDataDelegate; + + /** + * `prisma.opportunityStage`: Exposes CRUD operations for the **OpportunityStage** model. + * Example usage: + * ```ts + * // Fetch zero or more OpportunityStages + * const opportunityStages = await prisma.opportunityStage.findMany() + * ``` + */ + get opportunityStage(): Prisma.OpportunityStageDelegate; + + /** + * `prisma.opportunityType`: Exposes CRUD operations for the **OpportunityType** model. + * Example usage: + * ```ts + * // Fetch zero or more OpportunityTypes + * const opportunityTypes = await prisma.opportunityType.findMany() + * ``` + */ + get opportunityType(): Prisma.OpportunityTypeDelegate; + + /** + * `prisma.opportunityStatus`: Exposes CRUD operations for the **OpportunityStatus** model. + * Example usage: + * ```ts + * // Fetch zero or more OpportunityStatuses + * const opportunityStatuses = await prisma.opportunityStatus.findMany() + * ``` + */ + get opportunityStatus(): Prisma.OpportunityStatusDelegate; + /** * `prisma.opportunity`: Exposes CRUD operations for the **Opportunity** model. * Example usage: @@ -246,6 +538,46 @@ export interface PrismaClient< */ get opportunity(): Prisma.OpportunityDelegate; + /** + * `prisma.scheduleStatus`: Exposes CRUD operations for the **ScheduleStatus** model. + * Example usage: + * ```ts + * // Fetch zero or more ScheduleStatuses + * const scheduleStatuses = await prisma.scheduleStatus.findMany() + * ``` + */ + get scheduleStatus(): Prisma.ScheduleStatusDelegate; + + /** + * `prisma.scheduleType`: Exposes CRUD operations for the **ScheduleType** model. + * Example usage: + * ```ts + * // Fetch zero or more ScheduleTypes + * const scheduleTypes = await prisma.scheduleType.findMany() + * ``` + */ + get scheduleType(): Prisma.ScheduleTypeDelegate; + + /** + * `prisma.scheduleSpan`: Exposes CRUD operations for the **ScheduleSpan** model. + * Example usage: + * ```ts + * // Fetch zero or more ScheduleSpans + * const scheduleSpans = await prisma.scheduleSpan.findMany() + * ``` + */ + get scheduleSpan(): Prisma.ScheduleSpanDelegate; + + /** + * `prisma.schedule`: Exposes CRUD operations for the **Schedule** model. + * Example usage: + * ```ts + * // Fetch zero or more Schedules + * const schedules = await prisma.schedule.findMany() + * ``` + */ + get schedule(): Prisma.ScheduleDelegate; + /** * `prisma.credentialType`: Exposes CRUD operations for the **CredentialType** model. * Example usage: @@ -286,6 +618,16 @@ export interface PrismaClient< */ get generatedQuotes(): Prisma.GeneratedQuotesDelegate; + /** + * `prisma.taxCode`: Exposes CRUD operations for the **TaxCode** model. + * Example usage: + * ```ts + * // Fetch zero or more TaxCodes + * const taxCodes = await prisma.taxCode.findMany() + * ``` + */ + get taxCode(): Prisma.TaxCodeDelegate; + /** * `prisma.cwMember`: Exposes CRUD operations for the **CwMember** model. * Example usage: diff --git a/api/generated/prisma/internal/prismaNamespace.ts b/api/generated/prisma/internal/prismaNamespace.ts index 741fd2a..8199659 100644 --- a/api/generated/prisma/internal/prismaNamespace.ts +++ b/api/generated/prisma/internal/prismaNamespace.ts @@ -80,12 +80,12 @@ export type PrismaVersion = { } /** - * Prisma Client JS version: 7.3.0 - * Query Engine version: 9d6ad21cbbceab97458517b147a6a09ff43aa735 + * Prisma Client JS version: 7.5.0 + * Query Engine version: 280c870be64f457428992c43c1f6d557fab6e29e */ export const prismaVersion: PrismaVersion = { - client: "7.3.0", - engine: "9d6ad21cbbceab97458517b147a6a09ff43aa735" + client: "7.5.0", + engine: "280c870be64f457428992c43c1f6d557fab6e29e" } /** @@ -384,17 +384,50 @@ type FieldRefInputType = Model extends never ? never : FieldRe export const ModelName = { + SyncJobRun: 'SyncJobRun', + SyncStepLog: 'SyncStepLog', Session: 'Session', User: 'User', Role: 'Role', + CorporateLocation: 'CorporateLocation', + InternalDepartment: 'InternalDepartment', UnifiSite: 'UnifiSite', Company: 'Company', + CompanyAddress: 'CompanyAddress', + Contact: 'Contact', + CatalogItemType: 'CatalogItemType', + CatalogCategory: 'CatalogCategory', + CatalogSubcategory: 'CatalogSubcategory', + CatalogManufacturer: 'CatalogManufacturer', + WarehouseBin: 'WarehouseBin', + ProductInventory: 'ProductInventory', + Warehouse: 'Warehouse', + MinimumStockByWarehouse: 'MinimumStockByWarehouse', CatalogItem: 'CatalogItem', + ProductData: 'ProductData', + ServiceTicket: 'ServiceTicket', + ServiceTicketNote: 'ServiceTicketNote', + ServiceTicketType: 'ServiceTicketType', + ServiceTicketBoard: 'ServiceTicketBoard', + ServiceTicketLocation: 'ServiceTicketLocation', + ServiceTicketSource: 'ServiceTicketSource', + ServiceTicketImpact: 'ServiceTicketImpact', + ServiceTicketPriority: 'ServiceTicketPriority', + ServiceTicketSeverity: 'ServiceTicketSeverity', + ServiceTicketFinalData: 'ServiceTicketFinalData', + OpportunityStage: 'OpportunityStage', + OpportunityType: 'OpportunityType', + OpportunityStatus: 'OpportunityStatus', Opportunity: 'Opportunity', + ScheduleStatus: 'ScheduleStatus', + ScheduleType: 'ScheduleType', + ScheduleSpan: 'ScheduleSpan', + Schedule: 'Schedule', CredentialType: 'CredentialType', SecureValue: 'SecureValue', Credential: 'Credential', GeneratedQuotes: 'GeneratedQuotes', + TaxCode: 'TaxCode', CwMember: 'CwMember' } as const @@ -411,10 +444,158 @@ export type TypeMap + fields: Prisma.SyncJobRunFieldRefs + operations: { + findUnique: { + args: Prisma.SyncJobRunFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.SyncJobRunFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.SyncJobRunFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.SyncJobRunFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.SyncJobRunFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.SyncJobRunCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.SyncJobRunCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.SyncJobRunCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.SyncJobRunDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.SyncJobRunUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.SyncJobRunDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.SyncJobRunUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.SyncJobRunUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.SyncJobRunUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.SyncJobRunAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.SyncJobRunGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.SyncJobRunCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + SyncStepLog: { + payload: Prisma.$SyncStepLogPayload + fields: Prisma.SyncStepLogFieldRefs + operations: { + findUnique: { + args: Prisma.SyncStepLogFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.SyncStepLogFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.SyncStepLogFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.SyncStepLogFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.SyncStepLogFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.SyncStepLogCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.SyncStepLogCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.SyncStepLogCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.SyncStepLogDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.SyncStepLogUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.SyncStepLogDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.SyncStepLogUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.SyncStepLogUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.SyncStepLogUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.SyncStepLogAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.SyncStepLogGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.SyncStepLogCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } Session: { payload: Prisma.$SessionPayload fields: Prisma.SessionFieldRefs @@ -637,6 +818,154 @@ export type TypeMap + fields: Prisma.CorporateLocationFieldRefs + operations: { + findUnique: { + args: Prisma.CorporateLocationFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CorporateLocationFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CorporateLocationFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CorporateLocationFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CorporateLocationFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CorporateLocationCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CorporateLocationCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.CorporateLocationCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.CorporateLocationDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CorporateLocationUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CorporateLocationDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CorporateLocationUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.CorporateLocationUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.CorporateLocationUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CorporateLocationAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CorporateLocationGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CorporateLocationCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + InternalDepartment: { + payload: Prisma.$InternalDepartmentPayload + fields: Prisma.InternalDepartmentFieldRefs + operations: { + findUnique: { + args: Prisma.InternalDepartmentFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.InternalDepartmentFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.InternalDepartmentFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.InternalDepartmentFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.InternalDepartmentFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.InternalDepartmentCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.InternalDepartmentCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.InternalDepartmentCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.InternalDepartmentDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.InternalDepartmentUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.InternalDepartmentDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.InternalDepartmentUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.InternalDepartmentUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.InternalDepartmentUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.InternalDepartmentAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.InternalDepartmentGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.InternalDepartmentCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } UnifiSite: { payload: Prisma.$UnifiSitePayload fields: Prisma.UnifiSiteFieldRefs @@ -785,6 +1114,746 @@ export type TypeMap + fields: Prisma.CompanyAddressFieldRefs + operations: { + findUnique: { + args: Prisma.CompanyAddressFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CompanyAddressFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CompanyAddressFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CompanyAddressFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CompanyAddressFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CompanyAddressCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CompanyAddressCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.CompanyAddressCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.CompanyAddressDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CompanyAddressUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CompanyAddressDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CompanyAddressUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.CompanyAddressUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.CompanyAddressUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CompanyAddressAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CompanyAddressGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CompanyAddressCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + Contact: { + payload: Prisma.$ContactPayload + fields: Prisma.ContactFieldRefs + operations: { + findUnique: { + args: Prisma.ContactFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ContactFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ContactFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ContactFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ContactFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ContactCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ContactCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ContactCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ContactDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ContactUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ContactDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ContactUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ContactUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ContactUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ContactAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ContactGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ContactCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + CatalogItemType: { + payload: Prisma.$CatalogItemTypePayload + fields: Prisma.CatalogItemTypeFieldRefs + operations: { + findUnique: { + args: Prisma.CatalogItemTypeFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CatalogItemTypeFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CatalogItemTypeFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CatalogItemTypeFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CatalogItemTypeFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CatalogItemTypeCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CatalogItemTypeCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.CatalogItemTypeCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.CatalogItemTypeDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CatalogItemTypeUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CatalogItemTypeDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CatalogItemTypeUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.CatalogItemTypeUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.CatalogItemTypeUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CatalogItemTypeAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CatalogItemTypeGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CatalogItemTypeCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + CatalogCategory: { + payload: Prisma.$CatalogCategoryPayload + fields: Prisma.CatalogCategoryFieldRefs + operations: { + findUnique: { + args: Prisma.CatalogCategoryFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CatalogCategoryFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CatalogCategoryFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CatalogCategoryFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CatalogCategoryFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CatalogCategoryCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CatalogCategoryCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.CatalogCategoryCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.CatalogCategoryDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CatalogCategoryUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CatalogCategoryDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CatalogCategoryUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.CatalogCategoryUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.CatalogCategoryUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CatalogCategoryAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CatalogCategoryGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CatalogCategoryCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + CatalogSubcategory: { + payload: Prisma.$CatalogSubcategoryPayload + fields: Prisma.CatalogSubcategoryFieldRefs + operations: { + findUnique: { + args: Prisma.CatalogSubcategoryFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CatalogSubcategoryFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CatalogSubcategoryFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CatalogSubcategoryFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CatalogSubcategoryFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CatalogSubcategoryCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CatalogSubcategoryCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.CatalogSubcategoryCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.CatalogSubcategoryDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CatalogSubcategoryUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CatalogSubcategoryDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CatalogSubcategoryUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.CatalogSubcategoryUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.CatalogSubcategoryUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CatalogSubcategoryAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CatalogSubcategoryGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CatalogSubcategoryCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + CatalogManufacturer: { + payload: Prisma.$CatalogManufacturerPayload + fields: Prisma.CatalogManufacturerFieldRefs + operations: { + findUnique: { + args: Prisma.CatalogManufacturerFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CatalogManufacturerFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.CatalogManufacturerFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CatalogManufacturerFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.CatalogManufacturerFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.CatalogManufacturerCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.CatalogManufacturerCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.CatalogManufacturerCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.CatalogManufacturerDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.CatalogManufacturerUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CatalogManufacturerDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CatalogManufacturerUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.CatalogManufacturerUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.CatalogManufacturerUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.CatalogManufacturerAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.CatalogManufacturerGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.CatalogManufacturerCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + WarehouseBin: { + payload: Prisma.$WarehouseBinPayload + fields: Prisma.WarehouseBinFieldRefs + operations: { + findUnique: { + args: Prisma.WarehouseBinFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.WarehouseBinFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.WarehouseBinFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.WarehouseBinFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.WarehouseBinFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.WarehouseBinCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.WarehouseBinCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.WarehouseBinCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.WarehouseBinDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.WarehouseBinUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.WarehouseBinDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.WarehouseBinUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.WarehouseBinUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.WarehouseBinUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.WarehouseBinAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.WarehouseBinGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.WarehouseBinCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ProductInventory: { + payload: Prisma.$ProductInventoryPayload + fields: Prisma.ProductInventoryFieldRefs + operations: { + findUnique: { + args: Prisma.ProductInventoryFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ProductInventoryFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ProductInventoryFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ProductInventoryFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ProductInventoryFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ProductInventoryCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ProductInventoryCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ProductInventoryCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ProductInventoryDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ProductInventoryUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ProductInventoryDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ProductInventoryUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ProductInventoryUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ProductInventoryUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ProductInventoryAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ProductInventoryGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ProductInventoryCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + Warehouse: { + payload: Prisma.$WarehousePayload + fields: Prisma.WarehouseFieldRefs + operations: { + findUnique: { + args: Prisma.WarehouseFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.WarehouseFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.WarehouseFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.WarehouseFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.WarehouseFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.WarehouseCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.WarehouseCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.WarehouseCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.WarehouseDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.WarehouseUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.WarehouseDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.WarehouseUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.WarehouseUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.WarehouseUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.WarehouseAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.WarehouseGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.WarehouseCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + MinimumStockByWarehouse: { + payload: Prisma.$MinimumStockByWarehousePayload + fields: Prisma.MinimumStockByWarehouseFieldRefs + operations: { + findUnique: { + args: Prisma.MinimumStockByWarehouseFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.MinimumStockByWarehouseFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.MinimumStockByWarehouseFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.MinimumStockByWarehouseFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.MinimumStockByWarehouseFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.MinimumStockByWarehouseCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.MinimumStockByWarehouseCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.MinimumStockByWarehouseCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.MinimumStockByWarehouseDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.MinimumStockByWarehouseUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.MinimumStockByWarehouseDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.MinimumStockByWarehouseUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.MinimumStockByWarehouseUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.MinimumStockByWarehouseUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.MinimumStockByWarehouseAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.MinimumStockByWarehouseGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.MinimumStockByWarehouseCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } CatalogItem: { payload: Prisma.$CatalogItemPayload fields: Prisma.CatalogItemFieldRefs @@ -859,6 +1928,1042 @@ export type TypeMap + fields: Prisma.ProductDataFieldRefs + operations: { + findUnique: { + args: Prisma.ProductDataFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ProductDataFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ProductDataFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ProductDataFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ProductDataFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ProductDataCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ProductDataCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ProductDataCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ProductDataDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ProductDataUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ProductDataDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ProductDataUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ProductDataUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ProductDataUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ProductDataAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ProductDataGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ProductDataCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ServiceTicket: { + payload: Prisma.$ServiceTicketPayload + fields: Prisma.ServiceTicketFieldRefs + operations: { + findUnique: { + args: Prisma.ServiceTicketFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ServiceTicketFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ServiceTicketFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ServiceTicketFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ServiceTicketFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ServiceTicketCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ServiceTicketCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ServiceTicketCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ServiceTicketDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ServiceTicketUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ServiceTicketDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ServiceTicketUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ServiceTicketUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ServiceTicketUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ServiceTicketAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ServiceTicketGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ServiceTicketCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ServiceTicketNote: { + payload: Prisma.$ServiceTicketNotePayload + fields: Prisma.ServiceTicketNoteFieldRefs + operations: { + findUnique: { + args: Prisma.ServiceTicketNoteFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ServiceTicketNoteFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ServiceTicketNoteFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ServiceTicketNoteFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ServiceTicketNoteFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ServiceTicketNoteCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ServiceTicketNoteCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ServiceTicketNoteCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ServiceTicketNoteDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ServiceTicketNoteUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ServiceTicketNoteDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ServiceTicketNoteUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ServiceTicketNoteUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ServiceTicketNoteUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ServiceTicketNoteAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ServiceTicketNoteGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ServiceTicketNoteCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ServiceTicketType: { + payload: Prisma.$ServiceTicketTypePayload + fields: Prisma.ServiceTicketTypeFieldRefs + operations: { + findUnique: { + args: Prisma.ServiceTicketTypeFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ServiceTicketTypeFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ServiceTicketTypeFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ServiceTicketTypeFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ServiceTicketTypeFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ServiceTicketTypeCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ServiceTicketTypeCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ServiceTicketTypeCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ServiceTicketTypeDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ServiceTicketTypeUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ServiceTicketTypeDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ServiceTicketTypeUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ServiceTicketTypeUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ServiceTicketTypeUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ServiceTicketTypeAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ServiceTicketTypeGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ServiceTicketTypeCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ServiceTicketBoard: { + payload: Prisma.$ServiceTicketBoardPayload + fields: Prisma.ServiceTicketBoardFieldRefs + operations: { + findUnique: { + args: Prisma.ServiceTicketBoardFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ServiceTicketBoardFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ServiceTicketBoardFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ServiceTicketBoardFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ServiceTicketBoardFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ServiceTicketBoardCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ServiceTicketBoardCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ServiceTicketBoardCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ServiceTicketBoardDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ServiceTicketBoardUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ServiceTicketBoardDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ServiceTicketBoardUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ServiceTicketBoardUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ServiceTicketBoardUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ServiceTicketBoardAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ServiceTicketBoardGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ServiceTicketBoardCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ServiceTicketLocation: { + payload: Prisma.$ServiceTicketLocationPayload + fields: Prisma.ServiceTicketLocationFieldRefs + operations: { + findUnique: { + args: Prisma.ServiceTicketLocationFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ServiceTicketLocationFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ServiceTicketLocationFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ServiceTicketLocationFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ServiceTicketLocationFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ServiceTicketLocationCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ServiceTicketLocationCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ServiceTicketLocationCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ServiceTicketLocationDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ServiceTicketLocationUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ServiceTicketLocationDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ServiceTicketLocationUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ServiceTicketLocationUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ServiceTicketLocationUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ServiceTicketLocationAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ServiceTicketLocationGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ServiceTicketLocationCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ServiceTicketSource: { + payload: Prisma.$ServiceTicketSourcePayload + fields: Prisma.ServiceTicketSourceFieldRefs + operations: { + findUnique: { + args: Prisma.ServiceTicketSourceFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ServiceTicketSourceFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ServiceTicketSourceFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ServiceTicketSourceFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ServiceTicketSourceFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ServiceTicketSourceCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ServiceTicketSourceCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ServiceTicketSourceCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ServiceTicketSourceDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ServiceTicketSourceUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ServiceTicketSourceDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ServiceTicketSourceUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ServiceTicketSourceUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ServiceTicketSourceUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ServiceTicketSourceAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ServiceTicketSourceGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ServiceTicketSourceCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ServiceTicketImpact: { + payload: Prisma.$ServiceTicketImpactPayload + fields: Prisma.ServiceTicketImpactFieldRefs + operations: { + findUnique: { + args: Prisma.ServiceTicketImpactFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ServiceTicketImpactFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ServiceTicketImpactFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ServiceTicketImpactFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ServiceTicketImpactFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ServiceTicketImpactCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ServiceTicketImpactCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ServiceTicketImpactCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ServiceTicketImpactDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ServiceTicketImpactUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ServiceTicketImpactDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ServiceTicketImpactUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ServiceTicketImpactUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ServiceTicketImpactUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ServiceTicketImpactAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ServiceTicketImpactGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ServiceTicketImpactCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ServiceTicketPriority: { + payload: Prisma.$ServiceTicketPriorityPayload + fields: Prisma.ServiceTicketPriorityFieldRefs + operations: { + findUnique: { + args: Prisma.ServiceTicketPriorityFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ServiceTicketPriorityFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ServiceTicketPriorityFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ServiceTicketPriorityFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ServiceTicketPriorityFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ServiceTicketPriorityCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ServiceTicketPriorityCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ServiceTicketPriorityCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ServiceTicketPriorityDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ServiceTicketPriorityUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ServiceTicketPriorityDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ServiceTicketPriorityUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ServiceTicketPriorityUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ServiceTicketPriorityUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ServiceTicketPriorityAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ServiceTicketPriorityGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ServiceTicketPriorityCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ServiceTicketSeverity: { + payload: Prisma.$ServiceTicketSeverityPayload + fields: Prisma.ServiceTicketSeverityFieldRefs + operations: { + findUnique: { + args: Prisma.ServiceTicketSeverityFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ServiceTicketSeverityFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ServiceTicketSeverityFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ServiceTicketSeverityFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ServiceTicketSeverityFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ServiceTicketSeverityCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ServiceTicketSeverityCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ServiceTicketSeverityCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ServiceTicketSeverityDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ServiceTicketSeverityUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ServiceTicketSeverityDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ServiceTicketSeverityUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ServiceTicketSeverityUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ServiceTicketSeverityUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ServiceTicketSeverityAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ServiceTicketSeverityGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ServiceTicketSeverityCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ServiceTicketFinalData: { + payload: Prisma.$ServiceTicketFinalDataPayload + fields: Prisma.ServiceTicketFinalDataFieldRefs + operations: { + findUnique: { + args: Prisma.ServiceTicketFinalDataFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ServiceTicketFinalDataFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ServiceTicketFinalDataFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ServiceTicketFinalDataFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ServiceTicketFinalDataFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ServiceTicketFinalDataCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ServiceTicketFinalDataCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ServiceTicketFinalDataCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ServiceTicketFinalDataDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ServiceTicketFinalDataUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ServiceTicketFinalDataDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ServiceTicketFinalDataUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ServiceTicketFinalDataUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ServiceTicketFinalDataUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ServiceTicketFinalDataAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ServiceTicketFinalDataGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ServiceTicketFinalDataCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + OpportunityStage: { + payload: Prisma.$OpportunityStagePayload + fields: Prisma.OpportunityStageFieldRefs + operations: { + findUnique: { + args: Prisma.OpportunityStageFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.OpportunityStageFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.OpportunityStageFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.OpportunityStageFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.OpportunityStageFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.OpportunityStageCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.OpportunityStageCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.OpportunityStageCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.OpportunityStageDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.OpportunityStageUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.OpportunityStageDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.OpportunityStageUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.OpportunityStageUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.OpportunityStageUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.OpportunityStageAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.OpportunityStageGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.OpportunityStageCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + OpportunityType: { + payload: Prisma.$OpportunityTypePayload + fields: Prisma.OpportunityTypeFieldRefs + operations: { + findUnique: { + args: Prisma.OpportunityTypeFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.OpportunityTypeFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.OpportunityTypeFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.OpportunityTypeFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.OpportunityTypeFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.OpportunityTypeCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.OpportunityTypeCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.OpportunityTypeCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.OpportunityTypeDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.OpportunityTypeUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.OpportunityTypeDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.OpportunityTypeUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.OpportunityTypeUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.OpportunityTypeUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.OpportunityTypeAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.OpportunityTypeGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.OpportunityTypeCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + OpportunityStatus: { + payload: Prisma.$OpportunityStatusPayload + fields: Prisma.OpportunityStatusFieldRefs + operations: { + findUnique: { + args: Prisma.OpportunityStatusFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.OpportunityStatusFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.OpportunityStatusFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.OpportunityStatusFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.OpportunityStatusFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.OpportunityStatusCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.OpportunityStatusCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.OpportunityStatusCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.OpportunityStatusDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.OpportunityStatusUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.OpportunityStatusDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.OpportunityStatusUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.OpportunityStatusUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.OpportunityStatusUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.OpportunityStatusAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.OpportunityStatusGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.OpportunityStatusCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } Opportunity: { payload: Prisma.$OpportunityPayload fields: Prisma.OpportunityFieldRefs @@ -933,6 +3038,302 @@ export type TypeMap + fields: Prisma.ScheduleStatusFieldRefs + operations: { + findUnique: { + args: Prisma.ScheduleStatusFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ScheduleStatusFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ScheduleStatusFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ScheduleStatusFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ScheduleStatusFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ScheduleStatusCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ScheduleStatusCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ScheduleStatusCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ScheduleStatusDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ScheduleStatusUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ScheduleStatusDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ScheduleStatusUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ScheduleStatusUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ScheduleStatusUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ScheduleStatusAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ScheduleStatusGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ScheduleStatusCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ScheduleType: { + payload: Prisma.$ScheduleTypePayload + fields: Prisma.ScheduleTypeFieldRefs + operations: { + findUnique: { + args: Prisma.ScheduleTypeFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ScheduleTypeFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ScheduleTypeFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ScheduleTypeFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ScheduleTypeFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ScheduleTypeCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ScheduleTypeCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ScheduleTypeCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ScheduleTypeDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ScheduleTypeUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ScheduleTypeDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ScheduleTypeUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ScheduleTypeUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ScheduleTypeUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ScheduleTypeAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ScheduleTypeGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ScheduleTypeCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + ScheduleSpan: { + payload: Prisma.$ScheduleSpanPayload + fields: Prisma.ScheduleSpanFieldRefs + operations: { + findUnique: { + args: Prisma.ScheduleSpanFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ScheduleSpanFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ScheduleSpanFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ScheduleSpanFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ScheduleSpanFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ScheduleSpanCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ScheduleSpanCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ScheduleSpanCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ScheduleSpanDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ScheduleSpanUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ScheduleSpanDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ScheduleSpanUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ScheduleSpanUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ScheduleSpanUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ScheduleSpanAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ScheduleSpanGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ScheduleSpanCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } + Schedule: { + payload: Prisma.$SchedulePayload + fields: Prisma.ScheduleFieldRefs + operations: { + findUnique: { + args: Prisma.ScheduleFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.ScheduleFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.ScheduleFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.ScheduleFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.ScheduleFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.ScheduleCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.ScheduleCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.ScheduleCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.ScheduleDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.ScheduleUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.ScheduleDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.ScheduleUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.ScheduleUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.ScheduleUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.ScheduleAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.ScheduleGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.ScheduleCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } CredentialType: { payload: Prisma.$CredentialTypePayload fields: Prisma.CredentialTypeFieldRefs @@ -1229,6 +3630,80 @@ export type TypeMap + fields: Prisma.TaxCodeFieldRefs + operations: { + findUnique: { + args: Prisma.TaxCodeFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.TaxCodeFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.TaxCodeFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.TaxCodeFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.TaxCodeFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.TaxCodeCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.TaxCodeCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.TaxCodeCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.TaxCodeDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.TaxCodeUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.TaxCodeDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.TaxCodeUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.TaxCodeUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.TaxCodeUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.TaxCodeAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.TaxCodeGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.TaxCodeCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } CwMember: { payload: Prisma.$CwMemberPayload fields: Prisma.CwMemberFieldRefs @@ -1342,6 +3817,39 @@ export const TransactionIsolationLevel = runtime.makeStrictEnum({ export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel] +export const SyncJobRunScalarFieldEnum = { + id: 'id', + jobType: 'jobType', + status: 'status', + triggeredBy: 'triggeredBy', + startedAt: 'startedAt', + completedAt: 'completedAt', + errorSummary: 'errorSummary', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type SyncJobRunScalarFieldEnum = (typeof SyncJobRunScalarFieldEnum)[keyof typeof SyncJobRunScalarFieldEnum] + + +export const SyncStepLogScalarFieldEnum = { + id: 'id', + syncJobRunId: 'syncJobRunId', + tableName: 'tableName', + syncMode: 'syncMode', + recordsProcessed: 'recordsProcessed', + recordsInserted: 'recordsInserted', + recordsSkipped: 'recordsSkipped', + recordsFailed: 'recordsFailed', + recordsDeleted: 'recordsDeleted', + sampleErrors: 'sampleErrors', + durationMs: 'durationMs', + createdAt: 'createdAt' +} as const + +export type SyncStepLogScalarFieldEnum = (typeof SyncStepLogScalarFieldEnum)[keyof typeof SyncStepLogScalarFieldEnum] + + export const SessionScalarFieldEnum = { id: 'id', sessionKey: 'sessionKey', @@ -1359,13 +3867,18 @@ export const UserScalarFieldEnum = { id: 'id', permissions: 'permissions', login: 'login', - name: 'name', + firstName: 'firstName', + lastName: 'lastName', email: 'email', - emailVerified: 'emailVerified', image: 'image', + title: 'title', + active: 'active', + hidden: 'hidden', cwIdentifier: 'cwIdentifier', + cwMemberId: 'cwMemberId', userId: 'userId', token: 'token', + updatedBy: 'updatedBy', createdAt: 'createdAt', updatedAt: 'updatedAt' } as const @@ -1385,6 +3898,40 @@ export const RoleScalarFieldEnum = { export type RoleScalarFieldEnum = (typeof RoleScalarFieldEnum)[keyof typeof RoleScalarFieldEnum] +export const CorporateLocationScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + updatedById: 'updatedById', + addressLine1: 'addressLine1', + addressLine2: 'addressLine2', + city: 'city', + state: 'state', + zipCode: 'zipCode', + country: 'country', + inactiveFlag: 'inactiveFlag', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CorporateLocationScalarFieldEnum = (typeof CorporateLocationScalarFieldEnum)[keyof typeof CorporateLocationScalarFieldEnum] + + +export const InternalDepartmentScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + createdById: 'createdById', + updatedById: 'updatedById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type InternalDepartmentScalarFieldEnum = (typeof InternalDepartmentScalarFieldEnum)[keyof typeof InternalDepartmentScalarFieldEnum] + + export const UnifiSiteScalarFieldEnum = { id: 'id', name: 'name', @@ -1399,9 +3946,17 @@ export type UnifiSiteScalarFieldEnum = (typeof UnifiSiteScalarFieldEnum)[keyof t export const CompanyScalarFieldEnum = { id: 'id', + uid: 'uid', name: 'name', - cw_CompanyId: 'cw_CompanyId', - cw_Identifier: 'cw_Identifier', + phone: 'phone', + website: 'website', + deleteFlag: 'deleteFlag', + dateDeleted: 'dateDeleted', + taxId: 'taxId', + taxExempt: 'taxExempt', + enteredById: 'enteredById', + deletedById: 'deletedById', + deletedAt: 'deletedAt', createdAt: 'createdAt', updatedAt: 'updatedAt' } as const @@ -1409,20 +3964,195 @@ export const CompanyScalarFieldEnum = { export type CompanyScalarFieldEnum = (typeof CompanyScalarFieldEnum)[keyof typeof CompanyScalarFieldEnum] +export const CompanyAddressScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + addressLine1: 'addressLine1', + addressLine2: 'addressLine2', + city: 'city', + state: 'state', + zipCode: 'zipCode', + country: 'country', + phone: 'phone', + fax: 'fax', + inactiveFlag: 'inactiveFlag', + defaultFlag: 'defaultFlag', + defaultMailFlag: 'defaultMailFlag', + defaultBillFlag: 'defaultBillFlag', + defaultShipFlag: 'defaultShipFlag', + updatedById: 'updatedById', + companyId: 'companyId', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CompanyAddressScalarFieldEnum = (typeof CompanyAddressScalarFieldEnum)[keyof typeof CompanyAddressScalarFieldEnum] + + +export const ContactScalarFieldEnum = { + id: 'id', + uid: 'uid', + active: 'active', + default: 'default', + firstName: 'firstName', + lastName: 'lastName', + nickname: 'nickname', + title: 'title', + gender: 'gender', + birthday: 'birthday', + email: 'email', + phone: 'phone', + phoneExtension: 'phoneExtension', + phoneType: 'phoneType', + companyAddressId: 'companyAddressId', + memberId: 'memberId', + companyId: 'companyId', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ContactScalarFieldEnum = (typeof ContactScalarFieldEnum)[keyof typeof ContactScalarFieldEnum] + + +export const CatalogItemTypeScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CatalogItemTypeScalarFieldEnum = (typeof CatalogItemTypeScalarFieldEnum)[keyof typeof CatalogItemTypeScalarFieldEnum] + + +export const CatalogCategoryScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CatalogCategoryScalarFieldEnum = (typeof CatalogCategoryScalarFieldEnum)[keyof typeof CatalogCategoryScalarFieldEnum] + + +export const CatalogSubcategoryScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + categoryId: 'categoryId', + inactiveFlag: 'inactiveFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CatalogSubcategoryScalarFieldEnum = (typeof CatalogSubcategoryScalarFieldEnum)[keyof typeof CatalogSubcategoryScalarFieldEnum] + + +export const CatalogManufacturerScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CatalogManufacturerScalarFieldEnum = (typeof CatalogManufacturerScalarFieldEnum)[keyof typeof CatalogManufacturerScalarFieldEnum] + + +export const WarehouseBinScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + minQuantity: 'minQuantity', + maxQuantity: 'maxQuantity', + inactiveFlag: 'inactiveFlag', + defaultFlag: 'defaultFlag', + warehouseId: 'warehouseId', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type WarehouseBinScalarFieldEnum = (typeof WarehouseBinScalarFieldEnum)[keyof typeof WarehouseBinScalarFieldEnum] + + +export const ProductInventoryScalarFieldEnum = { + id: 'id', + uid: 'uid', + qtyOnHand: 'qtyOnHand', + warehouseBinId: 'warehouseBinId', + itemId: 'itemId', + warehouseId: 'warehouseId', + updatedById: 'updatedById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ProductInventoryScalarFieldEnum = (typeof ProductInventoryScalarFieldEnum)[keyof typeof ProductInventoryScalarFieldEnum] + + +export const WarehouseScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + lockedFlag: 'lockedFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type WarehouseScalarFieldEnum = (typeof WarehouseScalarFieldEnum)[keyof typeof WarehouseScalarFieldEnum] + + +export const MinimumStockByWarehouseScalarFieldEnum = { + id: 'id', + uid: 'uid', + minQty: 'minQty', + warehouseId: 'warehouseId', + itemId: 'itemId', + updatedById: 'updatedById', + enteredById: 'enteredById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type MinimumStockByWarehouseScalarFieldEnum = (typeof MinimumStockByWarehouseScalarFieldEnum)[keyof typeof MinimumStockByWarehouseScalarFieldEnum] + + export const CatalogItemScalarFieldEnum = { id: 'id', - cwCatalogId: 'cwCatalogId', + uid: 'uid', identifier: 'identifier', name: 'name', description: 'description', customerDescription: 'customerDescription', internalNotes: 'internalNotes', - category: 'category', - categoryCwId: 'categoryCwId', - subcategory: 'subcategory', - subcategoryCwId: 'subcategoryCwId', - manufacturer: 'manufacturer', - manufactureCwId: 'manufactureCwId', + subcategoryId: 'subcategoryId', + manufacturerId: 'manufacturerId', partNumber: 'partNumber', vendorName: 'vendorName', vendorSku: 'vendorSku', @@ -1433,6 +4163,7 @@ export const CatalogItemScalarFieldEnum = { salesTaxable: 'salesTaxable', onHand: 'onHand', cwLastUpdated: 'cwLastUpdated', + classId: 'classId', createdAt: 'createdAt', updatedAt: 'updatedAt' } as const @@ -1440,54 +4171,338 @@ export const CatalogItemScalarFieldEnum = { export type CatalogItemScalarFieldEnum = (typeof CatalogItemScalarFieldEnum)[keyof typeof CatalogItemScalarFieldEnum] +export const ProductDataScalarFieldEnum = { + id: 'id', + uid: 'uid', + qty: 'qty', + internalNote: 'internalNote', + shortDescription: 'shortDescription', + description: 'description', + sequenceNumber: 'sequenceNumber', + procurementNotes: 'procurementNotes', + productNarrative: 'productNarrative', + unitPrice: 'unitPrice', + unitCost: 'unitCost', + listPrice: 'listPrice', + discount: 'discount', + recurringRevenue: 'recurringRevenue', + recurringCost: 'recurringCost', + qtyPicked: 'qtyPicked', + qtyShipped: 'qtyShipped', + cancelReason: 'cancelReason', + cancelQty: 'cancelQty', + billableFlag: 'billableFlag', + taxableFlag: 'taxableFlag', + invoiceFlag: 'invoiceFlag', + recurringFlag: 'recurringFlag', + poApprovedFlag: 'poApprovedFlag', + calcPriceFlag: 'calcPriceFlag', + calcCostFlag: 'calcCostFlag', + cancelFlag: 'cancelFlag', + catalogItemId: 'catalogItemId', + corporateLocationId: 'corporateLocationId', + serviceTicketId: 'serviceTicketId', + opportunityId: 'opportunityId', + updatedById: 'updatedById', + createdById: 'createdById', + closedById: 'closedById', + cancelById: 'cancelById', + closedAt: 'closedAt', + cancelledAt: 'cancelledAt', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ProductDataScalarFieldEnum = (typeof ProductDataScalarFieldEnum)[keyof typeof ProductDataScalarFieldEnum] + + +export const ServiceTicketScalarFieldEnum = { + id: 'id', + uid: 'uid', + summary: 'summary', + addressLine1: 'addressLine1', + addressLine2: 'addressLine2', + city: 'city', + state: 'state', + zipCode: 'zipCode', + country: 'country', + contactName: 'contactName', + phone: 'phone', + phoneExtension: 'phoneExtension', + phoneType: 'phoneType', + email: 'email', + poNumber: 'poNumber', + billCompleteFlag: 'billCompleteFlag', + billUnapprovedFlag: 'billUnapprovedFlag', + billingAmount: 'billingAmount', + billingMethod: 'billingMethod', + timeBillableFlag: 'timeBillableFlag', + expenseBillableFlag: 'expenseBillableFlag', + productBillableFlag: 'productBillableFlag', + timeInvoiceableFlag: 'timeInvoiceableFlag', + expenseInvoiceableFlag: 'expenseInvoiceableFlag', + productInvoiceableFlag: 'productInvoiceableFlag', + dateRequested: 'dateRequested', + billingType: 'billingType', + billingInstructions: 'billingInstructions', + rejectedFlag: 'rejectedFlag', + closedFlag: 'closedFlag', + redFlag: 'redFlag', + publishFlag: 'publishFlag', + ticketOwnerId: 'ticketOwnerId', + serviceTicketBoardId: 'serviceTicketBoardId', + severityId: 'severityId', + impactId: 'impactId', + priorityId: 'priorityId', + sourceId: 'sourceId', + locationId: 'locationId', + parentId: 'parentId', + companyId: 'companyId', + contactId: 'contactId', + companyAddressId: 'companyAddressId', + billingCompanyId: 'billingCompanyId', + billingAddressId: 'billingAddressId', + createdById: 'createdById', + updatedById: 'updatedById', + closedById: 'closedById', + rejectedAt: 'rejectedAt', + closedAt: 'closedAt', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketScalarFieldEnum = (typeof ServiceTicketScalarFieldEnum)[keyof typeof ServiceTicketScalarFieldEnum] + + +export const ServiceTicketNoteScalarFieldEnum = { + id: 'id', + uid: 'uid', + notes: 'notes', + notesMd: 'notesMd', + authorId: 'authorId', + problemFlag: 'problemFlag', + resolutionFlag: 'resolutionFlag', + internalAnalysisFlag: 'internalAnalysisFlag', + internalMemberFlag: 'internalMemberFlag', + createdByParentFlag: 'createdByParentFlag', + mergedFlag: 'mergedFlag', + bundledFlag: 'bundledFlag', + serviceTicketId: 'serviceTicketId', + createdById: 'createdById', + updatedById: 'updatedById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketNoteScalarFieldEnum = (typeof ServiceTicketNoteScalarFieldEnum)[keyof typeof ServiceTicketNoteScalarFieldEnum] + + +export const ServiceTicketTypeScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketTypeScalarFieldEnum = (typeof ServiceTicketTypeScalarFieldEnum)[keyof typeof ServiceTicketTypeScalarFieldEnum] + + +export const ServiceTicketBoardScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + timeBillableFlag: 'timeBillableFlag', + expenseBillableFlag: 'expenseBillableFlag', + productBillableFlag: 'productBillableFlag', + timeInvoiceableFlag: 'timeInvoiceableFlag', + expenseInvoiceableFlag: 'expenseInvoiceableFlag', + productInvoiceableFlag: 'productInvoiceableFlag', + autoAssignNewFlag: 'autoAssignNewFlag', + autoAssignEmailCreatedFlag: 'autoAssignEmailCreatedFlag', + autoAssignPortalCreatedFlag: 'autoAssignPortalCreatedFlag', + projectFlag: 'projectFlag', + lockDescriptionFlag: 'lockDescriptionFlag', + emailContactFlag: 'emailContactFlag', + emailResourceFlag: 'emailResourceFlag', + resolutionSortOrder: 'resolutionSortOrder', + internalAnalysisSortOrder: 'internalAnalysisSortOrder', + locationId: 'locationId', + createdById: 'createdById', + updatedById: 'updatedById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketBoardScalarFieldEnum = (typeof ServiceTicketBoardScalarFieldEnum)[keyof typeof ServiceTicketBoardScalarFieldEnum] + + +export const ServiceTicketLocationScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketLocationScalarFieldEnum = (typeof ServiceTicketLocationScalarFieldEnum)[keyof typeof ServiceTicketLocationScalarFieldEnum] + + +export const ServiceTicketSourceScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketSourceScalarFieldEnum = (typeof ServiceTicketSourceScalarFieldEnum)[keyof typeof ServiceTicketSourceScalarFieldEnum] + + +export const ServiceTicketImpactScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketImpactScalarFieldEnum = (typeof ServiceTicketImpactScalarFieldEnum)[keyof typeof ServiceTicketImpactScalarFieldEnum] + + +export const ServiceTicketPriorityScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + color: 'color', + description: 'description', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketPriorityScalarFieldEnum = (typeof ServiceTicketPriorityScalarFieldEnum)[keyof typeof ServiceTicketPriorityScalarFieldEnum] + + +export const ServiceTicketSeverityScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketSeverityScalarFieldEnum = (typeof ServiceTicketSeverityScalarFieldEnum)[keyof typeof ServiceTicketSeverityScalarFieldEnum] + + +export const ServiceTicketFinalDataScalarFieldEnum = { + id: 'id' +} as const + +export type ServiceTicketFinalDataScalarFieldEnum = (typeof ServiceTicketFinalDataScalarFieldEnum)[keyof typeof ServiceTicketFinalDataScalarFieldEnum] + + +export const OpportunityStageScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + seqNbr: 'seqNbr', + funnelColor: 'funnelColor', + updatedById: 'updatedById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type OpportunityStageScalarFieldEnum = (typeof OpportunityStageScalarFieldEnum)[keyof typeof OpportunityStageScalarFieldEnum] + + +export const OpportunityTypeScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type OpportunityTypeScalarFieldEnum = (typeof OpportunityTypeScalarFieldEnum)[keyof typeof OpportunityTypeScalarFieldEnum] + + +export const OpportunityStatusScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + defaultFlag: 'defaultFlag', + wonFlag: 'wonFlag', + lostFlag: 'lostFlag', + closeFlag: 'closeFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type OpportunityStatusScalarFieldEnum = (typeof OpportunityStatusScalarFieldEnum)[keyof typeof OpportunityStatusScalarFieldEnum] + + export const OpportunityScalarFieldEnum = { id: 'id', - cwOpportunityId: 'cwOpportunityId', + uid: 'uid', name: 'name', notes: 'notes', - typeName: 'typeName', - typeCwId: 'typeCwId', - stageName: 'stageName', - stageCwId: 'stageCwId', - statusName: 'statusName', - statusCwId: 'statusCwId', - priorityName: 'priorityName', - priorityCwId: 'priorityCwId', - ratingName: 'ratingName', - ratingCwId: 'ratingCwId', - source: 'source', - campaignName: 'campaignName', - campaignCwId: 'campaignCwId', - primarySalesRepName: 'primarySalesRepName', - primarySalesRepIdentifier: 'primarySalesRepIdentifier', - primarySalesRepCwId: 'primarySalesRepCwId', - secondarySalesRepName: 'secondarySalesRepName', - secondarySalesRepIdentifier: 'secondarySalesRepIdentifier', - secondarySalesRepCwId: 'secondarySalesRepCwId', - companyCwId: 'companyCwId', - companyName: 'companyName', - contactCwId: 'contactCwId', - contactName: 'contactName', - siteCwId: 'siteCwId', - siteName: 'siteName', - customerPO: 'customerPO', - totalSalesTax: 'totalSalesTax', + oppNarrative: 'oppNarrative', + typeId: 'typeId', + stageId: 'stageId', + statusId: 'statusId', + taxCodeId: 'taxCodeId', + interest: 'interest', probability: 'probability', - locationName: 'locationName', - locationCwId: 'locationCwId', - departmentName: 'departmentName', - departmentCwId: 'departmentCwId', + source: 'source', + primarySalesRepId: 'primarySalesRepId', + secondarySalesRepId: 'secondarySalesRepId', + companyId: 'companyId', + contactId: 'contactId', + siteId: 'siteId', + customerPO: 'customerPO', + locationId: 'locationId', + departmentId: 'departmentId', expectedCloseDate: 'expectedCloseDate', pipelineChangeDate: 'pipelineChangeDate', dateBecameLead: 'dateBecameLead', closedDate: 'closedDate', closedFlag: 'closedFlag', - closedByName: 'closedByName', - closedByCwId: 'closedByCwId', - companyId: 'companyId', + closedById: 'closedById', productSequence: 'productSequence', - cwLastUpdated: 'cwLastUpdated', - cwDateEntered: 'cwDateEntered', + updatedBy: 'updatedBy', + eneteredBy: 'eneteredBy', createdAt: 'createdAt', updatedAt: 'updatedAt' } as const @@ -1495,6 +4510,87 @@ export const OpportunityScalarFieldEnum = { export type OpportunityScalarFieldEnum = (typeof OpportunityScalarFieldEnum)[keyof typeof OpportunityScalarFieldEnum] +export const ScheduleStatusScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + color: 'color', + softFlag: 'softFlag', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ScheduleStatusScalarFieldEnum = (typeof ScheduleStatusScalarFieldEnum)[keyof typeof ScheduleStatusScalarFieldEnum] + + +export const ScheduleTypeScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + displayColor: 'displayColor', + tableReference: 'tableReference', + moduleId: 'moduleId', + scheduleTypeId: 'scheduleTypeId', + systemFlag: 'systemFlag', + displayFlag: 'displayFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ScheduleTypeScalarFieldEnum = (typeof ScheduleTypeScalarFieldEnum)[keyof typeof ScheduleTypeScalarFieldEnum] + + +export const ScheduleSpanScalarFieldEnum = { + id: 'id', + scheduleSpanId: 'scheduleSpanId', + spanDesc: 'spanDesc' +} as const + +export type ScheduleSpanScalarFieldEnum = (typeof ScheduleSpanScalarFieldEnum)[keyof typeof ScheduleSpanScalarFieldEnum] + + +export const ScheduleScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + memberId: 'memberId', + closedFlag: 'closedFlag', + reminderFlag: 'reminderFlag', + allDayFlag: 'allDayFlag', + acknowledgementFlag: 'acknowledgementFlag', + meetingFlag: 'meetingFlag', + recurringFlag: 'recurringFlag', + billableFlag: 'billableFlag', + acknowledgedById: 'acknowledgedById', + acknowledgedAt: 'acknowledgedAt', + startDate: 'startDate', + endDate: 'endDate', + hoursScheduled: 'hoursScheduled', + duration: 'duration', + hoursPerDay: 'hoursPerDay', + reminderMinutes: 'reminderMinutes', + statusId: 'statusId', + typeId: 'typeId', + scheduleSpanId: 'scheduleSpanId', + updatedById: 'updatedById', + createdById: 'createdById', + closedById: 'closedById', + closedAt: 'closedAt', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ScheduleScalarFieldEnum = (typeof ScheduleScalarFieldEnum)[keyof typeof ScheduleScalarFieldEnum] + + export const CredentialTypeScalarFieldEnum = { id: 'id', name: 'name', @@ -1553,6 +4649,23 @@ export const GeneratedQuotesScalarFieldEnum = { export type GeneratedQuotesScalarFieldEnum = (typeof GeneratedQuotesScalarFieldEnum)[keyof typeof GeneratedQuotesScalarFieldEnum] +export const TaxCodeScalarFieldEnum = { + id: 'id', + uid: 'uid', + code: 'code', + codeCaption: 'codeCaption', + description: 'description', + rate: 'rate', + defaultFlag: 'defaultFlag', + createdBy: 'createdBy', + updatedBy: 'updatedBy', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type TaxCodeScalarFieldEnum = (typeof TaxCodeScalarFieldEnum)[keyof typeof TaxCodeScalarFieldEnum] + + export const CwMemberScalarFieldEnum = { id: 'id', cwMemberId: 'cwMemberId', @@ -1630,6 +4743,34 @@ export type ListStringFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaMod +/** + * Reference to a field of type 'SyncJobType' + */ +export type EnumSyncJobTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'SyncJobType'> + + + +/** + * Reference to a field of type 'SyncJobType[]' + */ +export type ListEnumSyncJobTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'SyncJobType[]'> + + + +/** + * Reference to a field of type 'SyncJobStatus' + */ +export type EnumSyncJobStatusFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'SyncJobStatus'> + + + +/** + * Reference to a field of type 'SyncJobStatus[]' + */ +export type ListEnumSyncJobStatusFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'SyncJobStatus[]'> + + + /** * Reference to a field of type 'DateTime' */ @@ -1644,13 +4785,6 @@ export type ListDateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaM -/** - * Reference to a field of type 'Boolean' - */ -export type BooleanFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Boolean'> - - - /** * Reference to a field of type 'Int' */ @@ -1665,6 +4799,83 @@ export type ListIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, +/** + * Reference to a field of type 'Json' + */ +export type JsonFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Json'> + + + +/** + * Reference to a field of type 'QueryMode' + */ +export type EnumQueryModeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'QueryMode'> + + + +/** + * Reference to a field of type 'Boolean' + */ +export type BooleanFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Boolean'> + + + +/** + * Reference to a field of type 'USState' + */ +export type EnumUSStateFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'USState'> + + + +/** + * Reference to a field of type 'USState[]' + */ +export type ListEnumUSStateFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'USState[]'> + + + +/** + * Reference to a field of type 'Country' + */ +export type EnumCountryFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Country'> + + + +/** + * Reference to a field of type 'Country[]' + */ +export type ListEnumCountryFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Country[]'> + + + +/** + * Reference to a field of type 'GenderType' + */ +export type EnumGenderTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'GenderType'> + + + +/** + * Reference to a field of type 'GenderType[]' + */ +export type ListEnumGenderTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'GenderType[]'> + + + +/** + * Reference to a field of type 'PhoneType' + */ +export type EnumPhoneTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'PhoneType'> + + + +/** + * Reference to a field of type 'PhoneType[]' + */ +export type ListEnumPhoneTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'PhoneType[]'> + + + /** * Reference to a field of type 'Float' */ @@ -1680,16 +4891,44 @@ export type ListFloatFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaMode /** - * Reference to a field of type 'Json' + * Reference to a field of type 'BillingMethod' */ -export type JsonFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Json'> +export type EnumBillingMethodFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'BillingMethod'> /** - * Reference to a field of type 'QueryMode' + * Reference to a field of type 'BillingMethod[]' */ -export type EnumQueryModeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'QueryMode'> +export type ListEnumBillingMethodFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'BillingMethod[]'> + + + +/** + * Reference to a field of type 'BillingType' + */ +export type EnumBillingTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'BillingType'> + + + +/** + * Reference to a field of type 'BillingType[]' + */ +export type ListEnumBillingTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'BillingType[]'> + + + +/** + * Reference to a field of type 'OpportunityInterest' + */ +export type EnumOpportunityInterestFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'OpportunityInterest'> + + + +/** + * Reference to a field of type 'OpportunityInterest[]' + */ +export type ListEnumOpportunityInterestFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'OpportunityInterest[]'> @@ -1801,17 +5040,50 @@ export type PrismaClientOptions = ({ comments?: runtime.SqlCommenterPlugin[] } export type GlobalOmitConfig = { + syncJobRun?: Prisma.SyncJobRunOmit + syncStepLog?: Prisma.SyncStepLogOmit session?: Prisma.SessionOmit user?: Prisma.UserOmit role?: Prisma.RoleOmit + corporateLocation?: Prisma.CorporateLocationOmit + internalDepartment?: Prisma.InternalDepartmentOmit unifiSite?: Prisma.UnifiSiteOmit company?: Prisma.CompanyOmit + companyAddress?: Prisma.CompanyAddressOmit + contact?: Prisma.ContactOmit + catalogItemType?: Prisma.CatalogItemTypeOmit + catalogCategory?: Prisma.CatalogCategoryOmit + catalogSubcategory?: Prisma.CatalogSubcategoryOmit + catalogManufacturer?: Prisma.CatalogManufacturerOmit + warehouseBin?: Prisma.WarehouseBinOmit + productInventory?: Prisma.ProductInventoryOmit + warehouse?: Prisma.WarehouseOmit + minimumStockByWarehouse?: Prisma.MinimumStockByWarehouseOmit catalogItem?: Prisma.CatalogItemOmit + productData?: Prisma.ProductDataOmit + serviceTicket?: Prisma.ServiceTicketOmit + serviceTicketNote?: Prisma.ServiceTicketNoteOmit + serviceTicketType?: Prisma.ServiceTicketTypeOmit + serviceTicketBoard?: Prisma.ServiceTicketBoardOmit + serviceTicketLocation?: Prisma.ServiceTicketLocationOmit + serviceTicketSource?: Prisma.ServiceTicketSourceOmit + serviceTicketImpact?: Prisma.ServiceTicketImpactOmit + serviceTicketPriority?: Prisma.ServiceTicketPriorityOmit + serviceTicketSeverity?: Prisma.ServiceTicketSeverityOmit + serviceTicketFinalData?: Prisma.ServiceTicketFinalDataOmit + opportunityStage?: Prisma.OpportunityStageOmit + opportunityType?: Prisma.OpportunityTypeOmit + opportunityStatus?: Prisma.OpportunityStatusOmit opportunity?: Prisma.OpportunityOmit + scheduleStatus?: Prisma.ScheduleStatusOmit + scheduleType?: Prisma.ScheduleTypeOmit + scheduleSpan?: Prisma.ScheduleSpanOmit + schedule?: Prisma.ScheduleOmit credentialType?: Prisma.CredentialTypeOmit secureValue?: Prisma.SecureValueOmit credential?: Prisma.CredentialOmit generatedQuotes?: Prisma.GeneratedQuotesOmit + taxCode?: Prisma.TaxCodeOmit cwMember?: Prisma.CwMemberOmit } diff --git a/api/generated/prisma/internal/prismaNamespaceBrowser.ts b/api/generated/prisma/internal/prismaNamespaceBrowser.ts index ed8e0ff..358bce1 100644 --- a/api/generated/prisma/internal/prismaNamespaceBrowser.ts +++ b/api/generated/prisma/internal/prismaNamespaceBrowser.ts @@ -51,17 +51,50 @@ export const AnyNull = runtime.AnyNull export const ModelName = { + SyncJobRun: 'SyncJobRun', + SyncStepLog: 'SyncStepLog', Session: 'Session', User: 'User', Role: 'Role', + CorporateLocation: 'CorporateLocation', + InternalDepartment: 'InternalDepartment', UnifiSite: 'UnifiSite', Company: 'Company', + CompanyAddress: 'CompanyAddress', + Contact: 'Contact', + CatalogItemType: 'CatalogItemType', + CatalogCategory: 'CatalogCategory', + CatalogSubcategory: 'CatalogSubcategory', + CatalogManufacturer: 'CatalogManufacturer', + WarehouseBin: 'WarehouseBin', + ProductInventory: 'ProductInventory', + Warehouse: 'Warehouse', + MinimumStockByWarehouse: 'MinimumStockByWarehouse', CatalogItem: 'CatalogItem', + ProductData: 'ProductData', + ServiceTicket: 'ServiceTicket', + ServiceTicketNote: 'ServiceTicketNote', + ServiceTicketType: 'ServiceTicketType', + ServiceTicketBoard: 'ServiceTicketBoard', + ServiceTicketLocation: 'ServiceTicketLocation', + ServiceTicketSource: 'ServiceTicketSource', + ServiceTicketImpact: 'ServiceTicketImpact', + ServiceTicketPriority: 'ServiceTicketPriority', + ServiceTicketSeverity: 'ServiceTicketSeverity', + ServiceTicketFinalData: 'ServiceTicketFinalData', + OpportunityStage: 'OpportunityStage', + OpportunityType: 'OpportunityType', + OpportunityStatus: 'OpportunityStatus', Opportunity: 'Opportunity', + ScheduleStatus: 'ScheduleStatus', + ScheduleType: 'ScheduleType', + ScheduleSpan: 'ScheduleSpan', + Schedule: 'Schedule', CredentialType: 'CredentialType', SecureValue: 'SecureValue', Credential: 'Credential', GeneratedQuotes: 'GeneratedQuotes', + TaxCode: 'TaxCode', CwMember: 'CwMember' } as const @@ -81,6 +114,39 @@ export const TransactionIsolationLevel = runtime.makeStrictEnum({ export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel] +export const SyncJobRunScalarFieldEnum = { + id: 'id', + jobType: 'jobType', + status: 'status', + triggeredBy: 'triggeredBy', + startedAt: 'startedAt', + completedAt: 'completedAt', + errorSummary: 'errorSummary', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type SyncJobRunScalarFieldEnum = (typeof SyncJobRunScalarFieldEnum)[keyof typeof SyncJobRunScalarFieldEnum] + + +export const SyncStepLogScalarFieldEnum = { + id: 'id', + syncJobRunId: 'syncJobRunId', + tableName: 'tableName', + syncMode: 'syncMode', + recordsProcessed: 'recordsProcessed', + recordsInserted: 'recordsInserted', + recordsSkipped: 'recordsSkipped', + recordsFailed: 'recordsFailed', + recordsDeleted: 'recordsDeleted', + sampleErrors: 'sampleErrors', + durationMs: 'durationMs', + createdAt: 'createdAt' +} as const + +export type SyncStepLogScalarFieldEnum = (typeof SyncStepLogScalarFieldEnum)[keyof typeof SyncStepLogScalarFieldEnum] + + export const SessionScalarFieldEnum = { id: 'id', sessionKey: 'sessionKey', @@ -98,13 +164,18 @@ export const UserScalarFieldEnum = { id: 'id', permissions: 'permissions', login: 'login', - name: 'name', + firstName: 'firstName', + lastName: 'lastName', email: 'email', - emailVerified: 'emailVerified', image: 'image', + title: 'title', + active: 'active', + hidden: 'hidden', cwIdentifier: 'cwIdentifier', + cwMemberId: 'cwMemberId', userId: 'userId', token: 'token', + updatedBy: 'updatedBy', createdAt: 'createdAt', updatedAt: 'updatedAt' } as const @@ -124,6 +195,40 @@ export const RoleScalarFieldEnum = { export type RoleScalarFieldEnum = (typeof RoleScalarFieldEnum)[keyof typeof RoleScalarFieldEnum] +export const CorporateLocationScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + updatedById: 'updatedById', + addressLine1: 'addressLine1', + addressLine2: 'addressLine2', + city: 'city', + state: 'state', + zipCode: 'zipCode', + country: 'country', + inactiveFlag: 'inactiveFlag', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CorporateLocationScalarFieldEnum = (typeof CorporateLocationScalarFieldEnum)[keyof typeof CorporateLocationScalarFieldEnum] + + +export const InternalDepartmentScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + createdById: 'createdById', + updatedById: 'updatedById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type InternalDepartmentScalarFieldEnum = (typeof InternalDepartmentScalarFieldEnum)[keyof typeof InternalDepartmentScalarFieldEnum] + + export const UnifiSiteScalarFieldEnum = { id: 'id', name: 'name', @@ -138,9 +243,17 @@ export type UnifiSiteScalarFieldEnum = (typeof UnifiSiteScalarFieldEnum)[keyof t export const CompanyScalarFieldEnum = { id: 'id', + uid: 'uid', name: 'name', - cw_CompanyId: 'cw_CompanyId', - cw_Identifier: 'cw_Identifier', + phone: 'phone', + website: 'website', + deleteFlag: 'deleteFlag', + dateDeleted: 'dateDeleted', + taxId: 'taxId', + taxExempt: 'taxExempt', + enteredById: 'enteredById', + deletedById: 'deletedById', + deletedAt: 'deletedAt', createdAt: 'createdAt', updatedAt: 'updatedAt' } as const @@ -148,20 +261,195 @@ export const CompanyScalarFieldEnum = { export type CompanyScalarFieldEnum = (typeof CompanyScalarFieldEnum)[keyof typeof CompanyScalarFieldEnum] +export const CompanyAddressScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + addressLine1: 'addressLine1', + addressLine2: 'addressLine2', + city: 'city', + state: 'state', + zipCode: 'zipCode', + country: 'country', + phone: 'phone', + fax: 'fax', + inactiveFlag: 'inactiveFlag', + defaultFlag: 'defaultFlag', + defaultMailFlag: 'defaultMailFlag', + defaultBillFlag: 'defaultBillFlag', + defaultShipFlag: 'defaultShipFlag', + updatedById: 'updatedById', + companyId: 'companyId', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CompanyAddressScalarFieldEnum = (typeof CompanyAddressScalarFieldEnum)[keyof typeof CompanyAddressScalarFieldEnum] + + +export const ContactScalarFieldEnum = { + id: 'id', + uid: 'uid', + active: 'active', + default: 'default', + firstName: 'firstName', + lastName: 'lastName', + nickname: 'nickname', + title: 'title', + gender: 'gender', + birthday: 'birthday', + email: 'email', + phone: 'phone', + phoneExtension: 'phoneExtension', + phoneType: 'phoneType', + companyAddressId: 'companyAddressId', + memberId: 'memberId', + companyId: 'companyId', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ContactScalarFieldEnum = (typeof ContactScalarFieldEnum)[keyof typeof ContactScalarFieldEnum] + + +export const CatalogItemTypeScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CatalogItemTypeScalarFieldEnum = (typeof CatalogItemTypeScalarFieldEnum)[keyof typeof CatalogItemTypeScalarFieldEnum] + + +export const CatalogCategoryScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CatalogCategoryScalarFieldEnum = (typeof CatalogCategoryScalarFieldEnum)[keyof typeof CatalogCategoryScalarFieldEnum] + + +export const CatalogSubcategoryScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + categoryId: 'categoryId', + inactiveFlag: 'inactiveFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CatalogSubcategoryScalarFieldEnum = (typeof CatalogSubcategoryScalarFieldEnum)[keyof typeof CatalogSubcategoryScalarFieldEnum] + + +export const CatalogManufacturerScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type CatalogManufacturerScalarFieldEnum = (typeof CatalogManufacturerScalarFieldEnum)[keyof typeof CatalogManufacturerScalarFieldEnum] + + +export const WarehouseBinScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + minQuantity: 'minQuantity', + maxQuantity: 'maxQuantity', + inactiveFlag: 'inactiveFlag', + defaultFlag: 'defaultFlag', + warehouseId: 'warehouseId', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type WarehouseBinScalarFieldEnum = (typeof WarehouseBinScalarFieldEnum)[keyof typeof WarehouseBinScalarFieldEnum] + + +export const ProductInventoryScalarFieldEnum = { + id: 'id', + uid: 'uid', + qtyOnHand: 'qtyOnHand', + warehouseBinId: 'warehouseBinId', + itemId: 'itemId', + warehouseId: 'warehouseId', + updatedById: 'updatedById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ProductInventoryScalarFieldEnum = (typeof ProductInventoryScalarFieldEnum)[keyof typeof ProductInventoryScalarFieldEnum] + + +export const WarehouseScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + lockedFlag: 'lockedFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type WarehouseScalarFieldEnum = (typeof WarehouseScalarFieldEnum)[keyof typeof WarehouseScalarFieldEnum] + + +export const MinimumStockByWarehouseScalarFieldEnum = { + id: 'id', + uid: 'uid', + minQty: 'minQty', + warehouseId: 'warehouseId', + itemId: 'itemId', + updatedById: 'updatedById', + enteredById: 'enteredById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type MinimumStockByWarehouseScalarFieldEnum = (typeof MinimumStockByWarehouseScalarFieldEnum)[keyof typeof MinimumStockByWarehouseScalarFieldEnum] + + export const CatalogItemScalarFieldEnum = { id: 'id', - cwCatalogId: 'cwCatalogId', + uid: 'uid', identifier: 'identifier', name: 'name', description: 'description', customerDescription: 'customerDescription', internalNotes: 'internalNotes', - category: 'category', - categoryCwId: 'categoryCwId', - subcategory: 'subcategory', - subcategoryCwId: 'subcategoryCwId', - manufacturer: 'manufacturer', - manufactureCwId: 'manufactureCwId', + subcategoryId: 'subcategoryId', + manufacturerId: 'manufacturerId', partNumber: 'partNumber', vendorName: 'vendorName', vendorSku: 'vendorSku', @@ -172,6 +460,7 @@ export const CatalogItemScalarFieldEnum = { salesTaxable: 'salesTaxable', onHand: 'onHand', cwLastUpdated: 'cwLastUpdated', + classId: 'classId', createdAt: 'createdAt', updatedAt: 'updatedAt' } as const @@ -179,54 +468,338 @@ export const CatalogItemScalarFieldEnum = { export type CatalogItemScalarFieldEnum = (typeof CatalogItemScalarFieldEnum)[keyof typeof CatalogItemScalarFieldEnum] +export const ProductDataScalarFieldEnum = { + id: 'id', + uid: 'uid', + qty: 'qty', + internalNote: 'internalNote', + shortDescription: 'shortDescription', + description: 'description', + sequenceNumber: 'sequenceNumber', + procurementNotes: 'procurementNotes', + productNarrative: 'productNarrative', + unitPrice: 'unitPrice', + unitCost: 'unitCost', + listPrice: 'listPrice', + discount: 'discount', + recurringRevenue: 'recurringRevenue', + recurringCost: 'recurringCost', + qtyPicked: 'qtyPicked', + qtyShipped: 'qtyShipped', + cancelReason: 'cancelReason', + cancelQty: 'cancelQty', + billableFlag: 'billableFlag', + taxableFlag: 'taxableFlag', + invoiceFlag: 'invoiceFlag', + recurringFlag: 'recurringFlag', + poApprovedFlag: 'poApprovedFlag', + calcPriceFlag: 'calcPriceFlag', + calcCostFlag: 'calcCostFlag', + cancelFlag: 'cancelFlag', + catalogItemId: 'catalogItemId', + corporateLocationId: 'corporateLocationId', + serviceTicketId: 'serviceTicketId', + opportunityId: 'opportunityId', + updatedById: 'updatedById', + createdById: 'createdById', + closedById: 'closedById', + cancelById: 'cancelById', + closedAt: 'closedAt', + cancelledAt: 'cancelledAt', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ProductDataScalarFieldEnum = (typeof ProductDataScalarFieldEnum)[keyof typeof ProductDataScalarFieldEnum] + + +export const ServiceTicketScalarFieldEnum = { + id: 'id', + uid: 'uid', + summary: 'summary', + addressLine1: 'addressLine1', + addressLine2: 'addressLine2', + city: 'city', + state: 'state', + zipCode: 'zipCode', + country: 'country', + contactName: 'contactName', + phone: 'phone', + phoneExtension: 'phoneExtension', + phoneType: 'phoneType', + email: 'email', + poNumber: 'poNumber', + billCompleteFlag: 'billCompleteFlag', + billUnapprovedFlag: 'billUnapprovedFlag', + billingAmount: 'billingAmount', + billingMethod: 'billingMethod', + timeBillableFlag: 'timeBillableFlag', + expenseBillableFlag: 'expenseBillableFlag', + productBillableFlag: 'productBillableFlag', + timeInvoiceableFlag: 'timeInvoiceableFlag', + expenseInvoiceableFlag: 'expenseInvoiceableFlag', + productInvoiceableFlag: 'productInvoiceableFlag', + dateRequested: 'dateRequested', + billingType: 'billingType', + billingInstructions: 'billingInstructions', + rejectedFlag: 'rejectedFlag', + closedFlag: 'closedFlag', + redFlag: 'redFlag', + publishFlag: 'publishFlag', + ticketOwnerId: 'ticketOwnerId', + serviceTicketBoardId: 'serviceTicketBoardId', + severityId: 'severityId', + impactId: 'impactId', + priorityId: 'priorityId', + sourceId: 'sourceId', + locationId: 'locationId', + parentId: 'parentId', + companyId: 'companyId', + contactId: 'contactId', + companyAddressId: 'companyAddressId', + billingCompanyId: 'billingCompanyId', + billingAddressId: 'billingAddressId', + createdById: 'createdById', + updatedById: 'updatedById', + closedById: 'closedById', + rejectedAt: 'rejectedAt', + closedAt: 'closedAt', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketScalarFieldEnum = (typeof ServiceTicketScalarFieldEnum)[keyof typeof ServiceTicketScalarFieldEnum] + + +export const ServiceTicketNoteScalarFieldEnum = { + id: 'id', + uid: 'uid', + notes: 'notes', + notesMd: 'notesMd', + authorId: 'authorId', + problemFlag: 'problemFlag', + resolutionFlag: 'resolutionFlag', + internalAnalysisFlag: 'internalAnalysisFlag', + internalMemberFlag: 'internalMemberFlag', + createdByParentFlag: 'createdByParentFlag', + mergedFlag: 'mergedFlag', + bundledFlag: 'bundledFlag', + serviceTicketId: 'serviceTicketId', + createdById: 'createdById', + updatedById: 'updatedById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketNoteScalarFieldEnum = (typeof ServiceTicketNoteScalarFieldEnum)[keyof typeof ServiceTicketNoteScalarFieldEnum] + + +export const ServiceTicketTypeScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketTypeScalarFieldEnum = (typeof ServiceTicketTypeScalarFieldEnum)[keyof typeof ServiceTicketTypeScalarFieldEnum] + + +export const ServiceTicketBoardScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + timeBillableFlag: 'timeBillableFlag', + expenseBillableFlag: 'expenseBillableFlag', + productBillableFlag: 'productBillableFlag', + timeInvoiceableFlag: 'timeInvoiceableFlag', + expenseInvoiceableFlag: 'expenseInvoiceableFlag', + productInvoiceableFlag: 'productInvoiceableFlag', + autoAssignNewFlag: 'autoAssignNewFlag', + autoAssignEmailCreatedFlag: 'autoAssignEmailCreatedFlag', + autoAssignPortalCreatedFlag: 'autoAssignPortalCreatedFlag', + projectFlag: 'projectFlag', + lockDescriptionFlag: 'lockDescriptionFlag', + emailContactFlag: 'emailContactFlag', + emailResourceFlag: 'emailResourceFlag', + resolutionSortOrder: 'resolutionSortOrder', + internalAnalysisSortOrder: 'internalAnalysisSortOrder', + locationId: 'locationId', + createdById: 'createdById', + updatedById: 'updatedById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketBoardScalarFieldEnum = (typeof ServiceTicketBoardScalarFieldEnum)[keyof typeof ServiceTicketBoardScalarFieldEnum] + + +export const ServiceTicketLocationScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketLocationScalarFieldEnum = (typeof ServiceTicketLocationScalarFieldEnum)[keyof typeof ServiceTicketLocationScalarFieldEnum] + + +export const ServiceTicketSourceScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketSourceScalarFieldEnum = (typeof ServiceTicketSourceScalarFieldEnum)[keyof typeof ServiceTicketSourceScalarFieldEnum] + + +export const ServiceTicketImpactScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketImpactScalarFieldEnum = (typeof ServiceTicketImpactScalarFieldEnum)[keyof typeof ServiceTicketImpactScalarFieldEnum] + + +export const ServiceTicketPriorityScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + color: 'color', + description: 'description', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketPriorityScalarFieldEnum = (typeof ServiceTicketPriorityScalarFieldEnum)[keyof typeof ServiceTicketPriorityScalarFieldEnum] + + +export const ServiceTicketSeverityScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ServiceTicketSeverityScalarFieldEnum = (typeof ServiceTicketSeverityScalarFieldEnum)[keyof typeof ServiceTicketSeverityScalarFieldEnum] + + +export const ServiceTicketFinalDataScalarFieldEnum = { + id: 'id' +} as const + +export type ServiceTicketFinalDataScalarFieldEnum = (typeof ServiceTicketFinalDataScalarFieldEnum)[keyof typeof ServiceTicketFinalDataScalarFieldEnum] + + +export const OpportunityStageScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + seqNbr: 'seqNbr', + funnelColor: 'funnelColor', + updatedById: 'updatedById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type OpportunityStageScalarFieldEnum = (typeof OpportunityStageScalarFieldEnum)[keyof typeof OpportunityStageScalarFieldEnum] + + +export const OpportunityTypeScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type OpportunityTypeScalarFieldEnum = (typeof OpportunityTypeScalarFieldEnum)[keyof typeof OpportunityTypeScalarFieldEnum] + + +export const OpportunityStatusScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + inactiveFlag: 'inactiveFlag', + defaultFlag: 'defaultFlag', + wonFlag: 'wonFlag', + lostFlag: 'lostFlag', + closeFlag: 'closeFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type OpportunityStatusScalarFieldEnum = (typeof OpportunityStatusScalarFieldEnum)[keyof typeof OpportunityStatusScalarFieldEnum] + + export const OpportunityScalarFieldEnum = { id: 'id', - cwOpportunityId: 'cwOpportunityId', + uid: 'uid', name: 'name', notes: 'notes', - typeName: 'typeName', - typeCwId: 'typeCwId', - stageName: 'stageName', - stageCwId: 'stageCwId', - statusName: 'statusName', - statusCwId: 'statusCwId', - priorityName: 'priorityName', - priorityCwId: 'priorityCwId', - ratingName: 'ratingName', - ratingCwId: 'ratingCwId', - source: 'source', - campaignName: 'campaignName', - campaignCwId: 'campaignCwId', - primarySalesRepName: 'primarySalesRepName', - primarySalesRepIdentifier: 'primarySalesRepIdentifier', - primarySalesRepCwId: 'primarySalesRepCwId', - secondarySalesRepName: 'secondarySalesRepName', - secondarySalesRepIdentifier: 'secondarySalesRepIdentifier', - secondarySalesRepCwId: 'secondarySalesRepCwId', - companyCwId: 'companyCwId', - companyName: 'companyName', - contactCwId: 'contactCwId', - contactName: 'contactName', - siteCwId: 'siteCwId', - siteName: 'siteName', - customerPO: 'customerPO', - totalSalesTax: 'totalSalesTax', + oppNarrative: 'oppNarrative', + typeId: 'typeId', + stageId: 'stageId', + statusId: 'statusId', + taxCodeId: 'taxCodeId', + interest: 'interest', probability: 'probability', - locationName: 'locationName', - locationCwId: 'locationCwId', - departmentName: 'departmentName', - departmentCwId: 'departmentCwId', + source: 'source', + primarySalesRepId: 'primarySalesRepId', + secondarySalesRepId: 'secondarySalesRepId', + companyId: 'companyId', + contactId: 'contactId', + siteId: 'siteId', + customerPO: 'customerPO', + locationId: 'locationId', + departmentId: 'departmentId', expectedCloseDate: 'expectedCloseDate', pipelineChangeDate: 'pipelineChangeDate', dateBecameLead: 'dateBecameLead', closedDate: 'closedDate', closedFlag: 'closedFlag', - closedByName: 'closedByName', - closedByCwId: 'closedByCwId', - companyId: 'companyId', + closedById: 'closedById', productSequence: 'productSequence', - cwLastUpdated: 'cwLastUpdated', - cwDateEntered: 'cwDateEntered', + updatedBy: 'updatedBy', + eneteredBy: 'eneteredBy', createdAt: 'createdAt', updatedAt: 'updatedAt' } as const @@ -234,6 +807,87 @@ export const OpportunityScalarFieldEnum = { export type OpportunityScalarFieldEnum = (typeof OpportunityScalarFieldEnum)[keyof typeof OpportunityScalarFieldEnum] +export const ScheduleStatusScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + color: 'color', + softFlag: 'softFlag', + defaultFlag: 'defaultFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ScheduleStatusScalarFieldEnum = (typeof ScheduleStatusScalarFieldEnum)[keyof typeof ScheduleStatusScalarFieldEnum] + + +export const ScheduleTypeScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + displayColor: 'displayColor', + tableReference: 'tableReference', + moduleId: 'moduleId', + scheduleTypeId: 'scheduleTypeId', + systemFlag: 'systemFlag', + displayFlag: 'displayFlag', + updatedById: 'updatedById', + createdById: 'createdById', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ScheduleTypeScalarFieldEnum = (typeof ScheduleTypeScalarFieldEnum)[keyof typeof ScheduleTypeScalarFieldEnum] + + +export const ScheduleSpanScalarFieldEnum = { + id: 'id', + scheduleSpanId: 'scheduleSpanId', + spanDesc: 'spanDesc' +} as const + +export type ScheduleSpanScalarFieldEnum = (typeof ScheduleSpanScalarFieldEnum)[keyof typeof ScheduleSpanScalarFieldEnum] + + +export const ScheduleScalarFieldEnum = { + id: 'id', + uid: 'uid', + name: 'name', + description: 'description', + memberId: 'memberId', + closedFlag: 'closedFlag', + reminderFlag: 'reminderFlag', + allDayFlag: 'allDayFlag', + acknowledgementFlag: 'acknowledgementFlag', + meetingFlag: 'meetingFlag', + recurringFlag: 'recurringFlag', + billableFlag: 'billableFlag', + acknowledgedById: 'acknowledgedById', + acknowledgedAt: 'acknowledgedAt', + startDate: 'startDate', + endDate: 'endDate', + hoursScheduled: 'hoursScheduled', + duration: 'duration', + hoursPerDay: 'hoursPerDay', + reminderMinutes: 'reminderMinutes', + statusId: 'statusId', + typeId: 'typeId', + scheduleSpanId: 'scheduleSpanId', + updatedById: 'updatedById', + createdById: 'createdById', + closedById: 'closedById', + closedAt: 'closedAt', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type ScheduleScalarFieldEnum = (typeof ScheduleScalarFieldEnum)[keyof typeof ScheduleScalarFieldEnum] + + export const CredentialTypeScalarFieldEnum = { id: 'id', name: 'name', @@ -292,6 +946,23 @@ export const GeneratedQuotesScalarFieldEnum = { export type GeneratedQuotesScalarFieldEnum = (typeof GeneratedQuotesScalarFieldEnum)[keyof typeof GeneratedQuotesScalarFieldEnum] +export const TaxCodeScalarFieldEnum = { + id: 'id', + uid: 'uid', + code: 'code', + codeCaption: 'codeCaption', + description: 'description', + rate: 'rate', + defaultFlag: 'defaultFlag', + createdBy: 'createdBy', + updatedBy: 'updatedBy', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type TaxCodeScalarFieldEnum = (typeof TaxCodeScalarFieldEnum)[keyof typeof TaxCodeScalarFieldEnum] + + export const CwMemberScalarFieldEnum = { id: 'id', cwMemberId: 'cwMemberId', diff --git a/api/generated/prisma/models.ts b/api/generated/prisma/models.ts index f71cab0..36f0935 100644 --- a/api/generated/prisma/models.ts +++ b/api/generated/prisma/models.ts @@ -8,16 +8,49 @@ * * 🟢 You can import this file directly. */ +export type * from './models/SyncJobRun.ts' +export type * from './models/SyncStepLog.ts' export type * from './models/Session.ts' export type * from './models/User.ts' export type * from './models/Role.ts' +export type * from './models/CorporateLocation.ts' +export type * from './models/InternalDepartment.ts' export type * from './models/UnifiSite.ts' export type * from './models/Company.ts' +export type * from './models/CompanyAddress.ts' +export type * from './models/Contact.ts' +export type * from './models/CatalogItemType.ts' +export type * from './models/CatalogCategory.ts' +export type * from './models/CatalogSubcategory.ts' +export type * from './models/CatalogManufacturer.ts' +export type * from './models/WarehouseBin.ts' +export type * from './models/ProductInventory.ts' +export type * from './models/Warehouse.ts' +export type * from './models/MinimumStockByWarehouse.ts' export type * from './models/CatalogItem.ts' +export type * from './models/ProductData.ts' +export type * from './models/ServiceTicket.ts' +export type * from './models/ServiceTicketNote.ts' +export type * from './models/ServiceTicketType.ts' +export type * from './models/ServiceTicketBoard.ts' +export type * from './models/ServiceTicketLocation.ts' +export type * from './models/ServiceTicketSource.ts' +export type * from './models/ServiceTicketImpact.ts' +export type * from './models/ServiceTicketPriority.ts' +export type * from './models/ServiceTicketSeverity.ts' +export type * from './models/ServiceTicketFinalData.ts' +export type * from './models/OpportunityStage.ts' +export type * from './models/OpportunityType.ts' +export type * from './models/OpportunityStatus.ts' export type * from './models/Opportunity.ts' +export type * from './models/ScheduleStatus.ts' +export type * from './models/ScheduleType.ts' +export type * from './models/ScheduleSpan.ts' +export type * from './models/Schedule.ts' export type * from './models/CredentialType.ts' export type * from './models/SecureValue.ts' export type * from './models/Credential.ts' export type * from './models/GeneratedQuotes.ts' +export type * from './models/TaxCode.ts' export type * from './models/CwMember.ts' export type * from './commonInputTypes.ts' \ No newline at end of file diff --git a/api/generated/prisma/models/CatalogItem.ts b/api/generated/prisma/models/CatalogItem.ts index d7cd619..bf0641e 100644 --- a/api/generated/prisma/models/CatalogItem.ts +++ b/api/generated/prisma/models/CatalogItem.ts @@ -27,10 +27,9 @@ export type AggregateCatalogItem = { } export type CatalogItemAvgAggregateOutputType = { - cwCatalogId: number | null - categoryCwId: number | null - subcategoryCwId: number | null - manufactureCwId: number | null + id: number | null + subcategoryId: number | null + manufacturerId: number | null vendorCwId: number | null price: number | null cost: number | null @@ -38,10 +37,9 @@ export type CatalogItemAvgAggregateOutputType = { } export type CatalogItemSumAggregateOutputType = { - cwCatalogId: number | null - categoryCwId: number | null - subcategoryCwId: number | null - manufactureCwId: number | null + id: number | null + subcategoryId: number | null + manufacturerId: number | null vendorCwId: number | null price: number | null cost: number | null @@ -49,19 +47,15 @@ export type CatalogItemSumAggregateOutputType = { } export type CatalogItemMinAggregateOutputType = { - id: string | null - cwCatalogId: number | null + id: number | null + uid: string | null identifier: string | null name: string | null description: string | null customerDescription: string | null internalNotes: string | null - category: string | null - categoryCwId: number | null - subcategory: string | null - subcategoryCwId: number | null - manufacturer: string | null - manufactureCwId: number | null + subcategoryId: number | null + manufacturerId: number | null partNumber: string | null vendorName: string | null vendorSku: string | null @@ -72,24 +66,21 @@ export type CatalogItemMinAggregateOutputType = { salesTaxable: boolean | null onHand: number | null cwLastUpdated: Date | null + classId: string | null createdAt: Date | null updatedAt: Date | null } export type CatalogItemMaxAggregateOutputType = { - id: string | null - cwCatalogId: number | null + id: number | null + uid: string | null identifier: string | null name: string | null description: string | null customerDescription: string | null internalNotes: string | null - category: string | null - categoryCwId: number | null - subcategory: string | null - subcategoryCwId: number | null - manufacturer: string | null - manufactureCwId: number | null + subcategoryId: number | null + manufacturerId: number | null partNumber: string | null vendorName: string | null vendorSku: string | null @@ -100,24 +91,21 @@ export type CatalogItemMaxAggregateOutputType = { salesTaxable: boolean | null onHand: number | null cwLastUpdated: Date | null + classId: string | null createdAt: Date | null updatedAt: Date | null } export type CatalogItemCountAggregateOutputType = { id: number - cwCatalogId: number + uid: number identifier: number name: number description: number customerDescription: number internalNotes: number - category: number - categoryCwId: number - subcategory: number - subcategoryCwId: number - manufacturer: number - manufactureCwId: number + subcategoryId: number + manufacturerId: number partNumber: number vendorName: number vendorSku: number @@ -128,6 +116,7 @@ export type CatalogItemCountAggregateOutputType = { salesTaxable: number onHand: number cwLastUpdated: number + classId: number createdAt: number updatedAt: number _all: number @@ -135,10 +124,9 @@ export type CatalogItemCountAggregateOutputType = { export type CatalogItemAvgAggregateInputType = { - cwCatalogId?: true - categoryCwId?: true - subcategoryCwId?: true - manufactureCwId?: true + id?: true + subcategoryId?: true + manufacturerId?: true vendorCwId?: true price?: true cost?: true @@ -146,10 +134,9 @@ export type CatalogItemAvgAggregateInputType = { } export type CatalogItemSumAggregateInputType = { - cwCatalogId?: true - categoryCwId?: true - subcategoryCwId?: true - manufactureCwId?: true + id?: true + subcategoryId?: true + manufacturerId?: true vendorCwId?: true price?: true cost?: true @@ -158,18 +145,14 @@ export type CatalogItemSumAggregateInputType = { export type CatalogItemMinAggregateInputType = { id?: true - cwCatalogId?: true + uid?: true identifier?: true name?: true description?: true customerDescription?: true internalNotes?: true - category?: true - categoryCwId?: true - subcategory?: true - subcategoryCwId?: true - manufacturer?: true - manufactureCwId?: true + subcategoryId?: true + manufacturerId?: true partNumber?: true vendorName?: true vendorSku?: true @@ -180,24 +163,21 @@ export type CatalogItemMinAggregateInputType = { salesTaxable?: true onHand?: true cwLastUpdated?: true + classId?: true createdAt?: true updatedAt?: true } export type CatalogItemMaxAggregateInputType = { id?: true - cwCatalogId?: true + uid?: true identifier?: true name?: true description?: true customerDescription?: true internalNotes?: true - category?: true - categoryCwId?: true - subcategory?: true - subcategoryCwId?: true - manufacturer?: true - manufactureCwId?: true + subcategoryId?: true + manufacturerId?: true partNumber?: true vendorName?: true vendorSku?: true @@ -208,24 +188,21 @@ export type CatalogItemMaxAggregateInputType = { salesTaxable?: true onHand?: true cwLastUpdated?: true + classId?: true createdAt?: true updatedAt?: true } export type CatalogItemCountAggregateInputType = { id?: true - cwCatalogId?: true + uid?: true identifier?: true name?: true description?: true customerDescription?: true internalNotes?: true - category?: true - categoryCwId?: true - subcategory?: true - subcategoryCwId?: true - manufacturer?: true - manufactureCwId?: true + subcategoryId?: true + manufacturerId?: true partNumber?: true vendorName?: true vendorSku?: true @@ -236,6 +213,7 @@ export type CatalogItemCountAggregateInputType = { salesTaxable?: true onHand?: true cwLastUpdated?: true + classId?: true createdAt?: true updatedAt?: true _all?: true @@ -328,19 +306,15 @@ export type CatalogItemGroupByArgs | string - cwCatalogId?: Prisma.IntFilter<"CatalogItem"> | number + id?: Prisma.IntFilter<"CatalogItem"> | number + uid?: Prisma.StringFilter<"CatalogItem"> | string identifier?: Prisma.StringNullableFilter<"CatalogItem"> | string | null name?: Prisma.StringFilter<"CatalogItem"> | string description?: Prisma.StringNullableFilter<"CatalogItem"> | string | null customerDescription?: Prisma.StringNullableFilter<"CatalogItem"> | string | null internalNotes?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - category?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - categoryCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null - subcategory?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - subcategoryCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null - manufacturer?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - manufactureCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null + subcategoryId?: Prisma.IntFilter<"CatalogItem"> | number + manufacturerId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null partNumber?: Prisma.StringNullableFilter<"CatalogItem"> | string | null vendorName?: Prisma.StringNullableFilter<"CatalogItem"> | string | null vendorSku?: Prisma.StringNullableFilter<"CatalogItem"> | string | null @@ -402,26 +373,28 @@ export type CatalogItemWhereInput = { salesTaxable?: Prisma.BoolFilter<"CatalogItem"> | boolean onHand?: Prisma.IntFilter<"CatalogItem"> | number cwLastUpdated?: Prisma.DateTimeNullableFilter<"CatalogItem"> | Date | string | null + classId?: Prisma.StringNullableFilter<"CatalogItem"> | string | null createdAt?: Prisma.DateTimeFilter<"CatalogItem"> | Date | string updatedAt?: Prisma.DateTimeFilter<"CatalogItem"> | Date | string linkedItems?: Prisma.CatalogItemListRelationFilter linkedTo?: Prisma.CatalogItemListRelationFilter + subcategory?: Prisma.XOR + manufacturer?: Prisma.XOR | null + inventory?: Prisma.ProductInventoryListRelationFilter + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseListRelationFilter + productDataRecords?: Prisma.ProductDataListRelationFilter } export type CatalogItemOrderByWithRelationInput = { id?: Prisma.SortOrder - cwCatalogId?: Prisma.SortOrder + uid?: Prisma.SortOrder identifier?: Prisma.SortOrderInput | Prisma.SortOrder name?: Prisma.SortOrder description?: Prisma.SortOrderInput | Prisma.SortOrder customerDescription?: Prisma.SortOrderInput | Prisma.SortOrder internalNotes?: Prisma.SortOrderInput | Prisma.SortOrder - category?: Prisma.SortOrderInput | Prisma.SortOrder - categoryCwId?: Prisma.SortOrderInput | Prisma.SortOrder - subcategory?: Prisma.SortOrderInput | Prisma.SortOrder - subcategoryCwId?: Prisma.SortOrderInput | Prisma.SortOrder - manufacturer?: Prisma.SortOrderInput | Prisma.SortOrder - manufactureCwId?: Prisma.SortOrderInput | Prisma.SortOrder + subcategoryId?: Prisma.SortOrder + manufacturerId?: Prisma.SortOrderInput | Prisma.SortOrder partNumber?: Prisma.SortOrderInput | Prisma.SortOrder vendorName?: Prisma.SortOrderInput | Prisma.SortOrder vendorSku?: Prisma.SortOrderInput | Prisma.SortOrder @@ -432,15 +405,21 @@ export type CatalogItemOrderByWithRelationInput = { salesTaxable?: Prisma.SortOrder onHand?: Prisma.SortOrder cwLastUpdated?: Prisma.SortOrderInput | Prisma.SortOrder + classId?: Prisma.SortOrderInput | Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder linkedItems?: Prisma.CatalogItemOrderByRelationAggregateInput linkedTo?: Prisma.CatalogItemOrderByRelationAggregateInput + subcategory?: Prisma.CatalogSubcategoryOrderByWithRelationInput + manufacturer?: Prisma.CatalogManufacturerOrderByWithRelationInput + inventory?: Prisma.ProductInventoryOrderByRelationAggregateInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseOrderByRelationAggregateInput + productDataRecords?: Prisma.ProductDataOrderByRelationAggregateInput } export type CatalogItemWhereUniqueInput = Prisma.AtLeast<{ - id?: string - cwCatalogId?: number + id?: number + uid?: string identifier?: string AND?: Prisma.CatalogItemWhereInput | Prisma.CatalogItemWhereInput[] OR?: Prisma.CatalogItemWhereInput[] @@ -449,12 +428,8 @@ export type CatalogItemWhereUniqueInput = Prisma.AtLeast<{ description?: Prisma.StringNullableFilter<"CatalogItem"> | string | null customerDescription?: Prisma.StringNullableFilter<"CatalogItem"> | string | null internalNotes?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - category?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - categoryCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null - subcategory?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - subcategoryCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null - manufacturer?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - manufactureCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null + subcategoryId?: Prisma.IntFilter<"CatalogItem"> | number + manufacturerId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null partNumber?: Prisma.StringNullableFilter<"CatalogItem"> | string | null vendorName?: Prisma.StringNullableFilter<"CatalogItem"> | string | null vendorSku?: Prisma.StringNullableFilter<"CatalogItem"> | string | null @@ -465,26 +440,28 @@ export type CatalogItemWhereUniqueInput = Prisma.AtLeast<{ salesTaxable?: Prisma.BoolFilter<"CatalogItem"> | boolean onHand?: Prisma.IntFilter<"CatalogItem"> | number cwLastUpdated?: Prisma.DateTimeNullableFilter<"CatalogItem"> | Date | string | null + classId?: Prisma.StringNullableFilter<"CatalogItem"> | string | null createdAt?: Prisma.DateTimeFilter<"CatalogItem"> | Date | string updatedAt?: Prisma.DateTimeFilter<"CatalogItem"> | Date | string linkedItems?: Prisma.CatalogItemListRelationFilter linkedTo?: Prisma.CatalogItemListRelationFilter -}, "id" | "cwCatalogId" | "identifier"> + subcategory?: Prisma.XOR + manufacturer?: Prisma.XOR | null + inventory?: Prisma.ProductInventoryListRelationFilter + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseListRelationFilter + productDataRecords?: Prisma.ProductDataListRelationFilter +}, "uid" | "id" | "identifier"> export type CatalogItemOrderByWithAggregationInput = { id?: Prisma.SortOrder - cwCatalogId?: Prisma.SortOrder + uid?: Prisma.SortOrder identifier?: Prisma.SortOrderInput | Prisma.SortOrder name?: Prisma.SortOrder description?: Prisma.SortOrderInput | Prisma.SortOrder customerDescription?: Prisma.SortOrderInput | Prisma.SortOrder internalNotes?: Prisma.SortOrderInput | Prisma.SortOrder - category?: Prisma.SortOrderInput | Prisma.SortOrder - categoryCwId?: Prisma.SortOrderInput | Prisma.SortOrder - subcategory?: Prisma.SortOrderInput | Prisma.SortOrder - subcategoryCwId?: Prisma.SortOrderInput | Prisma.SortOrder - manufacturer?: Prisma.SortOrderInput | Prisma.SortOrder - manufactureCwId?: Prisma.SortOrderInput | Prisma.SortOrder + subcategoryId?: Prisma.SortOrder + manufacturerId?: Prisma.SortOrderInput | Prisma.SortOrder partNumber?: Prisma.SortOrderInput | Prisma.SortOrder vendorName?: Prisma.SortOrderInput | Prisma.SortOrder vendorSku?: Prisma.SortOrderInput | Prisma.SortOrder @@ -495,6 +472,7 @@ export type CatalogItemOrderByWithAggregationInput = { salesTaxable?: Prisma.SortOrder onHand?: Prisma.SortOrder cwLastUpdated?: Prisma.SortOrderInput | Prisma.SortOrder + classId?: Prisma.SortOrderInput | Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder _count?: Prisma.CatalogItemCountOrderByAggregateInput @@ -508,19 +486,15 @@ export type CatalogItemScalarWhereWithAggregatesInput = { AND?: Prisma.CatalogItemScalarWhereWithAggregatesInput | Prisma.CatalogItemScalarWhereWithAggregatesInput[] OR?: Prisma.CatalogItemScalarWhereWithAggregatesInput[] NOT?: Prisma.CatalogItemScalarWhereWithAggregatesInput | Prisma.CatalogItemScalarWhereWithAggregatesInput[] - id?: Prisma.StringWithAggregatesFilter<"CatalogItem"> | string - cwCatalogId?: Prisma.IntWithAggregatesFilter<"CatalogItem"> | number + id?: Prisma.IntWithAggregatesFilter<"CatalogItem"> | number + uid?: Prisma.StringWithAggregatesFilter<"CatalogItem"> | string identifier?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null name?: Prisma.StringWithAggregatesFilter<"CatalogItem"> | string description?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null customerDescription?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null internalNotes?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null - category?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null - categoryCwId?: Prisma.IntNullableWithAggregatesFilter<"CatalogItem"> | number | null - subcategory?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null - subcategoryCwId?: Prisma.IntNullableWithAggregatesFilter<"CatalogItem"> | number | null - manufacturer?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null - manufactureCwId?: Prisma.IntNullableWithAggregatesFilter<"CatalogItem"> | number | null + subcategoryId?: Prisma.IntWithAggregatesFilter<"CatalogItem"> | number + manufacturerId?: Prisma.IntNullableWithAggregatesFilter<"CatalogItem"> | number | null partNumber?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null vendorName?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null vendorSku?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null @@ -531,24 +505,19 @@ export type CatalogItemScalarWhereWithAggregatesInput = { salesTaxable?: Prisma.BoolWithAggregatesFilter<"CatalogItem"> | boolean onHand?: Prisma.IntWithAggregatesFilter<"CatalogItem"> | number cwLastUpdated?: Prisma.DateTimeNullableWithAggregatesFilter<"CatalogItem"> | Date | string | null + classId?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null createdAt?: Prisma.DateTimeWithAggregatesFilter<"CatalogItem"> | Date | string updatedAt?: Prisma.DateTimeWithAggregatesFilter<"CatalogItem"> | Date | string } export type CatalogItemCreateInput = { - id?: string - cwCatalogId: number + id: number + uid?: string identifier?: string | null name: string description?: string | null customerDescription?: string | null internalNotes?: string | null - category?: string | null - categoryCwId?: number | null - subcategory?: string | null - subcategoryCwId?: number | null - manufacturer?: string | null - manufactureCwId?: number | null partNumber?: string | null vendorName?: string | null vendorSku?: string | null @@ -559,26 +528,28 @@ export type CatalogItemCreateInput = { salesTaxable?: boolean onHand?: number cwLastUpdated?: Date | string | null + classId?: string | null createdAt?: Date | string updatedAt?: Date | string linkedItems?: Prisma.CatalogItemCreateNestedManyWithoutLinkedToInput linkedTo?: Prisma.CatalogItemCreateNestedManyWithoutLinkedItemsInput + subcategory: Prisma.CatalogSubcategoryCreateNestedOneWithoutItemsInput + manufacturer?: Prisma.CatalogManufacturerCreateNestedOneWithoutItemsInput + inventory?: Prisma.ProductInventoryCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataCreateNestedManyWithoutCatalogItemInput } export type CatalogItemUncheckedCreateInput = { - id?: string - cwCatalogId: number + id: number + uid?: string identifier?: string | null name: string description?: string | null customerDescription?: string | null internalNotes?: string | null - category?: string | null - categoryCwId?: number | null - subcategory?: string | null - subcategoryCwId?: number | null - manufacturer?: string | null - manufactureCwId?: number | null + subcategoryId: number + manufacturerId?: number | null partNumber?: string | null vendorName?: string | null vendorSku?: string | null @@ -589,26 +560,24 @@ export type CatalogItemUncheckedCreateInput = { salesTaxable?: boolean onHand?: number cwLastUpdated?: Date | string | null + classId?: string | null createdAt?: Date | string updatedAt?: Date | string linkedItems?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedToInput linkedTo?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedItemsInput + inventory?: Prisma.ProductInventoryUncheckedCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataUncheckedCreateNestedManyWithoutCatalogItemInput } export type CatalogItemUpdateInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - categoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - subcategory?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - subcategoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - manufacturer?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - manufactureCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -619,26 +588,28 @@ export type CatalogItemUpdateInput = { salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean onHand?: Prisma.IntFieldUpdateOperationsInput | number cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string linkedItems?: Prisma.CatalogItemUpdateManyWithoutLinkedToNestedInput linkedTo?: Prisma.CatalogItemUpdateManyWithoutLinkedItemsNestedInput + subcategory?: Prisma.CatalogSubcategoryUpdateOneRequiredWithoutItemsNestedInput + manufacturer?: Prisma.CatalogManufacturerUpdateOneWithoutItemsNestedInput + inventory?: Prisma.ProductInventoryUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUpdateManyWithoutCatalogItemNestedInput } export type CatalogItemUncheckedUpdateInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - categoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - subcategory?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - subcategoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - manufacturer?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - manufactureCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -649,26 +620,26 @@ export type CatalogItemUncheckedUpdateInput = { salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean onHand?: Prisma.IntFieldUpdateOperationsInput | number cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string linkedItems?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedToNestedInput linkedTo?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedItemsNestedInput + inventory?: Prisma.ProductInventoryUncheckedUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUncheckedUpdateManyWithoutCatalogItemNestedInput } export type CatalogItemCreateManyInput = { - id?: string - cwCatalogId: number + id: number + uid?: string identifier?: string | null name: string description?: string | null customerDescription?: string | null internalNotes?: string | null - category?: string | null - categoryCwId?: number | null - subcategory?: string | null - subcategoryCwId?: number | null - manufacturer?: string | null - manufactureCwId?: number | null + subcategoryId: number + manufacturerId?: number | null partNumber?: string | null vendorName?: string | null vendorSku?: string | null @@ -679,24 +650,19 @@ export type CatalogItemCreateManyInput = { salesTaxable?: boolean onHand?: number cwLastUpdated?: Date | string | null + classId?: string | null createdAt?: Date | string updatedAt?: Date | string } export type CatalogItemUpdateManyMutationInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - categoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - subcategory?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - subcategoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - manufacturer?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - manufactureCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -707,24 +673,21 @@ export type CatalogItemUpdateManyMutationInput = { salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean onHand?: Prisma.IntFieldUpdateOperationsInput | number cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } export type CatalogItemUncheckedUpdateManyInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - categoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - subcategory?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - subcategoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - manufacturer?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - manufactureCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -735,6 +698,7 @@ export type CatalogItemUncheckedUpdateManyInput = { salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean onHand?: Prisma.IntFieldUpdateOperationsInput | number cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } @@ -749,20 +713,21 @@ export type CatalogItemOrderByRelationAggregateInput = { _count?: Prisma.SortOrder } +export type CatalogItemNullableScalarRelationFilter = { + is?: Prisma.CatalogItemWhereInput | null + isNot?: Prisma.CatalogItemWhereInput | null +} + export type CatalogItemCountOrderByAggregateInput = { id?: Prisma.SortOrder - cwCatalogId?: Prisma.SortOrder + uid?: Prisma.SortOrder identifier?: Prisma.SortOrder name?: Prisma.SortOrder description?: Prisma.SortOrder customerDescription?: Prisma.SortOrder internalNotes?: Prisma.SortOrder - category?: Prisma.SortOrder - categoryCwId?: Prisma.SortOrder - subcategory?: Prisma.SortOrder - subcategoryCwId?: Prisma.SortOrder - manufacturer?: Prisma.SortOrder - manufactureCwId?: Prisma.SortOrder + subcategoryId?: Prisma.SortOrder + manufacturerId?: Prisma.SortOrder partNumber?: Prisma.SortOrder vendorName?: Prisma.SortOrder vendorSku?: Prisma.SortOrder @@ -773,15 +738,15 @@ export type CatalogItemCountOrderByAggregateInput = { salesTaxable?: Prisma.SortOrder onHand?: Prisma.SortOrder cwLastUpdated?: Prisma.SortOrder + classId?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } export type CatalogItemAvgOrderByAggregateInput = { - cwCatalogId?: Prisma.SortOrder - categoryCwId?: Prisma.SortOrder - subcategoryCwId?: Prisma.SortOrder - manufactureCwId?: Prisma.SortOrder + id?: Prisma.SortOrder + subcategoryId?: Prisma.SortOrder + manufacturerId?: Prisma.SortOrder vendorCwId?: Prisma.SortOrder price?: Prisma.SortOrder cost?: Prisma.SortOrder @@ -790,18 +755,14 @@ export type CatalogItemAvgOrderByAggregateInput = { export type CatalogItemMaxOrderByAggregateInput = { id?: Prisma.SortOrder - cwCatalogId?: Prisma.SortOrder + uid?: Prisma.SortOrder identifier?: Prisma.SortOrder name?: Prisma.SortOrder description?: Prisma.SortOrder customerDescription?: Prisma.SortOrder internalNotes?: Prisma.SortOrder - category?: Prisma.SortOrder - categoryCwId?: Prisma.SortOrder - subcategory?: Prisma.SortOrder - subcategoryCwId?: Prisma.SortOrder - manufacturer?: Prisma.SortOrder - manufactureCwId?: Prisma.SortOrder + subcategoryId?: Prisma.SortOrder + manufacturerId?: Prisma.SortOrder partNumber?: Prisma.SortOrder vendorName?: Prisma.SortOrder vendorSku?: Prisma.SortOrder @@ -812,24 +773,21 @@ export type CatalogItemMaxOrderByAggregateInput = { salesTaxable?: Prisma.SortOrder onHand?: Prisma.SortOrder cwLastUpdated?: Prisma.SortOrder + classId?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } export type CatalogItemMinOrderByAggregateInput = { id?: Prisma.SortOrder - cwCatalogId?: Prisma.SortOrder + uid?: Prisma.SortOrder identifier?: Prisma.SortOrder name?: Prisma.SortOrder description?: Prisma.SortOrder customerDescription?: Prisma.SortOrder internalNotes?: Prisma.SortOrder - category?: Prisma.SortOrder - categoryCwId?: Prisma.SortOrder - subcategory?: Prisma.SortOrder - subcategoryCwId?: Prisma.SortOrder - manufacturer?: Prisma.SortOrder - manufactureCwId?: Prisma.SortOrder + subcategoryId?: Prisma.SortOrder + manufacturerId?: Prisma.SortOrder partNumber?: Prisma.SortOrder vendorName?: Prisma.SortOrder vendorSku?: Prisma.SortOrder @@ -840,21 +798,142 @@ export type CatalogItemMinOrderByAggregateInput = { salesTaxable?: Prisma.SortOrder onHand?: Prisma.SortOrder cwLastUpdated?: Prisma.SortOrder + classId?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } export type CatalogItemSumOrderByAggregateInput = { - cwCatalogId?: Prisma.SortOrder - categoryCwId?: Prisma.SortOrder - subcategoryCwId?: Prisma.SortOrder - manufactureCwId?: Prisma.SortOrder + id?: Prisma.SortOrder + subcategoryId?: Prisma.SortOrder + manufacturerId?: Prisma.SortOrder vendorCwId?: Prisma.SortOrder price?: Prisma.SortOrder cost?: Prisma.SortOrder onHand?: Prisma.SortOrder } +export type CatalogItemScalarRelationFilter = { + is?: Prisma.CatalogItemWhereInput + isNot?: Prisma.CatalogItemWhereInput +} + +export type CatalogItemCreateNestedManyWithoutSubcategoryInput = { + create?: Prisma.XOR | Prisma.CatalogItemCreateWithoutSubcategoryInput[] | Prisma.CatalogItemUncheckedCreateWithoutSubcategoryInput[] + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutSubcategoryInput | Prisma.CatalogItemCreateOrConnectWithoutSubcategoryInput[] + createMany?: Prisma.CatalogItemCreateManySubcategoryInputEnvelope + connect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] +} + +export type CatalogItemUncheckedCreateNestedManyWithoutSubcategoryInput = { + create?: Prisma.XOR | Prisma.CatalogItemCreateWithoutSubcategoryInput[] | Prisma.CatalogItemUncheckedCreateWithoutSubcategoryInput[] + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutSubcategoryInput | Prisma.CatalogItemCreateOrConnectWithoutSubcategoryInput[] + createMany?: Prisma.CatalogItemCreateManySubcategoryInputEnvelope + connect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] +} + +export type CatalogItemUpdateManyWithoutSubcategoryNestedInput = { + create?: Prisma.XOR | Prisma.CatalogItemCreateWithoutSubcategoryInput[] | Prisma.CatalogItemUncheckedCreateWithoutSubcategoryInput[] + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutSubcategoryInput | Prisma.CatalogItemCreateOrConnectWithoutSubcategoryInput[] + upsert?: Prisma.CatalogItemUpsertWithWhereUniqueWithoutSubcategoryInput | Prisma.CatalogItemUpsertWithWhereUniqueWithoutSubcategoryInput[] + createMany?: Prisma.CatalogItemCreateManySubcategoryInputEnvelope + set?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + disconnect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + delete?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + connect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + update?: Prisma.CatalogItemUpdateWithWhereUniqueWithoutSubcategoryInput | Prisma.CatalogItemUpdateWithWhereUniqueWithoutSubcategoryInput[] + updateMany?: Prisma.CatalogItemUpdateManyWithWhereWithoutSubcategoryInput | Prisma.CatalogItemUpdateManyWithWhereWithoutSubcategoryInput[] + deleteMany?: Prisma.CatalogItemScalarWhereInput | Prisma.CatalogItemScalarWhereInput[] +} + +export type CatalogItemUncheckedUpdateManyWithoutSubcategoryNestedInput = { + create?: Prisma.XOR | Prisma.CatalogItemCreateWithoutSubcategoryInput[] | Prisma.CatalogItemUncheckedCreateWithoutSubcategoryInput[] + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutSubcategoryInput | Prisma.CatalogItemCreateOrConnectWithoutSubcategoryInput[] + upsert?: Prisma.CatalogItemUpsertWithWhereUniqueWithoutSubcategoryInput | Prisma.CatalogItemUpsertWithWhereUniqueWithoutSubcategoryInput[] + createMany?: Prisma.CatalogItemCreateManySubcategoryInputEnvelope + set?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + disconnect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + delete?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + connect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + update?: Prisma.CatalogItemUpdateWithWhereUniqueWithoutSubcategoryInput | Prisma.CatalogItemUpdateWithWhereUniqueWithoutSubcategoryInput[] + updateMany?: Prisma.CatalogItemUpdateManyWithWhereWithoutSubcategoryInput | Prisma.CatalogItemUpdateManyWithWhereWithoutSubcategoryInput[] + deleteMany?: Prisma.CatalogItemScalarWhereInput | Prisma.CatalogItemScalarWhereInput[] +} + +export type CatalogItemCreateNestedManyWithoutManufacturerInput = { + create?: Prisma.XOR | Prisma.CatalogItemCreateWithoutManufacturerInput[] | Prisma.CatalogItemUncheckedCreateWithoutManufacturerInput[] + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutManufacturerInput | Prisma.CatalogItemCreateOrConnectWithoutManufacturerInput[] + createMany?: Prisma.CatalogItemCreateManyManufacturerInputEnvelope + connect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] +} + +export type CatalogItemUncheckedCreateNestedManyWithoutManufacturerInput = { + create?: Prisma.XOR | Prisma.CatalogItemCreateWithoutManufacturerInput[] | Prisma.CatalogItemUncheckedCreateWithoutManufacturerInput[] + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutManufacturerInput | Prisma.CatalogItemCreateOrConnectWithoutManufacturerInput[] + createMany?: Prisma.CatalogItemCreateManyManufacturerInputEnvelope + connect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] +} + +export type CatalogItemUpdateManyWithoutManufacturerNestedInput = { + create?: Prisma.XOR | Prisma.CatalogItemCreateWithoutManufacturerInput[] | Prisma.CatalogItemUncheckedCreateWithoutManufacturerInput[] + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutManufacturerInput | Prisma.CatalogItemCreateOrConnectWithoutManufacturerInput[] + upsert?: Prisma.CatalogItemUpsertWithWhereUniqueWithoutManufacturerInput | Prisma.CatalogItemUpsertWithWhereUniqueWithoutManufacturerInput[] + createMany?: Prisma.CatalogItemCreateManyManufacturerInputEnvelope + set?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + disconnect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + delete?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + connect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + update?: Prisma.CatalogItemUpdateWithWhereUniqueWithoutManufacturerInput | Prisma.CatalogItemUpdateWithWhereUniqueWithoutManufacturerInput[] + updateMany?: Prisma.CatalogItemUpdateManyWithWhereWithoutManufacturerInput | Prisma.CatalogItemUpdateManyWithWhereWithoutManufacturerInput[] + deleteMany?: Prisma.CatalogItemScalarWhereInput | Prisma.CatalogItemScalarWhereInput[] +} + +export type CatalogItemUncheckedUpdateManyWithoutManufacturerNestedInput = { + create?: Prisma.XOR | Prisma.CatalogItemCreateWithoutManufacturerInput[] | Prisma.CatalogItemUncheckedCreateWithoutManufacturerInput[] + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutManufacturerInput | Prisma.CatalogItemCreateOrConnectWithoutManufacturerInput[] + upsert?: Prisma.CatalogItemUpsertWithWhereUniqueWithoutManufacturerInput | Prisma.CatalogItemUpsertWithWhereUniqueWithoutManufacturerInput[] + createMany?: Prisma.CatalogItemCreateManyManufacturerInputEnvelope + set?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + disconnect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + delete?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + connect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] + update?: Prisma.CatalogItemUpdateWithWhereUniqueWithoutManufacturerInput | Prisma.CatalogItemUpdateWithWhereUniqueWithoutManufacturerInput[] + updateMany?: Prisma.CatalogItemUpdateManyWithWhereWithoutManufacturerInput | Prisma.CatalogItemUpdateManyWithWhereWithoutManufacturerInput[] + deleteMany?: Prisma.CatalogItemScalarWhereInput | Prisma.CatalogItemScalarWhereInput[] +} + +export type CatalogItemCreateNestedOneWithoutInventoryInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutInventoryInput + connect?: Prisma.CatalogItemWhereUniqueInput +} + +export type CatalogItemUpdateOneWithoutInventoryNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutInventoryInput + upsert?: Prisma.CatalogItemUpsertWithoutInventoryInput + disconnect?: Prisma.CatalogItemWhereInput | boolean + delete?: Prisma.CatalogItemWhereInput | boolean + connect?: Prisma.CatalogItemWhereUniqueInput + update?: Prisma.XOR, Prisma.CatalogItemUncheckedUpdateWithoutInventoryInput> +} + +export type CatalogItemCreateNestedOneWithoutMinimumStockByWarehousesInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutMinimumStockByWarehousesInput + connect?: Prisma.CatalogItemWhereUniqueInput +} + +export type CatalogItemUpdateOneWithoutMinimumStockByWarehousesNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutMinimumStockByWarehousesInput + upsert?: Prisma.CatalogItemUpsertWithoutMinimumStockByWarehousesInput + disconnect?: Prisma.CatalogItemWhereInput | boolean + delete?: Prisma.CatalogItemWhereInput | boolean + connect?: Prisma.CatalogItemWhereUniqueInput + update?: Prisma.XOR, Prisma.CatalogItemUncheckedUpdateWithoutMinimumStockByWarehousesInput> +} + export type CatalogItemCreateNestedManyWithoutLinkedToInput = { create?: Prisma.XOR | Prisma.CatalogItemCreateWithoutLinkedToInput[] | Prisma.CatalogItemUncheckedCreateWithoutLinkedToInput[] connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutLinkedToInput | Prisma.CatalogItemCreateOrConnectWithoutLinkedToInput[] @@ -879,14 +958,6 @@ export type CatalogItemUncheckedCreateNestedManyWithoutLinkedItemsInput = { connect?: Prisma.CatalogItemWhereUniqueInput | Prisma.CatalogItemWhereUniqueInput[] } -export type NullableIntFieldUpdateOperationsInput = { - set?: number | null - increment?: number - decrement?: number - multiply?: number - divide?: number -} - export type FloatFieldUpdateOperationsInput = { set?: number increment?: number @@ -947,20 +1018,28 @@ export type CatalogItemUncheckedUpdateManyWithoutLinkedItemsNestedInput = { deleteMany?: Prisma.CatalogItemScalarWhereInput | Prisma.CatalogItemScalarWhereInput[] } -export type CatalogItemCreateWithoutLinkedToInput = { - id?: string - cwCatalogId: number +export type CatalogItemCreateNestedOneWithoutProductDataRecordsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutProductDataRecordsInput + connect?: Prisma.CatalogItemWhereUniqueInput +} + +export type CatalogItemUpdateOneRequiredWithoutProductDataRecordsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CatalogItemCreateOrConnectWithoutProductDataRecordsInput + upsert?: Prisma.CatalogItemUpsertWithoutProductDataRecordsInput + connect?: Prisma.CatalogItemWhereUniqueInput + update?: Prisma.XOR, Prisma.CatalogItemUncheckedUpdateWithoutProductDataRecordsInput> +} + +export type CatalogItemCreateWithoutSubcategoryInput = { + id: number + uid?: string identifier?: string | null name: string description?: string | null customerDescription?: string | null internalNotes?: string | null - category?: string | null - categoryCwId?: number | null - subcategory?: string | null - subcategoryCwId?: number | null - manufacturer?: string | null - manufactureCwId?: number | null partNumber?: string | null vendorName?: string | null vendorSku?: string | null @@ -971,25 +1050,26 @@ export type CatalogItemCreateWithoutLinkedToInput = { salesTaxable?: boolean onHand?: number cwLastUpdated?: Date | string | null + classId?: string | null createdAt?: Date | string updatedAt?: Date | string linkedItems?: Prisma.CatalogItemCreateNestedManyWithoutLinkedToInput + linkedTo?: Prisma.CatalogItemCreateNestedManyWithoutLinkedItemsInput + manufacturer?: Prisma.CatalogManufacturerCreateNestedOneWithoutItemsInput + inventory?: Prisma.ProductInventoryCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataCreateNestedManyWithoutCatalogItemInput } -export type CatalogItemUncheckedCreateWithoutLinkedToInput = { - id?: string - cwCatalogId: number +export type CatalogItemUncheckedCreateWithoutSubcategoryInput = { + id: number + uid?: string identifier?: string | null name: string description?: string | null customerDescription?: string | null internalNotes?: string | null - category?: string | null - categoryCwId?: number | null - subcategory?: string | null - subcategoryCwId?: number | null - manufacturer?: string | null - manufactureCwId?: number | null + manufacturerId?: number | null partNumber?: string | null vendorName?: string | null vendorSku?: string | null @@ -1000,9 +1080,474 @@ export type CatalogItemUncheckedCreateWithoutLinkedToInput = { salesTaxable?: boolean onHand?: number cwLastUpdated?: Date | string | null + classId?: string | null createdAt?: Date | string updatedAt?: Date | string linkedItems?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedToInput + linkedTo?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedItemsInput + inventory?: Prisma.ProductInventoryUncheckedCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataUncheckedCreateNestedManyWithoutCatalogItemInput +} + +export type CatalogItemCreateOrConnectWithoutSubcategoryInput = { + where: Prisma.CatalogItemWhereUniqueInput + create: Prisma.XOR +} + +export type CatalogItemCreateManySubcategoryInputEnvelope = { + data: Prisma.CatalogItemCreateManySubcategoryInput | Prisma.CatalogItemCreateManySubcategoryInput[] + skipDuplicates?: boolean +} + +export type CatalogItemUpsertWithWhereUniqueWithoutSubcategoryInput = { + where: Prisma.CatalogItemWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type CatalogItemUpdateWithWhereUniqueWithoutSubcategoryInput = { + where: Prisma.CatalogItemWhereUniqueInput + data: Prisma.XOR +} + +export type CatalogItemUpdateManyWithWhereWithoutSubcategoryInput = { + where: Prisma.CatalogItemScalarWhereInput + data: Prisma.XOR +} + +export type CatalogItemScalarWhereInput = { + AND?: Prisma.CatalogItemScalarWhereInput | Prisma.CatalogItemScalarWhereInput[] + OR?: Prisma.CatalogItemScalarWhereInput[] + NOT?: Prisma.CatalogItemScalarWhereInput | Prisma.CatalogItemScalarWhereInput[] + id?: Prisma.IntFilter<"CatalogItem"> | number + uid?: Prisma.StringFilter<"CatalogItem"> | string + identifier?: Prisma.StringNullableFilter<"CatalogItem"> | string | null + name?: Prisma.StringFilter<"CatalogItem"> | string + description?: Prisma.StringNullableFilter<"CatalogItem"> | string | null + customerDescription?: Prisma.StringNullableFilter<"CatalogItem"> | string | null + internalNotes?: Prisma.StringNullableFilter<"CatalogItem"> | string | null + subcategoryId?: Prisma.IntFilter<"CatalogItem"> | number + manufacturerId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null + partNumber?: Prisma.StringNullableFilter<"CatalogItem"> | string | null + vendorName?: Prisma.StringNullableFilter<"CatalogItem"> | string | null + vendorSku?: Prisma.StringNullableFilter<"CatalogItem"> | string | null + vendorCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null + price?: Prisma.FloatFilter<"CatalogItem"> | number + cost?: Prisma.FloatFilter<"CatalogItem"> | number + inactive?: Prisma.BoolFilter<"CatalogItem"> | boolean + salesTaxable?: Prisma.BoolFilter<"CatalogItem"> | boolean + onHand?: Prisma.IntFilter<"CatalogItem"> | number + cwLastUpdated?: Prisma.DateTimeNullableFilter<"CatalogItem"> | Date | string | null + classId?: Prisma.StringNullableFilter<"CatalogItem"> | string | null + createdAt?: Prisma.DateTimeFilter<"CatalogItem"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"CatalogItem"> | Date | string +} + +export type CatalogItemCreateWithoutManufacturerInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + linkedItems?: Prisma.CatalogItemCreateNestedManyWithoutLinkedToInput + linkedTo?: Prisma.CatalogItemCreateNestedManyWithoutLinkedItemsInput + subcategory: Prisma.CatalogSubcategoryCreateNestedOneWithoutItemsInput + inventory?: Prisma.ProductInventoryCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataCreateNestedManyWithoutCatalogItemInput +} + +export type CatalogItemUncheckedCreateWithoutManufacturerInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + subcategoryId: number + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + linkedItems?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedToInput + linkedTo?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedItemsInput + inventory?: Prisma.ProductInventoryUncheckedCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataUncheckedCreateNestedManyWithoutCatalogItemInput +} + +export type CatalogItemCreateOrConnectWithoutManufacturerInput = { + where: Prisma.CatalogItemWhereUniqueInput + create: Prisma.XOR +} + +export type CatalogItemCreateManyManufacturerInputEnvelope = { + data: Prisma.CatalogItemCreateManyManufacturerInput | Prisma.CatalogItemCreateManyManufacturerInput[] + skipDuplicates?: boolean +} + +export type CatalogItemUpsertWithWhereUniqueWithoutManufacturerInput = { + where: Prisma.CatalogItemWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type CatalogItemUpdateWithWhereUniqueWithoutManufacturerInput = { + where: Prisma.CatalogItemWhereUniqueInput + data: Prisma.XOR +} + +export type CatalogItemUpdateManyWithWhereWithoutManufacturerInput = { + where: Prisma.CatalogItemScalarWhereInput + data: Prisma.XOR +} + +export type CatalogItemCreateWithoutInventoryInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + linkedItems?: Prisma.CatalogItemCreateNestedManyWithoutLinkedToInput + linkedTo?: Prisma.CatalogItemCreateNestedManyWithoutLinkedItemsInput + subcategory: Prisma.CatalogSubcategoryCreateNestedOneWithoutItemsInput + manufacturer?: Prisma.CatalogManufacturerCreateNestedOneWithoutItemsInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataCreateNestedManyWithoutCatalogItemInput +} + +export type CatalogItemUncheckedCreateWithoutInventoryInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + subcategoryId: number + manufacturerId?: number | null + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + linkedItems?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedToInput + linkedTo?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedItemsInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataUncheckedCreateNestedManyWithoutCatalogItemInput +} + +export type CatalogItemCreateOrConnectWithoutInventoryInput = { + where: Prisma.CatalogItemWhereUniqueInput + create: Prisma.XOR +} + +export type CatalogItemUpsertWithoutInventoryInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CatalogItemWhereInput +} + +export type CatalogItemUpdateToOneWithWhereWithoutInventoryInput = { + where?: Prisma.CatalogItemWhereInput + data: Prisma.XOR +} + +export type CatalogItemUpdateWithoutInventoryInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + linkedItems?: Prisma.CatalogItemUpdateManyWithoutLinkedToNestedInput + linkedTo?: Prisma.CatalogItemUpdateManyWithoutLinkedItemsNestedInput + subcategory?: Prisma.CatalogSubcategoryUpdateOneRequiredWithoutItemsNestedInput + manufacturer?: Prisma.CatalogManufacturerUpdateOneWithoutItemsNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUpdateManyWithoutCatalogItemNestedInput +} + +export type CatalogItemUncheckedUpdateWithoutInventoryInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + linkedItems?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedToNestedInput + linkedTo?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedItemsNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUncheckedUpdateManyWithoutCatalogItemNestedInput +} + +export type CatalogItemCreateWithoutMinimumStockByWarehousesInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + linkedItems?: Prisma.CatalogItemCreateNestedManyWithoutLinkedToInput + linkedTo?: Prisma.CatalogItemCreateNestedManyWithoutLinkedItemsInput + subcategory: Prisma.CatalogSubcategoryCreateNestedOneWithoutItemsInput + manufacturer?: Prisma.CatalogManufacturerCreateNestedOneWithoutItemsInput + inventory?: Prisma.ProductInventoryCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataCreateNestedManyWithoutCatalogItemInput +} + +export type CatalogItemUncheckedCreateWithoutMinimumStockByWarehousesInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + subcategoryId: number + manufacturerId?: number | null + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + linkedItems?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedToInput + linkedTo?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedItemsInput + inventory?: Prisma.ProductInventoryUncheckedCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataUncheckedCreateNestedManyWithoutCatalogItemInput +} + +export type CatalogItemCreateOrConnectWithoutMinimumStockByWarehousesInput = { + where: Prisma.CatalogItemWhereUniqueInput + create: Prisma.XOR +} + +export type CatalogItemUpsertWithoutMinimumStockByWarehousesInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CatalogItemWhereInput +} + +export type CatalogItemUpdateToOneWithWhereWithoutMinimumStockByWarehousesInput = { + where?: Prisma.CatalogItemWhereInput + data: Prisma.XOR +} + +export type CatalogItemUpdateWithoutMinimumStockByWarehousesInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + linkedItems?: Prisma.CatalogItemUpdateManyWithoutLinkedToNestedInput + linkedTo?: Prisma.CatalogItemUpdateManyWithoutLinkedItemsNestedInput + subcategory?: Prisma.CatalogSubcategoryUpdateOneRequiredWithoutItemsNestedInput + manufacturer?: Prisma.CatalogManufacturerUpdateOneWithoutItemsNestedInput + inventory?: Prisma.ProductInventoryUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUpdateManyWithoutCatalogItemNestedInput +} + +export type CatalogItemUncheckedUpdateWithoutMinimumStockByWarehousesInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + linkedItems?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedToNestedInput + linkedTo?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedItemsNestedInput + inventory?: Prisma.ProductInventoryUncheckedUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUncheckedUpdateManyWithoutCatalogItemNestedInput +} + +export type CatalogItemCreateWithoutLinkedToInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + linkedItems?: Prisma.CatalogItemCreateNestedManyWithoutLinkedToInput + subcategory: Prisma.CatalogSubcategoryCreateNestedOneWithoutItemsInput + manufacturer?: Prisma.CatalogManufacturerCreateNestedOneWithoutItemsInput + inventory?: Prisma.ProductInventoryCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataCreateNestedManyWithoutCatalogItemInput +} + +export type CatalogItemUncheckedCreateWithoutLinkedToInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + subcategoryId: number + manufacturerId?: number | null + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + linkedItems?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedToInput + inventory?: Prisma.ProductInventoryUncheckedCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataUncheckedCreateNestedManyWithoutCatalogItemInput } export type CatalogItemCreateOrConnectWithoutLinkedToInput = { @@ -1011,19 +1556,13 @@ export type CatalogItemCreateOrConnectWithoutLinkedToInput = { } export type CatalogItemCreateWithoutLinkedItemsInput = { - id?: string - cwCatalogId: number + id: number + uid?: string identifier?: string | null name: string description?: string | null customerDescription?: string | null internalNotes?: string | null - category?: string | null - categoryCwId?: number | null - subcategory?: string | null - subcategoryCwId?: number | null - manufacturer?: string | null - manufactureCwId?: number | null partNumber?: string | null vendorName?: string | null vendorSku?: string | null @@ -1034,25 +1573,27 @@ export type CatalogItemCreateWithoutLinkedItemsInput = { salesTaxable?: boolean onHand?: number cwLastUpdated?: Date | string | null + classId?: string | null createdAt?: Date | string updatedAt?: Date | string linkedTo?: Prisma.CatalogItemCreateNestedManyWithoutLinkedItemsInput + subcategory: Prisma.CatalogSubcategoryCreateNestedOneWithoutItemsInput + manufacturer?: Prisma.CatalogManufacturerCreateNestedOneWithoutItemsInput + inventory?: Prisma.ProductInventoryCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataCreateNestedManyWithoutCatalogItemInput } export type CatalogItemUncheckedCreateWithoutLinkedItemsInput = { - id?: string - cwCatalogId: number + id: number + uid?: string identifier?: string | null name: string description?: string | null customerDescription?: string | null internalNotes?: string | null - category?: string | null - categoryCwId?: number | null - subcategory?: string | null - subcategoryCwId?: number | null - manufacturer?: string | null - manufactureCwId?: number | null + subcategoryId: number + manufacturerId?: number | null partNumber?: string | null vendorName?: string | null vendorSku?: string | null @@ -1063,9 +1604,13 @@ export type CatalogItemUncheckedCreateWithoutLinkedItemsInput = { salesTaxable?: boolean onHand?: number cwLastUpdated?: Date | string | null + classId?: string | null createdAt?: Date | string updatedAt?: Date | string linkedTo?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedItemsInput + inventory?: Prisma.ProductInventoryUncheckedCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedCreateNestedManyWithoutItemInput + productDataRecords?: Prisma.ProductDataUncheckedCreateNestedManyWithoutCatalogItemInput } export type CatalogItemCreateOrConnectWithoutLinkedItemsInput = { @@ -1089,37 +1634,6 @@ export type CatalogItemUpdateManyWithWhereWithoutLinkedToInput = { data: Prisma.XOR } -export type CatalogItemScalarWhereInput = { - AND?: Prisma.CatalogItemScalarWhereInput | Prisma.CatalogItemScalarWhereInput[] - OR?: Prisma.CatalogItemScalarWhereInput[] - NOT?: Prisma.CatalogItemScalarWhereInput | Prisma.CatalogItemScalarWhereInput[] - id?: Prisma.StringFilter<"CatalogItem"> | string - cwCatalogId?: Prisma.IntFilter<"CatalogItem"> | number - identifier?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - name?: Prisma.StringFilter<"CatalogItem"> | string - description?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - customerDescription?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - internalNotes?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - category?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - categoryCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null - subcategory?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - subcategoryCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null - manufacturer?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - manufactureCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null - partNumber?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - vendorName?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - vendorSku?: Prisma.StringNullableFilter<"CatalogItem"> | string | null - vendorCwId?: Prisma.IntNullableFilter<"CatalogItem"> | number | null - price?: Prisma.FloatFilter<"CatalogItem"> | number - cost?: Prisma.FloatFilter<"CatalogItem"> | number - inactive?: Prisma.BoolFilter<"CatalogItem"> | boolean - salesTaxable?: Prisma.BoolFilter<"CatalogItem"> | boolean - onHand?: Prisma.IntFilter<"CatalogItem"> | number - cwLastUpdated?: Prisma.DateTimeNullableFilter<"CatalogItem"> | Date | string | null - createdAt?: Prisma.DateTimeFilter<"CatalogItem"> | Date | string - updatedAt?: Prisma.DateTimeFilter<"CatalogItem"> | Date | string -} - export type CatalogItemUpsertWithWhereUniqueWithoutLinkedItemsInput = { where: Prisma.CatalogItemWhereUniqueInput update: Prisma.XOR @@ -1136,20 +1650,88 @@ export type CatalogItemUpdateManyWithWhereWithoutLinkedItemsInput = { data: Prisma.XOR } -export type CatalogItemUpdateWithoutLinkedToInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number +export type CatalogItemCreateWithoutProductDataRecordsInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + linkedItems?: Prisma.CatalogItemCreateNestedManyWithoutLinkedToInput + linkedTo?: Prisma.CatalogItemCreateNestedManyWithoutLinkedItemsInput + subcategory: Prisma.CatalogSubcategoryCreateNestedOneWithoutItemsInput + manufacturer?: Prisma.CatalogManufacturerCreateNestedOneWithoutItemsInput + inventory?: Prisma.ProductInventoryCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseCreateNestedManyWithoutItemInput +} + +export type CatalogItemUncheckedCreateWithoutProductDataRecordsInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + subcategoryId: number + manufacturerId?: number | null + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + linkedItems?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedToInput + linkedTo?: Prisma.CatalogItemUncheckedCreateNestedManyWithoutLinkedItemsInput + inventory?: Prisma.ProductInventoryUncheckedCreateNestedManyWithoutItemInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedCreateNestedManyWithoutItemInput +} + +export type CatalogItemCreateOrConnectWithoutProductDataRecordsInput = { + where: Prisma.CatalogItemWhereUniqueInput + create: Prisma.XOR +} + +export type CatalogItemUpsertWithoutProductDataRecordsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CatalogItemWhereInput +} + +export type CatalogItemUpdateToOneWithWhereWithoutProductDataRecordsInput = { + where?: Prisma.CatalogItemWhereInput + data: Prisma.XOR +} + +export type CatalogItemUpdateWithoutProductDataRecordsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - categoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - subcategory?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - subcategoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - manufacturer?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - manufactureCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1160,25 +1742,27 @@ export type CatalogItemUpdateWithoutLinkedToInput = { salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean onHand?: Prisma.IntFieldUpdateOperationsInput | number cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string linkedItems?: Prisma.CatalogItemUpdateManyWithoutLinkedToNestedInput + linkedTo?: Prisma.CatalogItemUpdateManyWithoutLinkedItemsNestedInput + subcategory?: Prisma.CatalogSubcategoryUpdateOneRequiredWithoutItemsNestedInput + manufacturer?: Prisma.CatalogManufacturerUpdateOneWithoutItemsNestedInput + inventory?: Prisma.ProductInventoryUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUpdateManyWithoutItemNestedInput } -export type CatalogItemUncheckedUpdateWithoutLinkedToInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number +export type CatalogItemUncheckedUpdateWithoutProductDataRecordsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - categoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - subcategory?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - subcategoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - manufacturer?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - manufactureCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1189,25 +1773,47 @@ export type CatalogItemUncheckedUpdateWithoutLinkedToInput = { salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean onHand?: Prisma.IntFieldUpdateOperationsInput | number cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string linkedItems?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedToNestedInput + linkedTo?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedItemsNestedInput + inventory?: Prisma.ProductInventoryUncheckedUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedUpdateManyWithoutItemNestedInput } -export type CatalogItemUncheckedUpdateManyWithoutLinkedToInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number +export type CatalogItemCreateManySubcategoryInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + manufacturerId?: number | null + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CatalogItemUpdateWithoutSubcategoryInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - categoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - subcategory?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - subcategoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - manufacturer?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - manufactureCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1218,24 +1824,267 @@ export type CatalogItemUncheckedUpdateManyWithoutLinkedToInput = { salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean onHand?: Prisma.IntFieldUpdateOperationsInput | number cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + linkedItems?: Prisma.CatalogItemUpdateManyWithoutLinkedToNestedInput + linkedTo?: Prisma.CatalogItemUpdateManyWithoutLinkedItemsNestedInput + manufacturer?: Prisma.CatalogManufacturerUpdateOneWithoutItemsNestedInput + inventory?: Prisma.ProductInventoryUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUpdateManyWithoutCatalogItemNestedInput +} + +export type CatalogItemUncheckedUpdateWithoutSubcategoryInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + linkedItems?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedToNestedInput + linkedTo?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedItemsNestedInput + inventory?: Prisma.ProductInventoryUncheckedUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUncheckedUpdateManyWithoutCatalogItemNestedInput +} + +export type CatalogItemUncheckedUpdateManyWithoutSubcategoryInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CatalogItemCreateManyManufacturerInput = { + id: number + uid?: string + identifier?: string | null + name: string + description?: string | null + customerDescription?: string | null + internalNotes?: string | null + subcategoryId: number + partNumber?: string | null + vendorName?: string | null + vendorSku?: string | null + vendorCwId?: number | null + price: number + cost: number + inactive?: boolean + salesTaxable?: boolean + onHand?: number + cwLastUpdated?: Date | string | null + classId?: string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CatalogItemUpdateWithoutManufacturerInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + linkedItems?: Prisma.CatalogItemUpdateManyWithoutLinkedToNestedInput + linkedTo?: Prisma.CatalogItemUpdateManyWithoutLinkedItemsNestedInput + subcategory?: Prisma.CatalogSubcategoryUpdateOneRequiredWithoutItemsNestedInput + inventory?: Prisma.ProductInventoryUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUpdateManyWithoutCatalogItemNestedInput +} + +export type CatalogItemUncheckedUpdateWithoutManufacturerInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + linkedItems?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedToNestedInput + linkedTo?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedItemsNestedInput + inventory?: Prisma.ProductInventoryUncheckedUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUncheckedUpdateManyWithoutCatalogItemNestedInput +} + +export type CatalogItemUncheckedUpdateManyWithoutManufacturerInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CatalogItemUpdateWithoutLinkedToInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + linkedItems?: Prisma.CatalogItemUpdateManyWithoutLinkedToNestedInput + subcategory?: Prisma.CatalogSubcategoryUpdateOneRequiredWithoutItemsNestedInput + manufacturer?: Prisma.CatalogManufacturerUpdateOneWithoutItemsNestedInput + inventory?: Prisma.ProductInventoryUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUpdateManyWithoutCatalogItemNestedInput +} + +export type CatalogItemUncheckedUpdateWithoutLinkedToInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + linkedItems?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedToNestedInput + inventory?: Prisma.ProductInventoryUncheckedUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUncheckedUpdateManyWithoutCatalogItemNestedInput +} + +export type CatalogItemUncheckedUpdateManyWithoutLinkedToInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + name?: Prisma.StringFieldUpdateOperationsInput | string + description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + vendorCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + price?: Prisma.FloatFieldUpdateOperationsInput | number + cost?: Prisma.FloatFieldUpdateOperationsInput | number + inactive?: Prisma.BoolFieldUpdateOperationsInput | boolean + salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean + onHand?: Prisma.IntFieldUpdateOperationsInput | number + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } export type CatalogItemUpdateWithoutLinkedItemsInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - categoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - subcategory?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - subcategoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - manufacturer?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - manufactureCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1246,25 +2095,27 @@ export type CatalogItemUpdateWithoutLinkedItemsInput = { salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean onHand?: Prisma.IntFieldUpdateOperationsInput | number cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string linkedTo?: Prisma.CatalogItemUpdateManyWithoutLinkedItemsNestedInput + subcategory?: Prisma.CatalogSubcategoryUpdateOneRequiredWithoutItemsNestedInput + manufacturer?: Prisma.CatalogManufacturerUpdateOneWithoutItemsNestedInput + inventory?: Prisma.ProductInventoryUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUpdateManyWithoutCatalogItemNestedInput } export type CatalogItemUncheckedUpdateWithoutLinkedItemsInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - categoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - subcategory?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - subcategoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - manufacturer?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - manufactureCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1275,25 +2126,25 @@ export type CatalogItemUncheckedUpdateWithoutLinkedItemsInput = { salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean onHand?: Prisma.IntFieldUpdateOperationsInput | number cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string linkedTo?: Prisma.CatalogItemUncheckedUpdateManyWithoutLinkedItemsNestedInput + inventory?: Prisma.ProductInventoryUncheckedUpdateManyWithoutItemNestedInput + minimumStockByWarehouses?: Prisma.MinimumStockByWarehouseUncheckedUpdateManyWithoutItemNestedInput + productDataRecords?: Prisma.ProductDataUncheckedUpdateManyWithoutCatalogItemNestedInput } export type CatalogItemUncheckedUpdateManyWithoutLinkedItemsInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null internalNotes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - category?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - categoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - subcategory?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - subcategoryCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - manufacturer?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - manufactureCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + subcategoryId?: Prisma.IntFieldUpdateOperationsInput | number + manufacturerId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null partNumber?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null vendorSku?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1304,6 +2155,7 @@ export type CatalogItemUncheckedUpdateManyWithoutLinkedItemsInput = { salesTaxable?: Prisma.BoolFieldUpdateOperationsInput | boolean onHand?: Prisma.IntFieldUpdateOperationsInput | number cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + classId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } @@ -1316,11 +2168,17 @@ export type CatalogItemUncheckedUpdateManyWithoutLinkedItemsInput = { export type CatalogItemCountOutputType = { linkedItems: number linkedTo: number + inventory: number + minimumStockByWarehouses: number + productDataRecords: number } export type CatalogItemCountOutputTypeSelect = { linkedItems?: boolean | CatalogItemCountOutputTypeCountLinkedItemsArgs linkedTo?: boolean | CatalogItemCountOutputTypeCountLinkedToArgs + inventory?: boolean | CatalogItemCountOutputTypeCountInventoryArgs + minimumStockByWarehouses?: boolean | CatalogItemCountOutputTypeCountMinimumStockByWarehousesArgs + productDataRecords?: boolean | CatalogItemCountOutputTypeCountProductDataRecordsArgs } /** @@ -1347,21 +2205,38 @@ export type CatalogItemCountOutputTypeCountLinkedToArgs = { + where?: Prisma.ProductInventoryWhereInput +} + +/** + * CatalogItemCountOutputType without action + */ +export type CatalogItemCountOutputTypeCountMinimumStockByWarehousesArgs = { + where?: Prisma.MinimumStockByWarehouseWhereInput +} + +/** + * CatalogItemCountOutputType without action + */ +export type CatalogItemCountOutputTypeCountProductDataRecordsArgs = { + where?: Prisma.ProductDataWhereInput +} + export type CatalogItemSelect = runtime.Types.Extensions.GetSelect<{ id?: boolean - cwCatalogId?: boolean + uid?: boolean identifier?: boolean name?: boolean description?: boolean customerDescription?: boolean internalNotes?: boolean - category?: boolean - categoryCwId?: boolean - subcategory?: boolean - subcategoryCwId?: boolean - manufacturer?: boolean - manufactureCwId?: boolean + subcategoryId?: boolean + manufacturerId?: boolean partNumber?: boolean vendorName?: boolean vendorSku?: boolean @@ -1372,27 +2247,29 @@ export type CatalogItemSelect linkedTo?: boolean | Prisma.CatalogItem$linkedToArgs + subcategory?: boolean | Prisma.CatalogSubcategoryDefaultArgs + manufacturer?: boolean | Prisma.CatalogItem$manufacturerArgs + inventory?: boolean | Prisma.CatalogItem$inventoryArgs + minimumStockByWarehouses?: boolean | Prisma.CatalogItem$minimumStockByWarehousesArgs + productDataRecords?: boolean | Prisma.CatalogItem$productDataRecordsArgs _count?: boolean | Prisma.CatalogItemCountOutputTypeDefaultArgs }, ExtArgs["result"]["catalogItem"]> export type CatalogItemSelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ id?: boolean - cwCatalogId?: boolean + uid?: boolean identifier?: boolean name?: boolean description?: boolean customerDescription?: boolean internalNotes?: boolean - category?: boolean - categoryCwId?: boolean - subcategory?: boolean - subcategoryCwId?: boolean - manufacturer?: boolean - manufactureCwId?: boolean + subcategoryId?: boolean + manufacturerId?: boolean partNumber?: boolean vendorName?: boolean vendorSku?: boolean @@ -1403,24 +2280,23 @@ export type CatalogItemSelectCreateManyAndReturn + manufacturer?: boolean | Prisma.CatalogItem$manufacturerArgs }, ExtArgs["result"]["catalogItem"]> export type CatalogItemSelectUpdateManyAndReturn = runtime.Types.Extensions.GetSelect<{ id?: boolean - cwCatalogId?: boolean + uid?: boolean identifier?: boolean name?: boolean description?: boolean customerDescription?: boolean internalNotes?: boolean - category?: boolean - categoryCwId?: boolean - subcategory?: boolean - subcategoryCwId?: boolean - manufacturer?: boolean - manufactureCwId?: boolean + subcategoryId?: boolean + manufacturerId?: boolean partNumber?: boolean vendorName?: boolean vendorSku?: boolean @@ -1431,24 +2307,23 @@ export type CatalogItemSelectUpdateManyAndReturn + manufacturer?: boolean | Prisma.CatalogItem$manufacturerArgs }, ExtArgs["result"]["catalogItem"]> export type CatalogItemSelectScalar = { id?: boolean - cwCatalogId?: boolean + uid?: boolean identifier?: boolean name?: boolean description?: boolean customerDescription?: boolean internalNotes?: boolean - category?: boolean - categoryCwId?: boolean - subcategory?: boolean - subcategoryCwId?: boolean - manufacturer?: boolean - manufactureCwId?: boolean + subcategoryId?: boolean + manufacturerId?: boolean partNumber?: boolean vendorName?: boolean vendorSku?: boolean @@ -1459,39 +2334,52 @@ export type CatalogItemSelectScalar = { salesTaxable?: boolean onHand?: boolean cwLastUpdated?: boolean + classId?: boolean createdAt?: boolean updatedAt?: boolean } -export type CatalogItemOmit = runtime.Types.Extensions.GetOmit<"id" | "cwCatalogId" | "identifier" | "name" | "description" | "customerDescription" | "internalNotes" | "category" | "categoryCwId" | "subcategory" | "subcategoryCwId" | "manufacturer" | "manufactureCwId" | "partNumber" | "vendorName" | "vendorSku" | "vendorCwId" | "price" | "cost" | "inactive" | "salesTaxable" | "onHand" | "cwLastUpdated" | "createdAt" | "updatedAt", ExtArgs["result"]["catalogItem"]> +export type CatalogItemOmit = runtime.Types.Extensions.GetOmit<"id" | "uid" | "identifier" | "name" | "description" | "customerDescription" | "internalNotes" | "subcategoryId" | "manufacturerId" | "partNumber" | "vendorName" | "vendorSku" | "vendorCwId" | "price" | "cost" | "inactive" | "salesTaxable" | "onHand" | "cwLastUpdated" | "classId" | "createdAt" | "updatedAt", ExtArgs["result"]["catalogItem"]> export type CatalogItemInclude = { linkedItems?: boolean | Prisma.CatalogItem$linkedItemsArgs linkedTo?: boolean | Prisma.CatalogItem$linkedToArgs + subcategory?: boolean | Prisma.CatalogSubcategoryDefaultArgs + manufacturer?: boolean | Prisma.CatalogItem$manufacturerArgs + inventory?: boolean | Prisma.CatalogItem$inventoryArgs + minimumStockByWarehouses?: boolean | Prisma.CatalogItem$minimumStockByWarehousesArgs + productDataRecords?: boolean | Prisma.CatalogItem$productDataRecordsArgs _count?: boolean | Prisma.CatalogItemCountOutputTypeDefaultArgs } -export type CatalogItemIncludeCreateManyAndReturn = {} -export type CatalogItemIncludeUpdateManyAndReturn = {} +export type CatalogItemIncludeCreateManyAndReturn = { + subcategory?: boolean | Prisma.CatalogSubcategoryDefaultArgs + manufacturer?: boolean | Prisma.CatalogItem$manufacturerArgs +} +export type CatalogItemIncludeUpdateManyAndReturn = { + subcategory?: boolean | Prisma.CatalogSubcategoryDefaultArgs + manufacturer?: boolean | Prisma.CatalogItem$manufacturerArgs +} export type $CatalogItemPayload = { name: "CatalogItem" objects: { linkedItems: Prisma.$CatalogItemPayload[] linkedTo: Prisma.$CatalogItemPayload[] + subcategory: Prisma.$CatalogSubcategoryPayload + manufacturer: Prisma.$CatalogManufacturerPayload | null + inventory: Prisma.$ProductInventoryPayload[] + minimumStockByWarehouses: Prisma.$MinimumStockByWarehousePayload[] + productDataRecords: Prisma.$ProductDataPayload[] } scalars: runtime.Types.Extensions.GetPayloadResult<{ - id: string - cwCatalogId: number + id: number + uid: string identifier: string | null name: string description: string | null customerDescription: string | null internalNotes: string | null - category: string | null - categoryCwId: number | null - subcategory: string | null - subcategoryCwId: number | null - manufacturer: string | null - manufactureCwId: number | null + subcategoryId: number + manufacturerId: number | null partNumber: string | null vendorName: string | null vendorSku: string | null @@ -1502,6 +2390,7 @@ export type $CatalogItemPayload @@ -1900,6 +2789,11 @@ export interface Prisma__CatalogItemClient = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> linkedTo = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + subcategory = {}>(args?: Prisma.Subset>): Prisma.Prisma__CatalogSubcategoryClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + manufacturer = {}>(args?: Prisma.Subset>): Prisma.Prisma__CatalogManufacturerClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + inventory = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + minimumStockByWarehouses = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + productDataRecords = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -1929,19 +2823,15 @@ export interface Prisma__CatalogItemClient - readonly cwCatalogId: Prisma.FieldRef<"CatalogItem", 'Int'> + readonly id: Prisma.FieldRef<"CatalogItem", 'Int'> + readonly uid: Prisma.FieldRef<"CatalogItem", 'String'> readonly identifier: Prisma.FieldRef<"CatalogItem", 'String'> readonly name: Prisma.FieldRef<"CatalogItem", 'String'> readonly description: Prisma.FieldRef<"CatalogItem", 'String'> readonly customerDescription: Prisma.FieldRef<"CatalogItem", 'String'> readonly internalNotes: Prisma.FieldRef<"CatalogItem", 'String'> - readonly category: Prisma.FieldRef<"CatalogItem", 'String'> - readonly categoryCwId: Prisma.FieldRef<"CatalogItem", 'Int'> - readonly subcategory: Prisma.FieldRef<"CatalogItem", 'String'> - readonly subcategoryCwId: Prisma.FieldRef<"CatalogItem", 'Int'> - readonly manufacturer: Prisma.FieldRef<"CatalogItem", 'String'> - readonly manufactureCwId: Prisma.FieldRef<"CatalogItem", 'Int'> + readonly subcategoryId: Prisma.FieldRef<"CatalogItem", 'Int'> + readonly manufacturerId: Prisma.FieldRef<"CatalogItem", 'Int'> readonly partNumber: Prisma.FieldRef<"CatalogItem", 'String'> readonly vendorName: Prisma.FieldRef<"CatalogItem", 'String'> readonly vendorSku: Prisma.FieldRef<"CatalogItem", 'String'> @@ -1952,6 +2842,7 @@ export interface CatalogItemFieldRefs { readonly salesTaxable: Prisma.FieldRef<"CatalogItem", 'Boolean'> readonly onHand: Prisma.FieldRef<"CatalogItem", 'Int'> readonly cwLastUpdated: Prisma.FieldRef<"CatalogItem", 'DateTime'> + readonly classId: Prisma.FieldRef<"CatalogItem", 'String'> readonly createdAt: Prisma.FieldRef<"CatalogItem", 'DateTime'> readonly updatedAt: Prisma.FieldRef<"CatalogItem", 'DateTime'> } @@ -2150,6 +3041,11 @@ export type CatalogItemFindManyArgs | null } /** @@ -2273,6 +3173,10 @@ export type CatalogItemUpdateManyAndReturnArgs | null } /** @@ -2389,6 +3293,97 @@ export type CatalogItem$linkedToArgs = { + /** + * Select specific fields to fetch from the CatalogManufacturer + */ + select?: Prisma.CatalogManufacturerSelect | null + /** + * Omit specific fields from the CatalogManufacturer + */ + omit?: Prisma.CatalogManufacturerOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CatalogManufacturerInclude | null + where?: Prisma.CatalogManufacturerWhereInput +} + +/** + * CatalogItem.inventory + */ +export type CatalogItem$inventoryArgs = { + /** + * Select specific fields to fetch from the ProductInventory + */ + select?: Prisma.ProductInventorySelect | null + /** + * Omit specific fields from the ProductInventory + */ + omit?: Prisma.ProductInventoryOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ProductInventoryInclude | null + where?: Prisma.ProductInventoryWhereInput + orderBy?: Prisma.ProductInventoryOrderByWithRelationInput | Prisma.ProductInventoryOrderByWithRelationInput[] + cursor?: Prisma.ProductInventoryWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ProductInventoryScalarFieldEnum | Prisma.ProductInventoryScalarFieldEnum[] +} + +/** + * CatalogItem.minimumStockByWarehouses + */ +export type CatalogItem$minimumStockByWarehousesArgs = { + /** + * Select specific fields to fetch from the MinimumStockByWarehouse + */ + select?: Prisma.MinimumStockByWarehouseSelect | null + /** + * Omit specific fields from the MinimumStockByWarehouse + */ + omit?: Prisma.MinimumStockByWarehouseOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.MinimumStockByWarehouseInclude | null + where?: Prisma.MinimumStockByWarehouseWhereInput + orderBy?: Prisma.MinimumStockByWarehouseOrderByWithRelationInput | Prisma.MinimumStockByWarehouseOrderByWithRelationInput[] + cursor?: Prisma.MinimumStockByWarehouseWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.MinimumStockByWarehouseScalarFieldEnum | Prisma.MinimumStockByWarehouseScalarFieldEnum[] +} + +/** + * CatalogItem.productDataRecords + */ +export type CatalogItem$productDataRecordsArgs = { + /** + * Select specific fields to fetch from the ProductData + */ + select?: Prisma.ProductDataSelect | null + /** + * Omit specific fields from the ProductData + */ + omit?: Prisma.ProductDataOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ProductDataInclude | null + where?: Prisma.ProductDataWhereInput + orderBy?: Prisma.ProductDataOrderByWithRelationInput | Prisma.ProductDataOrderByWithRelationInput[] + cursor?: Prisma.ProductDataWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ProductDataScalarFieldEnum | Prisma.ProductDataScalarFieldEnum[] +} + /** * CatalogItem without action */ diff --git a/api/generated/prisma/models/Company.ts b/api/generated/prisma/models/Company.ts index 925ee53..eb29e9d 100644 --- a/api/generated/prisma/models/Company.ts +++ b/api/generated/prisma/models/Company.ts @@ -27,36 +27,60 @@ export type AggregateCompany = { } export type CompanyAvgAggregateOutputType = { - cw_CompanyId: number | null + id: number | null } export type CompanySumAggregateOutputType = { - cw_CompanyId: number | null + id: number | null } export type CompanyMinAggregateOutputType = { - id: string | null + id: number | null + uid: string | null name: string | null - cw_CompanyId: number | null - cw_Identifier: string | null + phone: string | null + website: string | null + deleteFlag: boolean | null + dateDeleted: Date | null + taxId: string | null + taxExempt: boolean | null + enteredById: string | null + deletedById: string | null + deletedAt: Date | null createdAt: Date | null updatedAt: Date | null } export type CompanyMaxAggregateOutputType = { - id: string | null + id: number | null + uid: string | null name: string | null - cw_CompanyId: number | null - cw_Identifier: string | null + phone: string | null + website: string | null + deleteFlag: boolean | null + dateDeleted: Date | null + taxId: string | null + taxExempt: boolean | null + enteredById: string | null + deletedById: string | null + deletedAt: Date | null createdAt: Date | null updatedAt: Date | null } export type CompanyCountAggregateOutputType = { id: number + uid: number name: number - cw_CompanyId: number - cw_Identifier: number + phone: number + website: number + deleteFlag: number + dateDeleted: number + taxId: number + taxExempt: number + enteredById: number + deletedById: number + deletedAt: number createdAt: number updatedAt: number _all: number @@ -64,36 +88,60 @@ export type CompanyCountAggregateOutputType = { export type CompanyAvgAggregateInputType = { - cw_CompanyId?: true + id?: true } export type CompanySumAggregateInputType = { - cw_CompanyId?: true + id?: true } export type CompanyMinAggregateInputType = { id?: true + uid?: true name?: true - cw_CompanyId?: true - cw_Identifier?: true + phone?: true + website?: true + deleteFlag?: true + dateDeleted?: true + taxId?: true + taxExempt?: true + enteredById?: true + deletedById?: true + deletedAt?: true createdAt?: true updatedAt?: true } export type CompanyMaxAggregateInputType = { id?: true + uid?: true name?: true - cw_CompanyId?: true - cw_Identifier?: true + phone?: true + website?: true + deleteFlag?: true + dateDeleted?: true + taxId?: true + taxExempt?: true + enteredById?: true + deletedById?: true + deletedAt?: true createdAt?: true updatedAt?: true } export type CompanyCountAggregateInputType = { id?: true + uid?: true name?: true - cw_CompanyId?: true - cw_Identifier?: true + phone?: true + website?: true + deleteFlag?: true + dateDeleted?: true + taxId?: true + taxExempt?: true + enteredById?: true + deletedById?: true + deletedAt?: true createdAt?: true updatedAt?: true _all?: true @@ -186,10 +234,18 @@ export type CompanyGroupByArgs | string + id?: Prisma.IntFilter<"Company"> | number + uid?: Prisma.StringFilter<"Company"> | string name?: Prisma.StringFilter<"Company"> | string - cw_CompanyId?: Prisma.IntFilter<"Company"> | number - cw_Identifier?: Prisma.StringFilter<"Company"> | string + phone?: Prisma.StringNullableFilter<"Company"> | string | null + website?: Prisma.StringNullableFilter<"Company"> | string | null + deleteFlag?: Prisma.BoolFilter<"Company"> | boolean + dateDeleted?: Prisma.DateTimeNullableFilter<"Company"> | Date | string | null + taxId?: Prisma.StringNullableFilter<"Company"> | string | null + taxExempt?: Prisma.BoolFilter<"Company"> | boolean + enteredById?: Prisma.StringNullableFilter<"Company"> | string | null + deletedById?: Prisma.StringNullableFilter<"Company"> | string | null + deletedAt?: Prisma.DateTimeNullableFilter<"Company"> | Date | string | null createdAt?: Prisma.DateTimeFilter<"Company"> | Date | string updatedAt?: Prisma.DateTimeFilter<"Company"> | Date | string + contacts?: Prisma.ContactListRelationFilter + companyAddresses?: Prisma.CompanyAddressListRelationFilter credentials?: Prisma.CredentialListRelationFilter unifiSites?: Prisma.UnifiSiteListRelationFilter opportunities?: Prisma.OpportunityListRelationFilter + deletedBy?: Prisma.XOR | null + enteredBy?: Prisma.XOR | null + serviceTickets?: Prisma.ServiceTicketListRelationFilter + billingServiceTickets?: Prisma.ServiceTicketListRelationFilter } export type CompanyOrderByWithRelationInput = { id?: Prisma.SortOrder + uid?: Prisma.SortOrder name?: Prisma.SortOrder - cw_CompanyId?: Prisma.SortOrder - cw_Identifier?: Prisma.SortOrder + phone?: Prisma.SortOrderInput | Prisma.SortOrder + website?: Prisma.SortOrderInput | Prisma.SortOrder + deleteFlag?: Prisma.SortOrder + dateDeleted?: Prisma.SortOrderInput | Prisma.SortOrder + taxId?: Prisma.SortOrderInput | Prisma.SortOrder + taxExempt?: Prisma.SortOrder + enteredById?: Prisma.SortOrderInput | Prisma.SortOrder + deletedById?: Prisma.SortOrderInput | Prisma.SortOrder + deletedAt?: Prisma.SortOrderInput | Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder + contacts?: Prisma.ContactOrderByRelationAggregateInput + companyAddresses?: Prisma.CompanyAddressOrderByRelationAggregateInput credentials?: Prisma.CredentialOrderByRelationAggregateInput unifiSites?: Prisma.UnifiSiteOrderByRelationAggregateInput opportunities?: Prisma.OpportunityOrderByRelationAggregateInput + deletedBy?: Prisma.UserOrderByWithRelationInput + enteredBy?: Prisma.UserOrderByWithRelationInput + serviceTickets?: Prisma.ServiceTicketOrderByRelationAggregateInput + billingServiceTickets?: Prisma.ServiceTicketOrderByRelationAggregateInput } export type CompanyWhereUniqueInput = Prisma.AtLeast<{ - id?: string - cw_CompanyId?: number - cw_Identifier?: string + id?: number + uid?: string AND?: Prisma.CompanyWhereInput | Prisma.CompanyWhereInput[] OR?: Prisma.CompanyWhereInput[] NOT?: Prisma.CompanyWhereInput | Prisma.CompanyWhereInput[] name?: Prisma.StringFilter<"Company"> | string + phone?: Prisma.StringNullableFilter<"Company"> | string | null + website?: Prisma.StringNullableFilter<"Company"> | string | null + deleteFlag?: Prisma.BoolFilter<"Company"> | boolean + dateDeleted?: Prisma.DateTimeNullableFilter<"Company"> | Date | string | null + taxId?: Prisma.StringNullableFilter<"Company"> | string | null + taxExempt?: Prisma.BoolFilter<"Company"> | boolean + enteredById?: Prisma.StringNullableFilter<"Company"> | string | null + deletedById?: Prisma.StringNullableFilter<"Company"> | string | null + deletedAt?: Prisma.DateTimeNullableFilter<"Company"> | Date | string | null createdAt?: Prisma.DateTimeFilter<"Company"> | Date | string updatedAt?: Prisma.DateTimeFilter<"Company"> | Date | string + contacts?: Prisma.ContactListRelationFilter + companyAddresses?: Prisma.CompanyAddressListRelationFilter credentials?: Prisma.CredentialListRelationFilter unifiSites?: Prisma.UnifiSiteListRelationFilter opportunities?: Prisma.OpportunityListRelationFilter -}, "id" | "cw_CompanyId" | "cw_Identifier"> + deletedBy?: Prisma.XOR | null + enteredBy?: Prisma.XOR | null + serviceTickets?: Prisma.ServiceTicketListRelationFilter + billingServiceTickets?: Prisma.ServiceTicketListRelationFilter +}, "uid" | "id"> export type CompanyOrderByWithAggregationInput = { id?: Prisma.SortOrder + uid?: Prisma.SortOrder name?: Prisma.SortOrder - cw_CompanyId?: Prisma.SortOrder - cw_Identifier?: Prisma.SortOrder + phone?: Prisma.SortOrderInput | Prisma.SortOrder + website?: Prisma.SortOrderInput | Prisma.SortOrder + deleteFlag?: Prisma.SortOrder + dateDeleted?: Prisma.SortOrderInput | Prisma.SortOrder + taxId?: Prisma.SortOrderInput | Prisma.SortOrder + taxExempt?: Prisma.SortOrder + enteredById?: Prisma.SortOrderInput | Prisma.SortOrder + deletedById?: Prisma.SortOrderInput | Prisma.SortOrder + deletedAt?: Prisma.SortOrderInput | Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder _count?: Prisma.CompanyCountOrderByAggregateInput @@ -274,89 +380,177 @@ export type CompanyScalarWhereWithAggregatesInput = { AND?: Prisma.CompanyScalarWhereWithAggregatesInput | Prisma.CompanyScalarWhereWithAggregatesInput[] OR?: Prisma.CompanyScalarWhereWithAggregatesInput[] NOT?: Prisma.CompanyScalarWhereWithAggregatesInput | Prisma.CompanyScalarWhereWithAggregatesInput[] - id?: Prisma.StringWithAggregatesFilter<"Company"> | string + id?: Prisma.IntWithAggregatesFilter<"Company"> | number + uid?: Prisma.StringWithAggregatesFilter<"Company"> | string name?: Prisma.StringWithAggregatesFilter<"Company"> | string - cw_CompanyId?: Prisma.IntWithAggregatesFilter<"Company"> | number - cw_Identifier?: Prisma.StringWithAggregatesFilter<"Company"> | string + phone?: Prisma.StringNullableWithAggregatesFilter<"Company"> | string | null + website?: Prisma.StringNullableWithAggregatesFilter<"Company"> | string | null + deleteFlag?: Prisma.BoolWithAggregatesFilter<"Company"> | boolean + dateDeleted?: Prisma.DateTimeNullableWithAggregatesFilter<"Company"> | Date | string | null + taxId?: Prisma.StringNullableWithAggregatesFilter<"Company"> | string | null + taxExempt?: Prisma.BoolWithAggregatesFilter<"Company"> | boolean + enteredById?: Prisma.StringNullableWithAggregatesFilter<"Company"> | string | null + deletedById?: Prisma.StringNullableWithAggregatesFilter<"Company"> | string | null + deletedAt?: Prisma.DateTimeNullableWithAggregatesFilter<"Company"> | Date | string | null createdAt?: Prisma.DateTimeWithAggregatesFilter<"Company"> | Date | string updatedAt?: Prisma.DateTimeWithAggregatesFilter<"Company"> | Date | string } export type CompanyCreateInput = { - id?: string + id: number + uid?: string name: string - cw_CompanyId: number - cw_Identifier: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedAt?: Date | string | null createdAt?: Date | string updatedAt?: Date | string + contacts?: Prisma.ContactCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutCompanyInput credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput + deletedBy?: Prisma.UserCreateNestedOneWithoutCompaniesDeletedInput + enteredBy?: Prisma.UserCreateNestedOneWithoutCompaniesEnteredInput + serviceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutBillingCompanyInput } export type CompanyUncheckedCreateInput = { - id?: string + id: number + uid?: string name: string - cw_CompanyId: number - cw_Identifier: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedById?: string | null + deletedAt?: Date | string | null createdAt?: Date | string updatedAt?: Date | string + contacts?: Prisma.ContactUncheckedCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutCompanyInput credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput + serviceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutBillingCompanyInput } export type CompanyUpdateInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string - cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number - cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutCompanyNestedInput credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput + deletedBy?: Prisma.UserUpdateOneWithoutCompaniesDeletedNestedInput + enteredBy?: Prisma.UserUpdateOneWithoutCompaniesEnteredNestedInput + serviceTickets?: Prisma.ServiceTicketUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUpdateManyWithoutBillingCompanyNestedInput } export type CompanyUncheckedUpdateInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string - cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number - cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUncheckedUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutCompanyNestedInput credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput + serviceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutBillingCompanyNestedInput } export type CompanyCreateManyInput = { - id?: string + id: number + uid?: string name: string - cw_CompanyId: number - cw_Identifier: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedById?: string | null + deletedAt?: Date | string | null createdAt?: Date | string updatedAt?: Date | string } export type CompanyUpdateManyMutationInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string - cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number - cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } export type CompanyUncheckedUpdateManyInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string - cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number - cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } +export type CompanyListRelationFilter = { + every?: Prisma.CompanyWhereInput + some?: Prisma.CompanyWhereInput + none?: Prisma.CompanyWhereInput +} + +export type CompanyOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + export type CompanyNullableScalarRelationFilter = { is?: Prisma.CompanyWhereInput | null isNot?: Prisma.CompanyWhereInput | null @@ -364,37 +558,61 @@ export type CompanyNullableScalarRelationFilter = { export type CompanyCountOrderByAggregateInput = { id?: Prisma.SortOrder + uid?: Prisma.SortOrder name?: Prisma.SortOrder - cw_CompanyId?: Prisma.SortOrder - cw_Identifier?: Prisma.SortOrder + phone?: Prisma.SortOrder + website?: Prisma.SortOrder + deleteFlag?: Prisma.SortOrder + dateDeleted?: Prisma.SortOrder + taxId?: Prisma.SortOrder + taxExempt?: Prisma.SortOrder + enteredById?: Prisma.SortOrder + deletedById?: Prisma.SortOrder + deletedAt?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } export type CompanyAvgOrderByAggregateInput = { - cw_CompanyId?: Prisma.SortOrder + id?: Prisma.SortOrder } export type CompanyMaxOrderByAggregateInput = { id?: Prisma.SortOrder + uid?: Prisma.SortOrder name?: Prisma.SortOrder - cw_CompanyId?: Prisma.SortOrder - cw_Identifier?: Prisma.SortOrder + phone?: Prisma.SortOrder + website?: Prisma.SortOrder + deleteFlag?: Prisma.SortOrder + dateDeleted?: Prisma.SortOrder + taxId?: Prisma.SortOrder + taxExempt?: Prisma.SortOrder + enteredById?: Prisma.SortOrder + deletedById?: Prisma.SortOrder + deletedAt?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } export type CompanyMinOrderByAggregateInput = { id?: Prisma.SortOrder + uid?: Prisma.SortOrder name?: Prisma.SortOrder - cw_CompanyId?: Prisma.SortOrder - cw_Identifier?: Prisma.SortOrder + phone?: Prisma.SortOrder + website?: Prisma.SortOrder + deleteFlag?: Prisma.SortOrder + dateDeleted?: Prisma.SortOrder + taxId?: Prisma.SortOrder + taxExempt?: Prisma.SortOrder + enteredById?: Prisma.SortOrder + deletedById?: Prisma.SortOrder + deletedAt?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } export type CompanySumOrderByAggregateInput = { - cw_CompanyId?: Prisma.SortOrder + id?: Prisma.SortOrder } export type CompanyScalarRelationFilter = { @@ -402,6 +620,90 @@ export type CompanyScalarRelationFilter = { isNot?: Prisma.CompanyWhereInput } +export type CompanyCreateNestedManyWithoutDeletedByInput = { + create?: Prisma.XOR | Prisma.CompanyCreateWithoutDeletedByInput[] | Prisma.CompanyUncheckedCreateWithoutDeletedByInput[] + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutDeletedByInput | Prisma.CompanyCreateOrConnectWithoutDeletedByInput[] + createMany?: Prisma.CompanyCreateManyDeletedByInputEnvelope + connect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] +} + +export type CompanyCreateNestedManyWithoutEnteredByInput = { + create?: Prisma.XOR | Prisma.CompanyCreateWithoutEnteredByInput[] | Prisma.CompanyUncheckedCreateWithoutEnteredByInput[] + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutEnteredByInput | Prisma.CompanyCreateOrConnectWithoutEnteredByInput[] + createMany?: Prisma.CompanyCreateManyEnteredByInputEnvelope + connect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] +} + +export type CompanyUncheckedCreateNestedManyWithoutDeletedByInput = { + create?: Prisma.XOR | Prisma.CompanyCreateWithoutDeletedByInput[] | Prisma.CompanyUncheckedCreateWithoutDeletedByInput[] + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutDeletedByInput | Prisma.CompanyCreateOrConnectWithoutDeletedByInput[] + createMany?: Prisma.CompanyCreateManyDeletedByInputEnvelope + connect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] +} + +export type CompanyUncheckedCreateNestedManyWithoutEnteredByInput = { + create?: Prisma.XOR | Prisma.CompanyCreateWithoutEnteredByInput[] | Prisma.CompanyUncheckedCreateWithoutEnteredByInput[] + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutEnteredByInput | Prisma.CompanyCreateOrConnectWithoutEnteredByInput[] + createMany?: Prisma.CompanyCreateManyEnteredByInputEnvelope + connect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] +} + +export type CompanyUpdateManyWithoutDeletedByNestedInput = { + create?: Prisma.XOR | Prisma.CompanyCreateWithoutDeletedByInput[] | Prisma.CompanyUncheckedCreateWithoutDeletedByInput[] + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutDeletedByInput | Prisma.CompanyCreateOrConnectWithoutDeletedByInput[] + upsert?: Prisma.CompanyUpsertWithWhereUniqueWithoutDeletedByInput | Prisma.CompanyUpsertWithWhereUniqueWithoutDeletedByInput[] + createMany?: Prisma.CompanyCreateManyDeletedByInputEnvelope + set?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + disconnect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + delete?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + connect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + update?: Prisma.CompanyUpdateWithWhereUniqueWithoutDeletedByInput | Prisma.CompanyUpdateWithWhereUniqueWithoutDeletedByInput[] + updateMany?: Prisma.CompanyUpdateManyWithWhereWithoutDeletedByInput | Prisma.CompanyUpdateManyWithWhereWithoutDeletedByInput[] + deleteMany?: Prisma.CompanyScalarWhereInput | Prisma.CompanyScalarWhereInput[] +} + +export type CompanyUpdateManyWithoutEnteredByNestedInput = { + create?: Prisma.XOR | Prisma.CompanyCreateWithoutEnteredByInput[] | Prisma.CompanyUncheckedCreateWithoutEnteredByInput[] + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutEnteredByInput | Prisma.CompanyCreateOrConnectWithoutEnteredByInput[] + upsert?: Prisma.CompanyUpsertWithWhereUniqueWithoutEnteredByInput | Prisma.CompanyUpsertWithWhereUniqueWithoutEnteredByInput[] + createMany?: Prisma.CompanyCreateManyEnteredByInputEnvelope + set?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + disconnect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + delete?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + connect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + update?: Prisma.CompanyUpdateWithWhereUniqueWithoutEnteredByInput | Prisma.CompanyUpdateWithWhereUniqueWithoutEnteredByInput[] + updateMany?: Prisma.CompanyUpdateManyWithWhereWithoutEnteredByInput | Prisma.CompanyUpdateManyWithWhereWithoutEnteredByInput[] + deleteMany?: Prisma.CompanyScalarWhereInput | Prisma.CompanyScalarWhereInput[] +} + +export type CompanyUncheckedUpdateManyWithoutDeletedByNestedInput = { + create?: Prisma.XOR | Prisma.CompanyCreateWithoutDeletedByInput[] | Prisma.CompanyUncheckedCreateWithoutDeletedByInput[] + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutDeletedByInput | Prisma.CompanyCreateOrConnectWithoutDeletedByInput[] + upsert?: Prisma.CompanyUpsertWithWhereUniqueWithoutDeletedByInput | Prisma.CompanyUpsertWithWhereUniqueWithoutDeletedByInput[] + createMany?: Prisma.CompanyCreateManyDeletedByInputEnvelope + set?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + disconnect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + delete?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + connect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + update?: Prisma.CompanyUpdateWithWhereUniqueWithoutDeletedByInput | Prisma.CompanyUpdateWithWhereUniqueWithoutDeletedByInput[] + updateMany?: Prisma.CompanyUpdateManyWithWhereWithoutDeletedByInput | Prisma.CompanyUpdateManyWithWhereWithoutDeletedByInput[] + deleteMany?: Prisma.CompanyScalarWhereInput | Prisma.CompanyScalarWhereInput[] +} + +export type CompanyUncheckedUpdateManyWithoutEnteredByNestedInput = { + create?: Prisma.XOR | Prisma.CompanyCreateWithoutEnteredByInput[] | Prisma.CompanyUncheckedCreateWithoutEnteredByInput[] + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutEnteredByInput | Prisma.CompanyCreateOrConnectWithoutEnteredByInput[] + upsert?: Prisma.CompanyUpsertWithWhereUniqueWithoutEnteredByInput | Prisma.CompanyUpsertWithWhereUniqueWithoutEnteredByInput[] + createMany?: Prisma.CompanyCreateManyEnteredByInputEnvelope + set?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + disconnect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + delete?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + connect?: Prisma.CompanyWhereUniqueInput | Prisma.CompanyWhereUniqueInput[] + update?: Prisma.CompanyUpdateWithWhereUniqueWithoutEnteredByInput | Prisma.CompanyUpdateWithWhereUniqueWithoutEnteredByInput[] + updateMany?: Prisma.CompanyUpdateManyWithWhereWithoutEnteredByInput | Prisma.CompanyUpdateManyWithWhereWithoutEnteredByInput[] + deleteMany?: Prisma.CompanyScalarWhereInput | Prisma.CompanyScalarWhereInput[] +} + export type CompanyCreateNestedOneWithoutUnifiSitesInput = { create?: Prisma.XOR connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutUnifiSitesInput @@ -418,12 +720,66 @@ export type CompanyUpdateOneWithoutUnifiSitesNestedInput = { update?: Prisma.XOR, Prisma.CompanyUncheckedUpdateWithoutUnifiSitesInput> } -export type IntFieldUpdateOperationsInput = { - set?: number - increment?: number - decrement?: number - multiply?: number - divide?: number +export type CompanyCreateNestedOneWithoutCompanyAddressesInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutCompanyAddressesInput + connect?: Prisma.CompanyWhereUniqueInput +} + +export type CompanyUpdateOneRequiredWithoutCompanyAddressesNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutCompanyAddressesInput + upsert?: Prisma.CompanyUpsertWithoutCompanyAddressesInput + connect?: Prisma.CompanyWhereUniqueInput + update?: Prisma.XOR, Prisma.CompanyUncheckedUpdateWithoutCompanyAddressesInput> +} + +export type CompanyCreateNestedOneWithoutContactsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutContactsInput + connect?: Prisma.CompanyWhereUniqueInput +} + +export type CompanyUpdateOneWithoutContactsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutContactsInput + upsert?: Prisma.CompanyUpsertWithoutContactsInput + disconnect?: Prisma.CompanyWhereInput | boolean + delete?: Prisma.CompanyWhereInput | boolean + connect?: Prisma.CompanyWhereUniqueInput + update?: Prisma.XOR, Prisma.CompanyUncheckedUpdateWithoutContactsInput> +} + +export type CompanyCreateNestedOneWithoutServiceTicketsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutServiceTicketsInput + connect?: Prisma.CompanyWhereUniqueInput +} + +export type CompanyCreateNestedOneWithoutBillingServiceTicketsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutBillingServiceTicketsInput + connect?: Prisma.CompanyWhereUniqueInput +} + +export type CompanyUpdateOneWithoutServiceTicketsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutServiceTicketsInput + upsert?: Prisma.CompanyUpsertWithoutServiceTicketsInput + disconnect?: Prisma.CompanyWhereInput | boolean + delete?: Prisma.CompanyWhereInput | boolean + connect?: Prisma.CompanyWhereUniqueInput + update?: Prisma.XOR, Prisma.CompanyUncheckedUpdateWithoutServiceTicketsInput> +} + +export type CompanyUpdateOneWithoutBillingServiceTicketsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutBillingServiceTicketsInput + upsert?: Prisma.CompanyUpsertWithoutBillingServiceTicketsInput + disconnect?: Prisma.CompanyWhereInput | boolean + delete?: Prisma.CompanyWhereInput | boolean + connect?: Prisma.CompanyWhereUniqueInput + update?: Prisma.XOR, Prisma.CompanyUncheckedUpdateWithoutBillingServiceTicketsInput> } export type CompanyCreateNestedOneWithoutOpportunitiesInput = { @@ -456,26 +812,214 @@ export type CompanyUpdateOneRequiredWithoutCredentialsNestedInput = { update?: Prisma.XOR, Prisma.CompanyUncheckedUpdateWithoutCredentialsInput> } -export type CompanyCreateWithoutUnifiSitesInput = { - id?: string +export type CompanyCreateWithoutDeletedByInput = { + id: number + uid?: string name: string - cw_CompanyId: number - cw_Identifier: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedAt?: Date | string | null createdAt?: Date | string updatedAt?: Date | string + contacts?: Prisma.ContactCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput + enteredBy?: Prisma.UserCreateNestedOneWithoutCompaniesEnteredInput + serviceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutBillingCompanyInput +} + +export type CompanyUncheckedCreateWithoutDeletedByInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + contacts?: Prisma.ContactUncheckedCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput + serviceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutBillingCompanyInput +} + +export type CompanyCreateOrConnectWithoutDeletedByInput = { + where: Prisma.CompanyWhereUniqueInput + create: Prisma.XOR +} + +export type CompanyCreateManyDeletedByInputEnvelope = { + data: Prisma.CompanyCreateManyDeletedByInput | Prisma.CompanyCreateManyDeletedByInput[] + skipDuplicates?: boolean +} + +export type CompanyCreateWithoutEnteredByInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + contacts?: Prisma.ContactCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput + deletedBy?: Prisma.UserCreateNestedOneWithoutCompaniesDeletedInput + serviceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutBillingCompanyInput +} + +export type CompanyUncheckedCreateWithoutEnteredByInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedById?: string | null + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + contacts?: Prisma.ContactUncheckedCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput + serviceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutBillingCompanyInput +} + +export type CompanyCreateOrConnectWithoutEnteredByInput = { + where: Prisma.CompanyWhereUniqueInput + create: Prisma.XOR +} + +export type CompanyCreateManyEnteredByInputEnvelope = { + data: Prisma.CompanyCreateManyEnteredByInput | Prisma.CompanyCreateManyEnteredByInput[] + skipDuplicates?: boolean +} + +export type CompanyUpsertWithWhereUniqueWithoutDeletedByInput = { + where: Prisma.CompanyWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type CompanyUpdateWithWhereUniqueWithoutDeletedByInput = { + where: Prisma.CompanyWhereUniqueInput + data: Prisma.XOR +} + +export type CompanyUpdateManyWithWhereWithoutDeletedByInput = { + where: Prisma.CompanyScalarWhereInput + data: Prisma.XOR +} + +export type CompanyScalarWhereInput = { + AND?: Prisma.CompanyScalarWhereInput | Prisma.CompanyScalarWhereInput[] + OR?: Prisma.CompanyScalarWhereInput[] + NOT?: Prisma.CompanyScalarWhereInput | Prisma.CompanyScalarWhereInput[] + id?: Prisma.IntFilter<"Company"> | number + uid?: Prisma.StringFilter<"Company"> | string + name?: Prisma.StringFilter<"Company"> | string + phone?: Prisma.StringNullableFilter<"Company"> | string | null + website?: Prisma.StringNullableFilter<"Company"> | string | null + deleteFlag?: Prisma.BoolFilter<"Company"> | boolean + dateDeleted?: Prisma.DateTimeNullableFilter<"Company"> | Date | string | null + taxId?: Prisma.StringNullableFilter<"Company"> | string | null + taxExempt?: Prisma.BoolFilter<"Company"> | boolean + enteredById?: Prisma.StringNullableFilter<"Company"> | string | null + deletedById?: Prisma.StringNullableFilter<"Company"> | string | null + deletedAt?: Prisma.DateTimeNullableFilter<"Company"> | Date | string | null + createdAt?: Prisma.DateTimeFilter<"Company"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"Company"> | Date | string +} + +export type CompanyUpsertWithWhereUniqueWithoutEnteredByInput = { + where: Prisma.CompanyWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type CompanyUpdateWithWhereUniqueWithoutEnteredByInput = { + where: Prisma.CompanyWhereUniqueInput + data: Prisma.XOR +} + +export type CompanyUpdateManyWithWhereWithoutEnteredByInput = { + where: Prisma.CompanyScalarWhereInput + data: Prisma.XOR +} + +export type CompanyCreateWithoutUnifiSitesInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + contacts?: Prisma.ContactCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutCompanyInput credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput + deletedBy?: Prisma.UserCreateNestedOneWithoutCompaniesDeletedInput + enteredBy?: Prisma.UserCreateNestedOneWithoutCompaniesEnteredInput + serviceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutBillingCompanyInput } export type CompanyUncheckedCreateWithoutUnifiSitesInput = { - id?: string + id: number + uid?: string name: string - cw_CompanyId: number - cw_Identifier: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedById?: string | null + deletedAt?: Date | string | null createdAt?: Date | string updatedAt?: Date | string + contacts?: Prisma.ContactUncheckedCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutCompanyInput credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput + serviceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutBillingCompanyInput } export type CompanyCreateOrConnectWithoutUnifiSitesInput = { @@ -495,47 +1039,527 @@ export type CompanyUpdateToOneWithWhereWithoutUnifiSitesInput = { } export type CompanyUpdateWithoutUnifiSitesInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string - cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number - cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutCompanyNestedInput credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput + deletedBy?: Prisma.UserUpdateOneWithoutCompaniesDeletedNestedInput + enteredBy?: Prisma.UserUpdateOneWithoutCompaniesEnteredNestedInput + serviceTickets?: Prisma.ServiceTicketUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUpdateManyWithoutBillingCompanyNestedInput } export type CompanyUncheckedUpdateWithoutUnifiSitesInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string - cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number - cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUncheckedUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutCompanyNestedInput credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput + serviceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyCreateWithoutCompanyAddressesInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + contacts?: Prisma.ContactCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput + deletedBy?: Prisma.UserCreateNestedOneWithoutCompaniesDeletedInput + enteredBy?: Prisma.UserCreateNestedOneWithoutCompaniesEnteredInput + serviceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutBillingCompanyInput +} + +export type CompanyUncheckedCreateWithoutCompanyAddressesInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedById?: string | null + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + contacts?: Prisma.ContactUncheckedCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput + serviceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutBillingCompanyInput +} + +export type CompanyCreateOrConnectWithoutCompanyAddressesInput = { + where: Prisma.CompanyWhereUniqueInput + create: Prisma.XOR +} + +export type CompanyUpsertWithoutCompanyAddressesInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CompanyWhereInput +} + +export type CompanyUpdateToOneWithWhereWithoutCompanyAddressesInput = { + where?: Prisma.CompanyWhereInput + data: Prisma.XOR +} + +export type CompanyUpdateWithoutCompanyAddressesInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput + deletedBy?: Prisma.UserUpdateOneWithoutCompaniesDeletedNestedInput + enteredBy?: Prisma.UserUpdateOneWithoutCompaniesEnteredNestedInput + serviceTickets?: Prisma.ServiceTicketUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyUncheckedUpdateWithoutCompanyAddressesInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUncheckedUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput + serviceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyCreateWithoutContactsInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput + deletedBy?: Prisma.UserCreateNestedOneWithoutCompaniesDeletedInput + enteredBy?: Prisma.UserCreateNestedOneWithoutCompaniesEnteredInput + serviceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutBillingCompanyInput +} + +export type CompanyUncheckedCreateWithoutContactsInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedById?: string | null + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput + serviceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutBillingCompanyInput +} + +export type CompanyCreateOrConnectWithoutContactsInput = { + where: Prisma.CompanyWhereUniqueInput + create: Prisma.XOR +} + +export type CompanyUpsertWithoutContactsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CompanyWhereInput +} + +export type CompanyUpdateToOneWithWhereWithoutContactsInput = { + where?: Prisma.CompanyWhereInput + data: Prisma.XOR +} + +export type CompanyUpdateWithoutContactsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput + deletedBy?: Prisma.UserUpdateOneWithoutCompaniesDeletedNestedInput + enteredBy?: Prisma.UserUpdateOneWithoutCompaniesEnteredNestedInput + serviceTickets?: Prisma.ServiceTicketUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyUncheckedUpdateWithoutContactsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput + serviceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyCreateWithoutServiceTicketsInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + contacts?: Prisma.ContactCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput + deletedBy?: Prisma.UserCreateNestedOneWithoutCompaniesDeletedInput + enteredBy?: Prisma.UserCreateNestedOneWithoutCompaniesEnteredInput + billingServiceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutBillingCompanyInput +} + +export type CompanyUncheckedCreateWithoutServiceTicketsInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedById?: string | null + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + contacts?: Prisma.ContactUncheckedCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutBillingCompanyInput +} + +export type CompanyCreateOrConnectWithoutServiceTicketsInput = { + where: Prisma.CompanyWhereUniqueInput + create: Prisma.XOR +} + +export type CompanyCreateWithoutBillingServiceTicketsInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + contacts?: Prisma.ContactCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput + deletedBy?: Prisma.UserCreateNestedOneWithoutCompaniesDeletedInput + enteredBy?: Prisma.UserCreateNestedOneWithoutCompaniesEnteredInput + serviceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutCompanyInput +} + +export type CompanyUncheckedCreateWithoutBillingServiceTicketsInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedById?: string | null + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + contacts?: Prisma.ContactUncheckedCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutCompanyInput + credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput + serviceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCompanyInput +} + +export type CompanyCreateOrConnectWithoutBillingServiceTicketsInput = { + where: Prisma.CompanyWhereUniqueInput + create: Prisma.XOR +} + +export type CompanyUpsertWithoutServiceTicketsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CompanyWhereInput +} + +export type CompanyUpdateToOneWithWhereWithoutServiceTicketsInput = { + where?: Prisma.CompanyWhereInput + data: Prisma.XOR +} + +export type CompanyUpdateWithoutServiceTicketsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput + deletedBy?: Prisma.UserUpdateOneWithoutCompaniesDeletedNestedInput + enteredBy?: Prisma.UserUpdateOneWithoutCompaniesEnteredNestedInput + billingServiceTickets?: Prisma.ServiceTicketUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyUncheckedUpdateWithoutServiceTicketsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUncheckedUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyUpsertWithoutBillingServiceTicketsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CompanyWhereInput +} + +export type CompanyUpdateToOneWithWhereWithoutBillingServiceTicketsInput = { + where?: Prisma.CompanyWhereInput + data: Prisma.XOR +} + +export type CompanyUpdateWithoutBillingServiceTicketsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput + deletedBy?: Prisma.UserUpdateOneWithoutCompaniesDeletedNestedInput + enteredBy?: Prisma.UserUpdateOneWithoutCompaniesEnteredNestedInput + serviceTickets?: Prisma.ServiceTicketUpdateManyWithoutCompanyNestedInput +} + +export type CompanyUncheckedUpdateWithoutBillingServiceTicketsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUncheckedUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput + serviceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCompanyNestedInput } export type CompanyCreateWithoutOpportunitiesInput = { - id?: string + id: number + uid?: string name: string - cw_CompanyId: number - cw_Identifier: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedAt?: Date | string | null createdAt?: Date | string updatedAt?: Date | string + contacts?: Prisma.ContactCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutCompanyInput credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput + deletedBy?: Prisma.UserCreateNestedOneWithoutCompaniesDeletedInput + enteredBy?: Prisma.UserCreateNestedOneWithoutCompaniesEnteredInput + serviceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutBillingCompanyInput } export type CompanyUncheckedCreateWithoutOpportunitiesInput = { - id?: string + id: number + uid?: string name: string - cw_CompanyId: number - cw_Identifier: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedById?: string | null + deletedAt?: Date | string | null createdAt?: Date | string updatedAt?: Date | string + contacts?: Prisma.ContactUncheckedCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutCompanyInput credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput + serviceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutBillingCompanyInput } export type CompanyCreateOrConnectWithoutOpportunitiesInput = { @@ -555,47 +1579,95 @@ export type CompanyUpdateToOneWithWhereWithoutOpportunitiesInput = { } export type CompanyUpdateWithoutOpportunitiesInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string - cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number - cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutCompanyNestedInput credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput + deletedBy?: Prisma.UserUpdateOneWithoutCompaniesDeletedNestedInput + enteredBy?: Prisma.UserUpdateOneWithoutCompaniesEnteredNestedInput + serviceTickets?: Prisma.ServiceTicketUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUpdateManyWithoutBillingCompanyNestedInput } export type CompanyUncheckedUpdateWithoutOpportunitiesInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string - cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number - cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUncheckedUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutCompanyNestedInput credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput + serviceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutBillingCompanyNestedInput } export type CompanyCreateWithoutCredentialsInput = { - id?: string + id: number + uid?: string name: string - cw_CompanyId: number - cw_Identifier: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedAt?: Date | string | null createdAt?: Date | string updatedAt?: Date | string + contacts?: Prisma.ContactCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutCompanyInput unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput + deletedBy?: Prisma.UserCreateNestedOneWithoutCompaniesDeletedInput + enteredBy?: Prisma.UserCreateNestedOneWithoutCompaniesEnteredInput + serviceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketCreateNestedManyWithoutBillingCompanyInput } export type CompanyUncheckedCreateWithoutCredentialsInput = { - id?: string + id: number + uid?: string name: string - cw_CompanyId: number - cw_Identifier: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedById?: string | null + deletedAt?: Date | string | null createdAt?: Date | string updatedAt?: Date | string + contacts?: Prisma.ContactUncheckedCreateNestedManyWithoutCompanyInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutCompanyInput unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput + serviceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCompanyInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutBillingCompanyInput } export type CompanyCreateOrConnectWithoutCredentialsInput = { @@ -615,25 +1687,205 @@ export type CompanyUpdateToOneWithWhereWithoutCredentialsInput = { } export type CompanyUpdateWithoutCredentialsInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string - cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number - cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutCompanyNestedInput unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput + deletedBy?: Prisma.UserUpdateOneWithoutCompaniesDeletedNestedInput + enteredBy?: Prisma.UserUpdateOneWithoutCompaniesEnteredNestedInput + serviceTickets?: Prisma.ServiceTicketUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUpdateManyWithoutBillingCompanyNestedInput } export type CompanyUncheckedUpdateWithoutCredentialsInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string - cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number - cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUncheckedUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutCompanyNestedInput unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput + serviceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyCreateManyDeletedByInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + enteredById?: string | null + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CompanyCreateManyEnteredByInput = { + id: number + uid?: string + name: string + phone?: string | null + website?: string | null + deleteFlag?: boolean + dateDeleted?: Date | string | null + taxId?: string | null + taxExempt?: boolean + deletedById?: string | null + deletedAt?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CompanyUpdateWithoutDeletedByInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput + enteredBy?: Prisma.UserUpdateOneWithoutCompaniesEnteredNestedInput + serviceTickets?: Prisma.ServiceTicketUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyUncheckedUpdateWithoutDeletedByInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUncheckedUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput + serviceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyUncheckedUpdateManyWithoutDeletedByInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + enteredById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CompanyUpdateWithoutEnteredByInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput + deletedBy?: Prisma.UserUpdateOneWithoutCompaniesDeletedNestedInput + serviceTickets?: Prisma.ServiceTicketUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyUncheckedUpdateWithoutEnteredByInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + contacts?: Prisma.ContactUncheckedUpdateManyWithoutCompanyNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutCompanyNestedInput + credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput + serviceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCompanyNestedInput + billingServiceTickets?: Prisma.ServiceTicketUncheckedUpdateManyWithoutBillingCompanyNestedInput +} + +export type CompanyUncheckedUpdateManyWithoutEnteredByInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + website?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deleteFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + dateDeleted?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + taxId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + taxExempt?: Prisma.BoolFieldUpdateOperationsInput | boolean + deletedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + deletedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } @@ -642,15 +1894,23 @@ export type CompanyUncheckedUpdateWithoutCredentialsInput = { */ export type CompanyCountOutputType = { + contacts: number + companyAddresses: number credentials: number unifiSites: number opportunities: number + serviceTickets: number + billingServiceTickets: number } export type CompanyCountOutputTypeSelect = { + contacts?: boolean | CompanyCountOutputTypeCountContactsArgs + companyAddresses?: boolean | CompanyCountOutputTypeCountCompanyAddressesArgs credentials?: boolean | CompanyCountOutputTypeCountCredentialsArgs unifiSites?: boolean | CompanyCountOutputTypeCountUnifiSitesArgs opportunities?: boolean | CompanyCountOutputTypeCountOpportunitiesArgs + serviceTickets?: boolean | CompanyCountOutputTypeCountServiceTicketsArgs + billingServiceTickets?: boolean | CompanyCountOutputTypeCountBillingServiceTicketsArgs } /** @@ -663,6 +1923,20 @@ export type CompanyCountOutputTypeDefaultArgs | null } +/** + * CompanyCountOutputType without action + */ +export type CompanyCountOutputTypeCountContactsArgs = { + where?: Prisma.ContactWhereInput +} + +/** + * CompanyCountOutputType without action + */ +export type CompanyCountOutputTypeCountCompanyAddressesArgs = { + where?: Prisma.CompanyAddressWhereInput +} + /** * CompanyCountOutputType without action */ @@ -684,69 +1958,151 @@ export type CompanyCountOutputTypeCountOpportunitiesArgs = { + where?: Prisma.ServiceTicketWhereInput +} + +/** + * CompanyCountOutputType without action + */ +export type CompanyCountOutputTypeCountBillingServiceTicketsArgs = { + where?: Prisma.ServiceTicketWhereInput +} + export type CompanySelect = runtime.Types.Extensions.GetSelect<{ id?: boolean + uid?: boolean name?: boolean - cw_CompanyId?: boolean - cw_Identifier?: boolean + phone?: boolean + website?: boolean + deleteFlag?: boolean + dateDeleted?: boolean + taxId?: boolean + taxExempt?: boolean + enteredById?: boolean + deletedById?: boolean + deletedAt?: boolean createdAt?: boolean updatedAt?: boolean + contacts?: boolean | Prisma.Company$contactsArgs + companyAddresses?: boolean | Prisma.Company$companyAddressesArgs credentials?: boolean | Prisma.Company$credentialsArgs unifiSites?: boolean | Prisma.Company$unifiSitesArgs opportunities?: boolean | Prisma.Company$opportunitiesArgs + deletedBy?: boolean | Prisma.Company$deletedByArgs + enteredBy?: boolean | Prisma.Company$enteredByArgs + serviceTickets?: boolean | Prisma.Company$serviceTicketsArgs + billingServiceTickets?: boolean | Prisma.Company$billingServiceTicketsArgs _count?: boolean | Prisma.CompanyCountOutputTypeDefaultArgs }, ExtArgs["result"]["company"]> export type CompanySelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ id?: boolean + uid?: boolean name?: boolean - cw_CompanyId?: boolean - cw_Identifier?: boolean + phone?: boolean + website?: boolean + deleteFlag?: boolean + dateDeleted?: boolean + taxId?: boolean + taxExempt?: boolean + enteredById?: boolean + deletedById?: boolean + deletedAt?: boolean createdAt?: boolean updatedAt?: boolean + deletedBy?: boolean | Prisma.Company$deletedByArgs + enteredBy?: boolean | Prisma.Company$enteredByArgs }, ExtArgs["result"]["company"]> export type CompanySelectUpdateManyAndReturn = runtime.Types.Extensions.GetSelect<{ id?: boolean + uid?: boolean name?: boolean - cw_CompanyId?: boolean - cw_Identifier?: boolean + phone?: boolean + website?: boolean + deleteFlag?: boolean + dateDeleted?: boolean + taxId?: boolean + taxExempt?: boolean + enteredById?: boolean + deletedById?: boolean + deletedAt?: boolean createdAt?: boolean updatedAt?: boolean + deletedBy?: boolean | Prisma.Company$deletedByArgs + enteredBy?: boolean | Prisma.Company$enteredByArgs }, ExtArgs["result"]["company"]> export type CompanySelectScalar = { id?: boolean + uid?: boolean name?: boolean - cw_CompanyId?: boolean - cw_Identifier?: boolean + phone?: boolean + website?: boolean + deleteFlag?: boolean + dateDeleted?: boolean + taxId?: boolean + taxExempt?: boolean + enteredById?: boolean + deletedById?: boolean + deletedAt?: boolean createdAt?: boolean updatedAt?: boolean } -export type CompanyOmit = runtime.Types.Extensions.GetOmit<"id" | "name" | "cw_CompanyId" | "cw_Identifier" | "createdAt" | "updatedAt", ExtArgs["result"]["company"]> +export type CompanyOmit = runtime.Types.Extensions.GetOmit<"id" | "uid" | "name" | "phone" | "website" | "deleteFlag" | "dateDeleted" | "taxId" | "taxExempt" | "enteredById" | "deletedById" | "deletedAt" | "createdAt" | "updatedAt", ExtArgs["result"]["company"]> export type CompanyInclude = { + contacts?: boolean | Prisma.Company$contactsArgs + companyAddresses?: boolean | Prisma.Company$companyAddressesArgs credentials?: boolean | Prisma.Company$credentialsArgs unifiSites?: boolean | Prisma.Company$unifiSitesArgs opportunities?: boolean | Prisma.Company$opportunitiesArgs + deletedBy?: boolean | Prisma.Company$deletedByArgs + enteredBy?: boolean | Prisma.Company$enteredByArgs + serviceTickets?: boolean | Prisma.Company$serviceTicketsArgs + billingServiceTickets?: boolean | Prisma.Company$billingServiceTicketsArgs _count?: boolean | Prisma.CompanyCountOutputTypeDefaultArgs } -export type CompanyIncludeCreateManyAndReturn = {} -export type CompanyIncludeUpdateManyAndReturn = {} +export type CompanyIncludeCreateManyAndReturn = { + deletedBy?: boolean | Prisma.Company$deletedByArgs + enteredBy?: boolean | Prisma.Company$enteredByArgs +} +export type CompanyIncludeUpdateManyAndReturn = { + deletedBy?: boolean | Prisma.Company$deletedByArgs + enteredBy?: boolean | Prisma.Company$enteredByArgs +} export type $CompanyPayload = { name: "Company" objects: { + contacts: Prisma.$ContactPayload[] + companyAddresses: Prisma.$CompanyAddressPayload[] credentials: Prisma.$CredentialPayload[] unifiSites: Prisma.$UnifiSitePayload[] opportunities: Prisma.$OpportunityPayload[] + deletedBy: Prisma.$UserPayload | null + enteredBy: Prisma.$UserPayload | null + serviceTickets: Prisma.$ServiceTicketPayload[] + billingServiceTickets: Prisma.$ServiceTicketPayload[] } scalars: runtime.Types.Extensions.GetPayloadResult<{ - id: string + id: number + uid: string name: string - cw_CompanyId: number - cw_Identifier: string + phone: string | null + website: string | null + deleteFlag: boolean + dateDeleted: Date | null + taxId: string | null + taxExempt: boolean + enteredById: string | null + deletedById: string | null + deletedAt: Date | null createdAt: Date updatedAt: Date }, ExtArgs["result"]["company"]> @@ -1143,9 +2499,15 @@ readonly fields: CompanyFieldRefs; */ export interface Prisma__CompanyClient extends Prisma.PrismaPromise { readonly [Symbol.toStringTag]: "PrismaPromise" + contacts = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + companyAddresses = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> credentials = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> unifiSites = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> opportunities = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + deletedBy = {}>(args?: Prisma.Subset>): Prisma.Prisma__UserClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + enteredBy = {}>(args?: Prisma.Subset>): Prisma.Prisma__UserClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + serviceTickets = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + billingServiceTickets = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -1175,10 +2537,18 @@ export interface Prisma__CompanyClient + readonly id: Prisma.FieldRef<"Company", 'Int'> + readonly uid: Prisma.FieldRef<"Company", 'String'> readonly name: Prisma.FieldRef<"Company", 'String'> - readonly cw_CompanyId: Prisma.FieldRef<"Company", 'Int'> - readonly cw_Identifier: Prisma.FieldRef<"Company", 'String'> + readonly phone: Prisma.FieldRef<"Company", 'String'> + readonly website: Prisma.FieldRef<"Company", 'String'> + readonly deleteFlag: Prisma.FieldRef<"Company", 'Boolean'> + readonly dateDeleted: Prisma.FieldRef<"Company", 'DateTime'> + readonly taxId: Prisma.FieldRef<"Company", 'String'> + readonly taxExempt: Prisma.FieldRef<"Company", 'Boolean'> + readonly enteredById: Prisma.FieldRef<"Company", 'String'> + readonly deletedById: Prisma.FieldRef<"Company", 'String'> + readonly deletedAt: Prisma.FieldRef<"Company", 'DateTime'> readonly createdAt: Prisma.FieldRef<"Company", 'DateTime'> readonly updatedAt: Prisma.FieldRef<"Company", 'DateTime'> } @@ -1377,6 +2747,11 @@ export type CompanyFindManyArgs | null } /** @@ -1500,6 +2879,10 @@ export type CompanyUpdateManyAndReturnArgs | null } /** @@ -1568,6 +2951,54 @@ export type CompanyDeleteManyArgs = { + /** + * Select specific fields to fetch from the Contact + */ + select?: Prisma.ContactSelect | null + /** + * Omit specific fields from the Contact + */ + omit?: Prisma.ContactOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ContactInclude | null + where?: Prisma.ContactWhereInput + orderBy?: Prisma.ContactOrderByWithRelationInput | Prisma.ContactOrderByWithRelationInput[] + cursor?: Prisma.ContactWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ContactScalarFieldEnum | Prisma.ContactScalarFieldEnum[] +} + +/** + * Company.companyAddresses + */ +export type Company$companyAddressesArgs = { + /** + * Select specific fields to fetch from the CompanyAddress + */ + select?: Prisma.CompanyAddressSelect | null + /** + * Omit specific fields from the CompanyAddress + */ + omit?: Prisma.CompanyAddressOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CompanyAddressInclude | null + where?: Prisma.CompanyAddressWhereInput + orderBy?: Prisma.CompanyAddressOrderByWithRelationInput | Prisma.CompanyAddressOrderByWithRelationInput[] + cursor?: Prisma.CompanyAddressWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.CompanyAddressScalarFieldEnum | Prisma.CompanyAddressScalarFieldEnum[] +} + /** * Company.credentials */ @@ -1640,6 +3071,92 @@ export type Company$opportunitiesArgs = { + /** + * Select specific fields to fetch from the User + */ + select?: Prisma.UserSelect | null + /** + * Omit specific fields from the User + */ + omit?: Prisma.UserOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null + where?: Prisma.UserWhereInput +} + +/** + * Company.enteredBy + */ +export type Company$enteredByArgs = { + /** + * Select specific fields to fetch from the User + */ + select?: Prisma.UserSelect | null + /** + * Omit specific fields from the User + */ + omit?: Prisma.UserOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null + where?: Prisma.UserWhereInput +} + +/** + * Company.serviceTickets + */ +export type Company$serviceTicketsArgs = { + /** + * Select specific fields to fetch from the ServiceTicket + */ + select?: Prisma.ServiceTicketSelect | null + /** + * Omit specific fields from the ServiceTicket + */ + omit?: Prisma.ServiceTicketOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ServiceTicketInclude | null + where?: Prisma.ServiceTicketWhereInput + orderBy?: Prisma.ServiceTicketOrderByWithRelationInput | Prisma.ServiceTicketOrderByWithRelationInput[] + cursor?: Prisma.ServiceTicketWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ServiceTicketScalarFieldEnum | Prisma.ServiceTicketScalarFieldEnum[] +} + +/** + * Company.billingServiceTickets + */ +export type Company$billingServiceTicketsArgs = { + /** + * Select specific fields to fetch from the ServiceTicket + */ + select?: Prisma.ServiceTicketSelect | null + /** + * Omit specific fields from the ServiceTicket + */ + omit?: Prisma.ServiceTicketOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ServiceTicketInclude | null + where?: Prisma.ServiceTicketWhereInput + orderBy?: Prisma.ServiceTicketOrderByWithRelationInput | Prisma.ServiceTicketOrderByWithRelationInput[] + cursor?: Prisma.ServiceTicketWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ServiceTicketScalarFieldEnum | Prisma.ServiceTicketScalarFieldEnum[] +} + /** * Company without action */ diff --git a/api/generated/prisma/models/Credential.ts b/api/generated/prisma/models/Credential.ts index aa992c2..c3b3029 100644 --- a/api/generated/prisma/models/Credential.ts +++ b/api/generated/prisma/models/Credential.ts @@ -1806,6 +1806,11 @@ export type CredentialFindManyArgs | Date | string | null createdAt?: Prisma.DateTimeFilter<"CwMember"> | Date | string updatedAt?: Prisma.DateTimeFilter<"CwMember"> | Date | string + user?: Prisma.XOR | null } export type CwMemberOrderByWithRelationInput = { @@ -278,6 +279,7 @@ export type CwMemberOrderByWithRelationInput = { cwLastUpdated?: Prisma.SortOrderInput | Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder + user?: Prisma.UserOrderByWithRelationInput } export type CwMemberWhereUniqueInput = Prisma.AtLeast<{ @@ -295,6 +297,7 @@ export type CwMemberWhereUniqueInput = Prisma.AtLeast<{ cwLastUpdated?: Prisma.DateTimeNullableFilter<"CwMember"> | Date | string | null createdAt?: Prisma.DateTimeFilter<"CwMember"> | Date | string updatedAt?: Prisma.DateTimeFilter<"CwMember"> | Date | string + user?: Prisma.XOR | null }, "id" | "cwMemberId" | "identifier"> export type CwMemberOrderByWithAggregationInput = { @@ -345,6 +348,7 @@ export type CwMemberCreateInput = { cwLastUpdated?: Date | string | null createdAt?: Date | string updatedAt?: Date | string + user?: Prisma.UserCreateNestedOneWithoutCwMemberInput } export type CwMemberUncheckedCreateInput = { @@ -359,6 +363,7 @@ export type CwMemberUncheckedCreateInput = { cwLastUpdated?: Date | string | null createdAt?: Date | string updatedAt?: Date | string + user?: Prisma.UserUncheckedCreateNestedOneWithoutCwMemberInput } export type CwMemberUpdateInput = { @@ -373,6 +378,7 @@ export type CwMemberUpdateInput = { cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + user?: Prisma.UserUpdateOneWithoutCwMemberNestedInput } export type CwMemberUncheckedUpdateInput = { @@ -387,6 +393,7 @@ export type CwMemberUncheckedUpdateInput = { cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + user?: Prisma.UserUncheckedUpdateOneWithoutCwMemberNestedInput } export type CwMemberCreateManyInput = { @@ -431,6 +438,11 @@ export type CwMemberUncheckedUpdateManyInput = { updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } +export type CwMemberNullableScalarRelationFilter = { + is?: Prisma.CwMemberWhereInput | null + isNot?: Prisma.CwMemberWhereInput | null +} + export type CwMemberCountOrderByAggregateInput = { id?: Prisma.SortOrder cwMemberId?: Prisma.SortOrder @@ -481,6 +493,94 @@ export type CwMemberSumOrderByAggregateInput = { cwMemberId?: Prisma.SortOrder } +export type CwMemberCreateNestedOneWithoutUserInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CwMemberCreateOrConnectWithoutUserInput + connect?: Prisma.CwMemberWhereUniqueInput +} + +export type CwMemberUpdateOneWithoutUserNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CwMemberCreateOrConnectWithoutUserInput + upsert?: Prisma.CwMemberUpsertWithoutUserInput + disconnect?: Prisma.CwMemberWhereInput | boolean + delete?: Prisma.CwMemberWhereInput | boolean + connect?: Prisma.CwMemberWhereUniqueInput + update?: Prisma.XOR, Prisma.CwMemberUncheckedUpdateWithoutUserInput> +} + +export type CwMemberCreateWithoutUserInput = { + id?: string + cwMemberId: number + identifier: string + firstName: string + lastName: string + officeEmail?: string | null + inactiveFlag?: boolean + apiKey?: string | null + cwLastUpdated?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CwMemberUncheckedCreateWithoutUserInput = { + id?: string + cwMemberId: number + identifier: string + firstName: string + lastName: string + officeEmail?: string | null + inactiveFlag?: boolean + apiKey?: string | null + cwLastUpdated?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type CwMemberCreateOrConnectWithoutUserInput = { + where: Prisma.CwMemberWhereUniqueInput + create: Prisma.XOR +} + +export type CwMemberUpsertWithoutUserInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CwMemberWhereInput +} + +export type CwMemberUpdateToOneWithWhereWithoutUserInput = { + where?: Prisma.CwMemberWhereInput + data: Prisma.XOR +} + +export type CwMemberUpdateWithoutUserInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + cwMemberId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.StringFieldUpdateOperationsInput | string + lastName?: Prisma.StringFieldUpdateOperationsInput | string + officeEmail?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + inactiveFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + apiKey?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type CwMemberUncheckedUpdateWithoutUserInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + cwMemberId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.StringFieldUpdateOperationsInput | string + lastName?: Prisma.StringFieldUpdateOperationsInput | string + officeEmail?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + inactiveFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + apiKey?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + export type CwMemberSelect = runtime.Types.Extensions.GetSelect<{ @@ -495,6 +595,7 @@ export type CwMemberSelect }, ExtArgs["result"]["cwMember"]> export type CwMemberSelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ @@ -540,10 +641,17 @@ export type CwMemberSelectScalar = { } export type CwMemberOmit = runtime.Types.Extensions.GetOmit<"id" | "cwMemberId" | "identifier" | "firstName" | "lastName" | "officeEmail" | "inactiveFlag" | "apiKey" | "cwLastUpdated" | "createdAt" | "updatedAt", ExtArgs["result"]["cwMember"]> +export type CwMemberInclude = { + user?: boolean | Prisma.CwMember$userArgs +} +export type CwMemberIncludeCreateManyAndReturn = {} +export type CwMemberIncludeUpdateManyAndReturn = {} export type $CwMemberPayload = { name: "CwMember" - objects: {} + objects: { + user: Prisma.$UserPayload | null + } scalars: runtime.Types.Extensions.GetPayloadResult<{ id: string cwMemberId: number @@ -950,6 +1058,7 @@ readonly fields: CwMemberFieldRefs; */ export interface Prisma__CwMemberClient extends Prisma.PrismaPromise { readonly [Symbol.toStringTag]: "PrismaPromise" + user = {}>(args?: Prisma.Subset>): Prisma.Prisma__UserClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -1006,6 +1115,10 @@ export type CwMemberFindUniqueArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null /** * Filter, which CwMember to fetch. */ @@ -1024,6 +1137,10 @@ export type CwMemberFindUniqueOrThrowArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null /** * Filter, which CwMember to fetch. */ @@ -1042,6 +1159,10 @@ export type CwMemberFindFirstArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null /** * Filter, which CwMember to fetch. */ @@ -1090,6 +1211,10 @@ export type CwMemberFindFirstOrThrowArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null /** * Filter, which CwMember to fetch. */ @@ -1138,6 +1263,10 @@ export type CwMemberFindManyArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null /** * Filter, which CwMembers to fetch. */ @@ -1166,6 +1295,11 @@ export type CwMemberFindManyArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null /** * The data needed to create a CwMember. */ @@ -1229,6 +1367,10 @@ export type CwMemberUpdateArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null /** * The data needed to update a CwMember. */ @@ -1295,6 +1437,10 @@ export type CwMemberUpsertArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null /** * The filter to search for the CwMember to update in case it exists. */ @@ -1321,6 +1467,10 @@ export type CwMemberDeleteArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null /** * Filter which CwMember to delete. */ @@ -1341,6 +1491,25 @@ export type CwMemberDeleteManyArgs = { + /** + * Select specific fields to fetch from the User + */ + select?: Prisma.UserSelect | null + /** + * Omit specific fields from the User + */ + omit?: Prisma.UserOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null + where?: Prisma.UserWhereInput +} + /** * CwMember without action */ @@ -1353,4 +1522,8 @@ export type CwMemberDefaultArgs | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null } diff --git a/api/generated/prisma/models/GeneratedQuotes.ts b/api/generated/prisma/models/GeneratedQuotes.ts index 6fad6b9..737aa52 100644 --- a/api/generated/prisma/models/GeneratedQuotes.ts +++ b/api/generated/prisma/models/GeneratedQuotes.ts @@ -1474,6 +1474,11 @@ export type GeneratedQuotesFindManyArgs | string - cwOpportunityId?: Prisma.IntFilter<"Opportunity"> | number + id?: Prisma.IntFilter<"Opportunity"> | number + uid?: Prisma.StringFilter<"Opportunity"> | string name?: Prisma.StringFilter<"Opportunity"> | string notes?: Prisma.StringNullableFilter<"Opportunity"> | string | null - typeName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - typeCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - stageName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - stageCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - statusName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - statusCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - priorityName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - priorityCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - ratingName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - ratingCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - source?: Prisma.StringNullableFilter<"Opportunity"> | string | null - campaignName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - campaignCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - primarySalesRepName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - primarySalesRepIdentifier?: Prisma.StringNullableFilter<"Opportunity"> | string | null - primarySalesRepCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - secondarySalesRepName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - secondarySalesRepIdentifier?: Prisma.StringNullableFilter<"Opportunity"> | string | null - secondarySalesRepCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - companyCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - companyName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - contactCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - contactName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - siteCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - siteName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - customerPO?: Prisma.StringNullableFilter<"Opportunity"> | string | null - totalSalesTax?: Prisma.FloatFilter<"Opportunity"> | number + oppNarrative?: Prisma.StringNullableFilter<"Opportunity"> | string | null + typeId?: Prisma.IntFilter<"Opportunity"> | number + stageId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + statusId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + taxCodeId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + interest?: Prisma.EnumOpportunityInterestNullableFilter<"Opportunity"> | $Enums.OpportunityInterest | null probability?: Prisma.FloatFilter<"Opportunity"> | number - locationName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - locationCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - departmentName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - departmentCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + source?: Prisma.StringNullableFilter<"Opportunity"> | string | null + primarySalesRepId?: Prisma.StringNullableFilter<"Opportunity"> | string | null + secondarySalesRepId?: Prisma.StringNullableFilter<"Opportunity"> | string | null + companyId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + contactId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + siteId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + customerPO?: Prisma.StringNullableFilter<"Opportunity"> | string | null + locationId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + departmentId?: Prisma.IntNullableFilter<"Opportunity"> | number | null expectedCloseDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null pipelineChangeDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null dateBecameLead?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null closedDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null closedFlag?: Prisma.BoolFilter<"Opportunity"> | boolean - closedByName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - closedByCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - companyId?: Prisma.StringNullableFilter<"Opportunity"> | string | null + closedById?: Prisma.StringNullableFilter<"Opportunity"> | string | null productSequence?: Prisma.IntNullableListFilter<"Opportunity"> - cwLastUpdated?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null - cwDateEntered?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null + updatedBy?: Prisma.StringFilter<"Opportunity"> | string + eneteredBy?: Prisma.StringFilter<"Opportunity"> | string createdAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string updatedAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string generatedQuotes?: Prisma.GeneratedQuotesListRelationFilter + type?: Prisma.XOR + stage?: Prisma.XOR | null + status?: Prisma.XOR | null + taxCode?: Prisma.XOR | null + primarySalesRep?: Prisma.XOR | null + secondarySalesRep?: Prisma.XOR | null company?: Prisma.XOR | null + contact?: Prisma.XOR | null + site?: Prisma.XOR | null + location?: Prisma.XOR | null + department?: Prisma.XOR | null + products?: Prisma.ProductDataListRelationFilter } export type OpportunityOrderByWithRelationInput = { id?: Prisma.SortOrder - cwOpportunityId?: Prisma.SortOrder + uid?: Prisma.SortOrder name?: Prisma.SortOrder notes?: Prisma.SortOrderInput | Prisma.SortOrder - typeName?: Prisma.SortOrderInput | Prisma.SortOrder - typeCwId?: Prisma.SortOrderInput | Prisma.SortOrder - stageName?: Prisma.SortOrderInput | Prisma.SortOrder - stageCwId?: Prisma.SortOrderInput | Prisma.SortOrder - statusName?: Prisma.SortOrderInput | Prisma.SortOrder - statusCwId?: Prisma.SortOrderInput | Prisma.SortOrder - priorityName?: Prisma.SortOrderInput | Prisma.SortOrder - priorityCwId?: Prisma.SortOrderInput | Prisma.SortOrder - ratingName?: Prisma.SortOrderInput | Prisma.SortOrder - ratingCwId?: Prisma.SortOrderInput | Prisma.SortOrder - source?: Prisma.SortOrderInput | Prisma.SortOrder - campaignName?: Prisma.SortOrderInput | Prisma.SortOrder - campaignCwId?: Prisma.SortOrderInput | Prisma.SortOrder - primarySalesRepName?: Prisma.SortOrderInput | Prisma.SortOrder - primarySalesRepIdentifier?: Prisma.SortOrderInput | Prisma.SortOrder - primarySalesRepCwId?: Prisma.SortOrderInput | Prisma.SortOrder - secondarySalesRepName?: Prisma.SortOrderInput | Prisma.SortOrder - secondarySalesRepIdentifier?: Prisma.SortOrderInput | Prisma.SortOrder - secondarySalesRepCwId?: Prisma.SortOrderInput | Prisma.SortOrder - companyCwId?: Prisma.SortOrderInput | Prisma.SortOrder - companyName?: Prisma.SortOrderInput | Prisma.SortOrder - contactCwId?: Prisma.SortOrderInput | Prisma.SortOrder - contactName?: Prisma.SortOrderInput | Prisma.SortOrder - siteCwId?: Prisma.SortOrderInput | Prisma.SortOrder - siteName?: Prisma.SortOrderInput | Prisma.SortOrder - customerPO?: Prisma.SortOrderInput | Prisma.SortOrder - totalSalesTax?: Prisma.SortOrder + oppNarrative?: Prisma.SortOrderInput | Prisma.SortOrder + typeId?: Prisma.SortOrder + stageId?: Prisma.SortOrderInput | Prisma.SortOrder + statusId?: Prisma.SortOrderInput | Prisma.SortOrder + taxCodeId?: Prisma.SortOrderInput | Prisma.SortOrder + interest?: Prisma.SortOrderInput | Prisma.SortOrder probability?: Prisma.SortOrder - locationName?: Prisma.SortOrderInput | Prisma.SortOrder - locationCwId?: Prisma.SortOrderInput | Prisma.SortOrder - departmentName?: Prisma.SortOrderInput | Prisma.SortOrder - departmentCwId?: Prisma.SortOrderInput | Prisma.SortOrder + source?: Prisma.SortOrderInput | Prisma.SortOrder + primarySalesRepId?: Prisma.SortOrderInput | Prisma.SortOrder + secondarySalesRepId?: Prisma.SortOrderInput | Prisma.SortOrder + companyId?: Prisma.SortOrderInput | Prisma.SortOrder + contactId?: Prisma.SortOrderInput | Prisma.SortOrder + siteId?: Prisma.SortOrderInput | Prisma.SortOrder + customerPO?: Prisma.SortOrderInput | Prisma.SortOrder + locationId?: Prisma.SortOrderInput | Prisma.SortOrder + departmentId?: Prisma.SortOrderInput | Prisma.SortOrder expectedCloseDate?: Prisma.SortOrderInput | Prisma.SortOrder pipelineChangeDate?: Prisma.SortOrderInput | Prisma.SortOrder dateBecameLead?: Prisma.SortOrderInput | Prisma.SortOrder closedDate?: Prisma.SortOrderInput | Prisma.SortOrder closedFlag?: Prisma.SortOrder - closedByName?: Prisma.SortOrderInput | Prisma.SortOrder - closedByCwId?: Prisma.SortOrderInput | Prisma.SortOrder - companyId?: Prisma.SortOrderInput | Prisma.SortOrder + closedById?: Prisma.SortOrderInput | Prisma.SortOrder productSequence?: Prisma.SortOrder - cwLastUpdated?: Prisma.SortOrderInput | Prisma.SortOrder - cwDateEntered?: Prisma.SortOrderInput | Prisma.SortOrder + updatedBy?: Prisma.SortOrder + eneteredBy?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder generatedQuotes?: Prisma.GeneratedQuotesOrderByRelationAggregateInput + type?: Prisma.OpportunityTypeOrderByWithRelationInput + stage?: Prisma.OpportunityStageOrderByWithRelationInput + status?: Prisma.OpportunityStatusOrderByWithRelationInput + taxCode?: Prisma.TaxCodeOrderByWithRelationInput + primarySalesRep?: Prisma.UserOrderByWithRelationInput + secondarySalesRep?: Prisma.UserOrderByWithRelationInput company?: Prisma.CompanyOrderByWithRelationInput + contact?: Prisma.ContactOrderByWithRelationInput + site?: Prisma.CompanyAddressOrderByWithRelationInput + location?: Prisma.CorporateLocationOrderByWithRelationInput + department?: Prisma.InternalDepartmentOrderByWithRelationInput + products?: Prisma.ProductDataOrderByRelationAggregateInput } export type OpportunityWhereUniqueInput = Prisma.AtLeast<{ - id?: string - cwOpportunityId?: number + id?: number + uid?: string AND?: Prisma.OpportunityWhereInput | Prisma.OpportunityWhereInput[] OR?: Prisma.OpportunityWhereInput[] NOT?: Prisma.OpportunityWhereInput | Prisma.OpportunityWhereInput[] name?: Prisma.StringFilter<"Opportunity"> | string notes?: Prisma.StringNullableFilter<"Opportunity"> | string | null - typeName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - typeCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - stageName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - stageCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - statusName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - statusCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - priorityName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - priorityCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - ratingName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - ratingCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - source?: Prisma.StringNullableFilter<"Opportunity"> | string | null - campaignName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - campaignCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - primarySalesRepName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - primarySalesRepIdentifier?: Prisma.StringNullableFilter<"Opportunity"> | string | null - primarySalesRepCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - secondarySalesRepName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - secondarySalesRepIdentifier?: Prisma.StringNullableFilter<"Opportunity"> | string | null - secondarySalesRepCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - companyCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - companyName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - contactCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - contactName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - siteCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - siteName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - customerPO?: Prisma.StringNullableFilter<"Opportunity"> | string | null - totalSalesTax?: Prisma.FloatFilter<"Opportunity"> | number + oppNarrative?: Prisma.StringNullableFilter<"Opportunity"> | string | null + typeId?: Prisma.IntFilter<"Opportunity"> | number + stageId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + statusId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + taxCodeId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + interest?: Prisma.EnumOpportunityInterestNullableFilter<"Opportunity"> | $Enums.OpportunityInterest | null probability?: Prisma.FloatFilter<"Opportunity"> | number - locationName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - locationCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - departmentName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - departmentCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + source?: Prisma.StringNullableFilter<"Opportunity"> | string | null + primarySalesRepId?: Prisma.StringNullableFilter<"Opportunity"> | string | null + secondarySalesRepId?: Prisma.StringNullableFilter<"Opportunity"> | string | null + companyId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + contactId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + siteId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + customerPO?: Prisma.StringNullableFilter<"Opportunity"> | string | null + locationId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + departmentId?: Prisma.IntNullableFilter<"Opportunity"> | number | null expectedCloseDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null pipelineChangeDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null dateBecameLead?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null closedDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null closedFlag?: Prisma.BoolFilter<"Opportunity"> | boolean - closedByName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - closedByCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - companyId?: Prisma.StringNullableFilter<"Opportunity"> | string | null + closedById?: Prisma.StringNullableFilter<"Opportunity"> | string | null productSequence?: Prisma.IntNullableListFilter<"Opportunity"> - cwLastUpdated?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null - cwDateEntered?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null + updatedBy?: Prisma.StringFilter<"Opportunity"> | string + eneteredBy?: Prisma.StringFilter<"Opportunity"> | string createdAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string updatedAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string generatedQuotes?: Prisma.GeneratedQuotesListRelationFilter + type?: Prisma.XOR + stage?: Prisma.XOR | null + status?: Prisma.XOR | null + taxCode?: Prisma.XOR | null + primarySalesRep?: Prisma.XOR | null + secondarySalesRep?: Prisma.XOR | null company?: Prisma.XOR | null -}, "id" | "cwOpportunityId"> + contact?: Prisma.XOR | null + site?: Prisma.XOR | null + location?: Prisma.XOR | null + department?: Prisma.XOR | null + products?: Prisma.ProductDataListRelationFilter +}, "uid" | "id"> export type OpportunityOrderByWithAggregationInput = { id?: Prisma.SortOrder - cwOpportunityId?: Prisma.SortOrder + uid?: Prisma.SortOrder name?: Prisma.SortOrder notes?: Prisma.SortOrderInput | Prisma.SortOrder - typeName?: Prisma.SortOrderInput | Prisma.SortOrder - typeCwId?: Prisma.SortOrderInput | Prisma.SortOrder - stageName?: Prisma.SortOrderInput | Prisma.SortOrder - stageCwId?: Prisma.SortOrderInput | Prisma.SortOrder - statusName?: Prisma.SortOrderInput | Prisma.SortOrder - statusCwId?: Prisma.SortOrderInput | Prisma.SortOrder - priorityName?: Prisma.SortOrderInput | Prisma.SortOrder - priorityCwId?: Prisma.SortOrderInput | Prisma.SortOrder - ratingName?: Prisma.SortOrderInput | Prisma.SortOrder - ratingCwId?: Prisma.SortOrderInput | Prisma.SortOrder - source?: Prisma.SortOrderInput | Prisma.SortOrder - campaignName?: Prisma.SortOrderInput | Prisma.SortOrder - campaignCwId?: Prisma.SortOrderInput | Prisma.SortOrder - primarySalesRepName?: Prisma.SortOrderInput | Prisma.SortOrder - primarySalesRepIdentifier?: Prisma.SortOrderInput | Prisma.SortOrder - primarySalesRepCwId?: Prisma.SortOrderInput | Prisma.SortOrder - secondarySalesRepName?: Prisma.SortOrderInput | Prisma.SortOrder - secondarySalesRepIdentifier?: Prisma.SortOrderInput | Prisma.SortOrder - secondarySalesRepCwId?: Prisma.SortOrderInput | Prisma.SortOrder - companyCwId?: Prisma.SortOrderInput | Prisma.SortOrder - companyName?: Prisma.SortOrderInput | Prisma.SortOrder - contactCwId?: Prisma.SortOrderInput | Prisma.SortOrder - contactName?: Prisma.SortOrderInput | Prisma.SortOrder - siteCwId?: Prisma.SortOrderInput | Prisma.SortOrder - siteName?: Prisma.SortOrderInput | Prisma.SortOrder - customerPO?: Prisma.SortOrderInput | Prisma.SortOrder - totalSalesTax?: Prisma.SortOrder + oppNarrative?: Prisma.SortOrderInput | Prisma.SortOrder + typeId?: Prisma.SortOrder + stageId?: Prisma.SortOrderInput | Prisma.SortOrder + statusId?: Prisma.SortOrderInput | Prisma.SortOrder + taxCodeId?: Prisma.SortOrderInput | Prisma.SortOrder + interest?: Prisma.SortOrderInput | Prisma.SortOrder probability?: Prisma.SortOrder - locationName?: Prisma.SortOrderInput | Prisma.SortOrder - locationCwId?: Prisma.SortOrderInput | Prisma.SortOrder - departmentName?: Prisma.SortOrderInput | Prisma.SortOrder - departmentCwId?: Prisma.SortOrderInput | Prisma.SortOrder + source?: Prisma.SortOrderInput | Prisma.SortOrder + primarySalesRepId?: Prisma.SortOrderInput | Prisma.SortOrder + secondarySalesRepId?: Prisma.SortOrderInput | Prisma.SortOrder + companyId?: Prisma.SortOrderInput | Prisma.SortOrder + contactId?: Prisma.SortOrderInput | Prisma.SortOrder + siteId?: Prisma.SortOrderInput | Prisma.SortOrder + customerPO?: Prisma.SortOrderInput | Prisma.SortOrder + locationId?: Prisma.SortOrderInput | Prisma.SortOrder + departmentId?: Prisma.SortOrderInput | Prisma.SortOrder expectedCloseDate?: Prisma.SortOrderInput | Prisma.SortOrder pipelineChangeDate?: Prisma.SortOrderInput | Prisma.SortOrder dateBecameLead?: Prisma.SortOrderInput | Prisma.SortOrder closedDate?: Prisma.SortOrderInput | Prisma.SortOrder closedFlag?: Prisma.SortOrder - closedByName?: Prisma.SortOrderInput | Prisma.SortOrder - closedByCwId?: Prisma.SortOrderInput | Prisma.SortOrder - companyId?: Prisma.SortOrderInput | Prisma.SortOrder + closedById?: Prisma.SortOrderInput | Prisma.SortOrder productSequence?: Prisma.SortOrder - cwLastUpdated?: Prisma.SortOrderInput | Prisma.SortOrder - cwDateEntered?: Prisma.SortOrderInput | Prisma.SortOrder + updatedBy?: Prisma.SortOrder + eneteredBy?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder _count?: Prisma.OpportunityCountOrderByAggregateInput @@ -808,420 +619,270 @@ export type OpportunityScalarWhereWithAggregatesInput = { AND?: Prisma.OpportunityScalarWhereWithAggregatesInput | Prisma.OpportunityScalarWhereWithAggregatesInput[] OR?: Prisma.OpportunityScalarWhereWithAggregatesInput[] NOT?: Prisma.OpportunityScalarWhereWithAggregatesInput | Prisma.OpportunityScalarWhereWithAggregatesInput[] - id?: Prisma.StringWithAggregatesFilter<"Opportunity"> | string - cwOpportunityId?: Prisma.IntWithAggregatesFilter<"Opportunity"> | number + id?: Prisma.IntWithAggregatesFilter<"Opportunity"> | number + uid?: Prisma.StringWithAggregatesFilter<"Opportunity"> | string name?: Prisma.StringWithAggregatesFilter<"Opportunity"> | string notes?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - typeName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - typeCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - stageName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - stageCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - statusName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - statusCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - priorityName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - priorityCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - ratingName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - ratingCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - source?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - campaignName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - campaignCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - primarySalesRepName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - primarySalesRepIdentifier?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - primarySalesRepCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - secondarySalesRepName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - secondarySalesRepIdentifier?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - secondarySalesRepCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - companyCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - companyName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - contactCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - contactName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - siteCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - siteName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - customerPO?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - totalSalesTax?: Prisma.FloatWithAggregatesFilter<"Opportunity"> | number + oppNarrative?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null + typeId?: Prisma.IntWithAggregatesFilter<"Opportunity"> | number + stageId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null + statusId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null + taxCodeId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null + interest?: Prisma.EnumOpportunityInterestNullableWithAggregatesFilter<"Opportunity"> | $Enums.OpportunityInterest | null probability?: Prisma.FloatWithAggregatesFilter<"Opportunity"> | number - locationName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - locationCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - departmentName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - departmentCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null + source?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null + primarySalesRepId?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null + secondarySalesRepId?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null + companyId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null + contactId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null + siteId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null + customerPO?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null + locationId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null + departmentId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null expectedCloseDate?: Prisma.DateTimeNullableWithAggregatesFilter<"Opportunity"> | Date | string | null pipelineChangeDate?: Prisma.DateTimeNullableWithAggregatesFilter<"Opportunity"> | Date | string | null dateBecameLead?: Prisma.DateTimeNullableWithAggregatesFilter<"Opportunity"> | Date | string | null closedDate?: Prisma.DateTimeNullableWithAggregatesFilter<"Opportunity"> | Date | string | null closedFlag?: Prisma.BoolWithAggregatesFilter<"Opportunity"> | boolean - closedByName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null - closedByCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null - companyId?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null + closedById?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null productSequence?: Prisma.IntNullableListFilter<"Opportunity"> - cwLastUpdated?: Prisma.DateTimeNullableWithAggregatesFilter<"Opportunity"> | Date | string | null - cwDateEntered?: Prisma.DateTimeNullableWithAggregatesFilter<"Opportunity"> | Date | string | null + updatedBy?: Prisma.StringWithAggregatesFilter<"Opportunity"> | string + eneteredBy?: Prisma.StringWithAggregatesFilter<"Opportunity"> | string createdAt?: Prisma.DateTimeWithAggregatesFilter<"Opportunity"> | Date | string updatedAt?: Prisma.DateTimeWithAggregatesFilter<"Opportunity"> | Date | string } export type OpportunityCreateInput = { - id?: string - cwOpportunityId: number + id: number + uid?: string name: string notes?: string | null - typeName?: string | null - typeCwId?: number | null - stageName?: string | null - stageCwId?: number | null - statusName?: string | null - statusCwId?: number | null - priorityName?: string | null - priorityCwId?: number | null - ratingName?: string | null - ratingCwId?: number | null - source?: string | null - campaignName?: string | null - campaignCwId?: number | null - primarySalesRepName?: string | null - primarySalesRepIdentifier?: string | null - primarySalesRepCwId?: number | null - secondarySalesRepName?: string | null - secondarySalesRepIdentifier?: string | null - secondarySalesRepCwId?: number | null - companyCwId?: number | null - companyName?: string | null - contactCwId?: number | null - contactName?: string | null - siteCwId?: number | null - siteName?: string | null - customerPO?: string | null - totalSalesTax?: number + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null probability?: number - locationName?: string | null - locationCwId?: number | null - departmentName?: string | null - departmentCwId?: number | null + source?: string | null + customerPO?: string | null expectedCloseDate?: Date | string | null pipelineChangeDate?: Date | string | null dateBecameLead?: Date | string | null closedDate?: Date | string | null closedFlag?: boolean - closedByName?: string | null - closedByCwId?: number | null + closedById?: string | null productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] - cwLastUpdated?: Date | string | null - cwDateEntered?: Date | string | null + updatedBy: string + eneteredBy: string createdAt?: Date | string updatedAt?: Date | string generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput } export type OpportunityUncheckedCreateInput = { - id?: string - cwOpportunityId: number + id: number + uid?: string name: string notes?: string | null - typeName?: string | null - typeCwId?: number | null - stageName?: string | null - stageCwId?: number | null - statusName?: string | null - statusCwId?: number | null - priorityName?: string | null - priorityCwId?: number | null - ratingName?: string | null - ratingCwId?: number | null - source?: string | null - campaignName?: string | null - campaignCwId?: number | null - primarySalesRepName?: string | null - primarySalesRepIdentifier?: string | null - primarySalesRepCwId?: number | null - secondarySalesRepName?: string | null - secondarySalesRepIdentifier?: string | null - secondarySalesRepCwId?: number | null - companyCwId?: number | null - companyName?: string | null - contactCwId?: number | null - contactName?: string | null - siteCwId?: number | null - siteName?: string | null - customerPO?: string | null - totalSalesTax?: number + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null probability?: number - locationName?: string | null - locationCwId?: number | null - departmentName?: string | null - departmentCwId?: number | null + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null expectedCloseDate?: Date | string | null pipelineChangeDate?: Date | string | null dateBecameLead?: Date | string | null closedDate?: Date | string | null closedFlag?: boolean - closedByName?: string | null - closedByCwId?: number | null - companyId?: string | null + closedById?: string | null productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] - cwLastUpdated?: Date | string | null - cwDateEntered?: Date | string | null + updatedBy: string + eneteredBy: string createdAt?: Date | string updatedAt?: Date | string generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput } export type OpportunityUpdateInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - stageName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - stageCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - statusName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - statusCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - priorityName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - priorityCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - ratingName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - ratingCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - primarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - secondarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - contactCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - contactName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - siteCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - siteName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - totalSalesTax?: Prisma.FloatFieldUpdateOperationsInput | number + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null probability?: Prisma.FloatFieldUpdateOperationsInput | number - locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - departmentCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean - closedByName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - closedByCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] - cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null - cwDateEntered?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput } export type OpportunityUncheckedUpdateInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - stageName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - stageCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - statusName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - statusCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - priorityName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - priorityCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - ratingName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - ratingCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - primarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - secondarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - contactCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - contactName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - siteCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - siteName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - totalSalesTax?: Prisma.FloatFieldUpdateOperationsInput | number + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null probability?: Prisma.FloatFieldUpdateOperationsInput | number - locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - departmentCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean - closedByName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - closedByCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] - cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null - cwDateEntered?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput } export type OpportunityCreateManyInput = { - id?: string - cwOpportunityId: number + id: number + uid?: string name: string notes?: string | null - typeName?: string | null - typeCwId?: number | null - stageName?: string | null - stageCwId?: number | null - statusName?: string | null - statusCwId?: number | null - priorityName?: string | null - priorityCwId?: number | null - ratingName?: string | null - ratingCwId?: number | null - source?: string | null - campaignName?: string | null - campaignCwId?: number | null - primarySalesRepName?: string | null - primarySalesRepIdentifier?: string | null - primarySalesRepCwId?: number | null - secondarySalesRepName?: string | null - secondarySalesRepIdentifier?: string | null - secondarySalesRepCwId?: number | null - companyCwId?: number | null - companyName?: string | null - contactCwId?: number | null - contactName?: string | null - siteCwId?: number | null - siteName?: string | null - customerPO?: string | null - totalSalesTax?: number + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null probability?: number - locationName?: string | null - locationCwId?: number | null - departmentName?: string | null - departmentCwId?: number | null + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null expectedCloseDate?: Date | string | null pipelineChangeDate?: Date | string | null dateBecameLead?: Date | string | null closedDate?: Date | string | null closedFlag?: boolean - closedByName?: string | null - closedByCwId?: number | null - companyId?: string | null + closedById?: string | null productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] - cwLastUpdated?: Date | string | null - cwDateEntered?: Date | string | null + updatedBy: string + eneteredBy: string createdAt?: Date | string updatedAt?: Date | string } export type OpportunityUpdateManyMutationInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - stageName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - stageCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - statusName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - statusCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - priorityName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - priorityCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - ratingName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - ratingCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - primarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - secondarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - contactCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - contactName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - siteCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - siteName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - totalSalesTax?: Prisma.FloatFieldUpdateOperationsInput | number + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null probability?: Prisma.FloatFieldUpdateOperationsInput | number - locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - departmentCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean - closedByName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - closedByCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] - cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null - cwDateEntered?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } export type OpportunityUncheckedUpdateManyInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - stageName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - stageCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - statusName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - statusCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - priorityName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - priorityCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - ratingName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - ratingCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - primarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - secondarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - contactCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - contactName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - siteCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - siteName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - totalSalesTax?: Prisma.FloatFieldUpdateOperationsInput | number + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null probability?: Prisma.FloatFieldUpdateOperationsInput | number - locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - departmentCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean - closedByName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - closedByCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] - cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null - cwDateEntered?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } @@ -1236,6 +897,11 @@ export type OpportunityOrderByRelationAggregateInput = { _count?: Prisma.SortOrder } +export type OpportunityNullableScalarRelationFilter = { + is?: Prisma.OpportunityWhereInput | null + isNot?: Prisma.OpportunityWhereInput | null +} + export type IntNullableListFilter<$PrismaModel = never> = { equals?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null has?: number | Prisma.IntFieldRefInput<$PrismaModel> | null @@ -1246,197 +912,131 @@ export type IntNullableListFilter<$PrismaModel = never> = { export type OpportunityCountOrderByAggregateInput = { id?: Prisma.SortOrder - cwOpportunityId?: Prisma.SortOrder + uid?: Prisma.SortOrder name?: Prisma.SortOrder notes?: Prisma.SortOrder - typeName?: Prisma.SortOrder - typeCwId?: Prisma.SortOrder - stageName?: Prisma.SortOrder - stageCwId?: Prisma.SortOrder - statusName?: Prisma.SortOrder - statusCwId?: Prisma.SortOrder - priorityName?: Prisma.SortOrder - priorityCwId?: Prisma.SortOrder - ratingName?: Prisma.SortOrder - ratingCwId?: Prisma.SortOrder - source?: Prisma.SortOrder - campaignName?: Prisma.SortOrder - campaignCwId?: Prisma.SortOrder - primarySalesRepName?: Prisma.SortOrder - primarySalesRepIdentifier?: Prisma.SortOrder - primarySalesRepCwId?: Prisma.SortOrder - secondarySalesRepName?: Prisma.SortOrder - secondarySalesRepIdentifier?: Prisma.SortOrder - secondarySalesRepCwId?: Prisma.SortOrder - companyCwId?: Prisma.SortOrder - companyName?: Prisma.SortOrder - contactCwId?: Prisma.SortOrder - contactName?: Prisma.SortOrder - siteCwId?: Prisma.SortOrder - siteName?: Prisma.SortOrder - customerPO?: Prisma.SortOrder - totalSalesTax?: Prisma.SortOrder + oppNarrative?: Prisma.SortOrder + typeId?: Prisma.SortOrder + stageId?: Prisma.SortOrder + statusId?: Prisma.SortOrder + taxCodeId?: Prisma.SortOrder + interest?: Prisma.SortOrder probability?: Prisma.SortOrder - locationName?: Prisma.SortOrder - locationCwId?: Prisma.SortOrder - departmentName?: Prisma.SortOrder - departmentCwId?: Prisma.SortOrder + source?: Prisma.SortOrder + primarySalesRepId?: Prisma.SortOrder + secondarySalesRepId?: Prisma.SortOrder + companyId?: Prisma.SortOrder + contactId?: Prisma.SortOrder + siteId?: Prisma.SortOrder + customerPO?: Prisma.SortOrder + locationId?: Prisma.SortOrder + departmentId?: Prisma.SortOrder expectedCloseDate?: Prisma.SortOrder pipelineChangeDate?: Prisma.SortOrder dateBecameLead?: Prisma.SortOrder closedDate?: Prisma.SortOrder closedFlag?: Prisma.SortOrder - closedByName?: Prisma.SortOrder - closedByCwId?: Prisma.SortOrder - companyId?: Prisma.SortOrder + closedById?: Prisma.SortOrder productSequence?: Prisma.SortOrder - cwLastUpdated?: Prisma.SortOrder - cwDateEntered?: Prisma.SortOrder + updatedBy?: Prisma.SortOrder + eneteredBy?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } export type OpportunityAvgOrderByAggregateInput = { - cwOpportunityId?: Prisma.SortOrder - typeCwId?: Prisma.SortOrder - stageCwId?: Prisma.SortOrder - statusCwId?: Prisma.SortOrder - priorityCwId?: Prisma.SortOrder - ratingCwId?: Prisma.SortOrder - campaignCwId?: Prisma.SortOrder - primarySalesRepCwId?: Prisma.SortOrder - secondarySalesRepCwId?: Prisma.SortOrder - companyCwId?: Prisma.SortOrder - contactCwId?: Prisma.SortOrder - siteCwId?: Prisma.SortOrder - totalSalesTax?: Prisma.SortOrder + id?: Prisma.SortOrder + typeId?: Prisma.SortOrder + stageId?: Prisma.SortOrder + statusId?: Prisma.SortOrder + taxCodeId?: Prisma.SortOrder probability?: Prisma.SortOrder - locationCwId?: Prisma.SortOrder - departmentCwId?: Prisma.SortOrder - closedByCwId?: Prisma.SortOrder + companyId?: Prisma.SortOrder + contactId?: Prisma.SortOrder + siteId?: Prisma.SortOrder + locationId?: Prisma.SortOrder + departmentId?: Prisma.SortOrder productSequence?: Prisma.SortOrder } export type OpportunityMaxOrderByAggregateInput = { id?: Prisma.SortOrder - cwOpportunityId?: Prisma.SortOrder + uid?: Prisma.SortOrder name?: Prisma.SortOrder notes?: Prisma.SortOrder - typeName?: Prisma.SortOrder - typeCwId?: Prisma.SortOrder - stageName?: Prisma.SortOrder - stageCwId?: Prisma.SortOrder - statusName?: Prisma.SortOrder - statusCwId?: Prisma.SortOrder - priorityName?: Prisma.SortOrder - priorityCwId?: Prisma.SortOrder - ratingName?: Prisma.SortOrder - ratingCwId?: Prisma.SortOrder - source?: Prisma.SortOrder - campaignName?: Prisma.SortOrder - campaignCwId?: Prisma.SortOrder - primarySalesRepName?: Prisma.SortOrder - primarySalesRepIdentifier?: Prisma.SortOrder - primarySalesRepCwId?: Prisma.SortOrder - secondarySalesRepName?: Prisma.SortOrder - secondarySalesRepIdentifier?: Prisma.SortOrder - secondarySalesRepCwId?: Prisma.SortOrder - companyCwId?: Prisma.SortOrder - companyName?: Prisma.SortOrder - contactCwId?: Prisma.SortOrder - contactName?: Prisma.SortOrder - siteCwId?: Prisma.SortOrder - siteName?: Prisma.SortOrder - customerPO?: Prisma.SortOrder - totalSalesTax?: Prisma.SortOrder + oppNarrative?: Prisma.SortOrder + typeId?: Prisma.SortOrder + stageId?: Prisma.SortOrder + statusId?: Prisma.SortOrder + taxCodeId?: Prisma.SortOrder + interest?: Prisma.SortOrder probability?: Prisma.SortOrder - locationName?: Prisma.SortOrder - locationCwId?: Prisma.SortOrder - departmentName?: Prisma.SortOrder - departmentCwId?: Prisma.SortOrder + source?: Prisma.SortOrder + primarySalesRepId?: Prisma.SortOrder + secondarySalesRepId?: Prisma.SortOrder + companyId?: Prisma.SortOrder + contactId?: Prisma.SortOrder + siteId?: Prisma.SortOrder + customerPO?: Prisma.SortOrder + locationId?: Prisma.SortOrder + departmentId?: Prisma.SortOrder expectedCloseDate?: Prisma.SortOrder pipelineChangeDate?: Prisma.SortOrder dateBecameLead?: Prisma.SortOrder closedDate?: Prisma.SortOrder closedFlag?: Prisma.SortOrder - closedByName?: Prisma.SortOrder - closedByCwId?: Prisma.SortOrder - companyId?: Prisma.SortOrder - cwLastUpdated?: Prisma.SortOrder - cwDateEntered?: Prisma.SortOrder + closedById?: Prisma.SortOrder + updatedBy?: Prisma.SortOrder + eneteredBy?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } export type OpportunityMinOrderByAggregateInput = { id?: Prisma.SortOrder - cwOpportunityId?: Prisma.SortOrder + uid?: Prisma.SortOrder name?: Prisma.SortOrder notes?: Prisma.SortOrder - typeName?: Prisma.SortOrder - typeCwId?: Prisma.SortOrder - stageName?: Prisma.SortOrder - stageCwId?: Prisma.SortOrder - statusName?: Prisma.SortOrder - statusCwId?: Prisma.SortOrder - priorityName?: Prisma.SortOrder - priorityCwId?: Prisma.SortOrder - ratingName?: Prisma.SortOrder - ratingCwId?: Prisma.SortOrder - source?: Prisma.SortOrder - campaignName?: Prisma.SortOrder - campaignCwId?: Prisma.SortOrder - primarySalesRepName?: Prisma.SortOrder - primarySalesRepIdentifier?: Prisma.SortOrder - primarySalesRepCwId?: Prisma.SortOrder - secondarySalesRepName?: Prisma.SortOrder - secondarySalesRepIdentifier?: Prisma.SortOrder - secondarySalesRepCwId?: Prisma.SortOrder - companyCwId?: Prisma.SortOrder - companyName?: Prisma.SortOrder - contactCwId?: Prisma.SortOrder - contactName?: Prisma.SortOrder - siteCwId?: Prisma.SortOrder - siteName?: Prisma.SortOrder - customerPO?: Prisma.SortOrder - totalSalesTax?: Prisma.SortOrder + oppNarrative?: Prisma.SortOrder + typeId?: Prisma.SortOrder + stageId?: Prisma.SortOrder + statusId?: Prisma.SortOrder + taxCodeId?: Prisma.SortOrder + interest?: Prisma.SortOrder probability?: Prisma.SortOrder - locationName?: Prisma.SortOrder - locationCwId?: Prisma.SortOrder - departmentName?: Prisma.SortOrder - departmentCwId?: Prisma.SortOrder + source?: Prisma.SortOrder + primarySalesRepId?: Prisma.SortOrder + secondarySalesRepId?: Prisma.SortOrder + companyId?: Prisma.SortOrder + contactId?: Prisma.SortOrder + siteId?: Prisma.SortOrder + customerPO?: Prisma.SortOrder + locationId?: Prisma.SortOrder + departmentId?: Prisma.SortOrder expectedCloseDate?: Prisma.SortOrder pipelineChangeDate?: Prisma.SortOrder dateBecameLead?: Prisma.SortOrder closedDate?: Prisma.SortOrder closedFlag?: Prisma.SortOrder - closedByName?: Prisma.SortOrder - closedByCwId?: Prisma.SortOrder - companyId?: Prisma.SortOrder - cwLastUpdated?: Prisma.SortOrder - cwDateEntered?: Prisma.SortOrder + closedById?: Prisma.SortOrder + updatedBy?: Prisma.SortOrder + eneteredBy?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } export type OpportunitySumOrderByAggregateInput = { - cwOpportunityId?: Prisma.SortOrder - typeCwId?: Prisma.SortOrder - stageCwId?: Prisma.SortOrder - statusCwId?: Prisma.SortOrder - priorityCwId?: Prisma.SortOrder - ratingCwId?: Prisma.SortOrder - campaignCwId?: Prisma.SortOrder - primarySalesRepCwId?: Prisma.SortOrder - secondarySalesRepCwId?: Prisma.SortOrder - companyCwId?: Prisma.SortOrder - contactCwId?: Prisma.SortOrder - siteCwId?: Prisma.SortOrder - totalSalesTax?: Prisma.SortOrder + id?: Prisma.SortOrder + typeId?: Prisma.SortOrder + stageId?: Prisma.SortOrder + statusId?: Prisma.SortOrder + taxCodeId?: Prisma.SortOrder probability?: Prisma.SortOrder - locationCwId?: Prisma.SortOrder - departmentCwId?: Prisma.SortOrder - closedByCwId?: Prisma.SortOrder + companyId?: Prisma.SortOrder + contactId?: Prisma.SortOrder + siteId?: Prisma.SortOrder + locationId?: Prisma.SortOrder + departmentId?: Prisma.SortOrder productSequence?: Prisma.SortOrder } @@ -1445,6 +1045,174 @@ export type OpportunityScalarRelationFilter = { isNot?: Prisma.OpportunityWhereInput } +export type OpportunityCreateNestedManyWithoutPrimarySalesRepInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutPrimarySalesRepInput[] | Prisma.OpportunityUncheckedCreateWithoutPrimarySalesRepInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutPrimarySalesRepInput | Prisma.OpportunityCreateOrConnectWithoutPrimarySalesRepInput[] + createMany?: Prisma.OpportunityCreateManyPrimarySalesRepInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityCreateNestedManyWithoutSecondarySalesRepInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutSecondarySalesRepInput[] | Prisma.OpportunityUncheckedCreateWithoutSecondarySalesRepInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutSecondarySalesRepInput | Prisma.OpportunityCreateOrConnectWithoutSecondarySalesRepInput[] + createMany?: Prisma.OpportunityCreateManySecondarySalesRepInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutPrimarySalesRepInput[] | Prisma.OpportunityUncheckedCreateWithoutPrimarySalesRepInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutPrimarySalesRepInput | Prisma.OpportunityCreateOrConnectWithoutPrimarySalesRepInput[] + createMany?: Prisma.OpportunityCreateManyPrimarySalesRepInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutSecondarySalesRepInput[] | Prisma.OpportunityUncheckedCreateWithoutSecondarySalesRepInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutSecondarySalesRepInput | Prisma.OpportunityCreateOrConnectWithoutSecondarySalesRepInput[] + createMany?: Prisma.OpportunityCreateManySecondarySalesRepInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUpdateManyWithoutPrimarySalesRepNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutPrimarySalesRepInput[] | Prisma.OpportunityUncheckedCreateWithoutPrimarySalesRepInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutPrimarySalesRepInput | Prisma.OpportunityCreateOrConnectWithoutPrimarySalesRepInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutPrimarySalesRepInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutPrimarySalesRepInput[] + createMany?: Prisma.OpportunityCreateManyPrimarySalesRepInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutPrimarySalesRepInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutPrimarySalesRepInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutPrimarySalesRepInput | Prisma.OpportunityUpdateManyWithWhereWithoutPrimarySalesRepInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUpdateManyWithoutSecondarySalesRepNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutSecondarySalesRepInput[] | Prisma.OpportunityUncheckedCreateWithoutSecondarySalesRepInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutSecondarySalesRepInput | Prisma.OpportunityCreateOrConnectWithoutSecondarySalesRepInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutSecondarySalesRepInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutSecondarySalesRepInput[] + createMany?: Prisma.OpportunityCreateManySecondarySalesRepInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutSecondarySalesRepInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutSecondarySalesRepInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutSecondarySalesRepInput | Prisma.OpportunityUpdateManyWithWhereWithoutSecondarySalesRepInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutPrimarySalesRepInput[] | Prisma.OpportunityUncheckedCreateWithoutPrimarySalesRepInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutPrimarySalesRepInput | Prisma.OpportunityCreateOrConnectWithoutPrimarySalesRepInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutPrimarySalesRepInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutPrimarySalesRepInput[] + createMany?: Prisma.OpportunityCreateManyPrimarySalesRepInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutPrimarySalesRepInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutPrimarySalesRepInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutPrimarySalesRepInput | Prisma.OpportunityUpdateManyWithWhereWithoutPrimarySalesRepInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutSecondarySalesRepInput[] | Prisma.OpportunityUncheckedCreateWithoutSecondarySalesRepInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutSecondarySalesRepInput | Prisma.OpportunityCreateOrConnectWithoutSecondarySalesRepInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutSecondarySalesRepInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutSecondarySalesRepInput[] + createMany?: Prisma.OpportunityCreateManySecondarySalesRepInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutSecondarySalesRepInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutSecondarySalesRepInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutSecondarySalesRepInput | Prisma.OpportunityUpdateManyWithWhereWithoutSecondarySalesRepInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityCreateNestedManyWithoutLocationInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutLocationInput[] | Prisma.OpportunityUncheckedCreateWithoutLocationInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutLocationInput | Prisma.OpportunityCreateOrConnectWithoutLocationInput[] + createMany?: Prisma.OpportunityCreateManyLocationInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutLocationInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutLocationInput[] | Prisma.OpportunityUncheckedCreateWithoutLocationInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutLocationInput | Prisma.OpportunityCreateOrConnectWithoutLocationInput[] + createMany?: Prisma.OpportunityCreateManyLocationInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUpdateManyWithoutLocationNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutLocationInput[] | Prisma.OpportunityUncheckedCreateWithoutLocationInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutLocationInput | Prisma.OpportunityCreateOrConnectWithoutLocationInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutLocationInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutLocationInput[] + createMany?: Prisma.OpportunityCreateManyLocationInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutLocationInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutLocationInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutLocationInput | Prisma.OpportunityUpdateManyWithWhereWithoutLocationInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutLocationNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutLocationInput[] | Prisma.OpportunityUncheckedCreateWithoutLocationInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutLocationInput | Prisma.OpportunityCreateOrConnectWithoutLocationInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutLocationInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutLocationInput[] + createMany?: Prisma.OpportunityCreateManyLocationInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutLocationInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutLocationInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutLocationInput | Prisma.OpportunityUpdateManyWithWhereWithoutLocationInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityCreateNestedManyWithoutDepartmentInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutDepartmentInput[] | Prisma.OpportunityUncheckedCreateWithoutDepartmentInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutDepartmentInput | Prisma.OpportunityCreateOrConnectWithoutDepartmentInput[] + createMany?: Prisma.OpportunityCreateManyDepartmentInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutDepartmentInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutDepartmentInput[] | Prisma.OpportunityUncheckedCreateWithoutDepartmentInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutDepartmentInput | Prisma.OpportunityCreateOrConnectWithoutDepartmentInput[] + createMany?: Prisma.OpportunityCreateManyDepartmentInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUpdateManyWithoutDepartmentNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutDepartmentInput[] | Prisma.OpportunityUncheckedCreateWithoutDepartmentInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutDepartmentInput | Prisma.OpportunityCreateOrConnectWithoutDepartmentInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutDepartmentInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutDepartmentInput[] + createMany?: Prisma.OpportunityCreateManyDepartmentInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutDepartmentInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutDepartmentInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutDepartmentInput | Prisma.OpportunityUpdateManyWithWhereWithoutDepartmentInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutDepartmentNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutDepartmentInput[] | Prisma.OpportunityUncheckedCreateWithoutDepartmentInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutDepartmentInput | Prisma.OpportunityCreateOrConnectWithoutDepartmentInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutDepartmentInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutDepartmentInput[] + createMany?: Prisma.OpportunityCreateManyDepartmentInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutDepartmentInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutDepartmentInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutDepartmentInput | Prisma.OpportunityUpdateManyWithWhereWithoutDepartmentInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + export type OpportunityCreateNestedManyWithoutCompanyInput = { create?: Prisma.XOR | Prisma.OpportunityCreateWithoutCompanyInput[] | Prisma.OpportunityUncheckedCreateWithoutCompanyInput[] connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutCompanyInput | Prisma.OpportunityCreateOrConnectWithoutCompanyInput[] @@ -1487,10 +1255,240 @@ export type OpportunityUncheckedUpdateManyWithoutCompanyNestedInput = { deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] } +export type OpportunityCreateNestedManyWithoutSiteInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutSiteInput[] | Prisma.OpportunityUncheckedCreateWithoutSiteInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutSiteInput | Prisma.OpportunityCreateOrConnectWithoutSiteInput[] + createMany?: Prisma.OpportunityCreateManySiteInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutSiteInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutSiteInput[] | Prisma.OpportunityUncheckedCreateWithoutSiteInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutSiteInput | Prisma.OpportunityCreateOrConnectWithoutSiteInput[] + createMany?: Prisma.OpportunityCreateManySiteInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUpdateManyWithoutSiteNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutSiteInput[] | Prisma.OpportunityUncheckedCreateWithoutSiteInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutSiteInput | Prisma.OpportunityCreateOrConnectWithoutSiteInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutSiteInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutSiteInput[] + createMany?: Prisma.OpportunityCreateManySiteInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutSiteInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutSiteInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutSiteInput | Prisma.OpportunityUpdateManyWithWhereWithoutSiteInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutSiteNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutSiteInput[] | Prisma.OpportunityUncheckedCreateWithoutSiteInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutSiteInput | Prisma.OpportunityCreateOrConnectWithoutSiteInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutSiteInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutSiteInput[] + createMany?: Prisma.OpportunityCreateManySiteInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutSiteInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutSiteInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutSiteInput | Prisma.OpportunityUpdateManyWithWhereWithoutSiteInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityCreateNestedManyWithoutContactInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutContactInput[] | Prisma.OpportunityUncheckedCreateWithoutContactInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutContactInput | Prisma.OpportunityCreateOrConnectWithoutContactInput[] + createMany?: Prisma.OpportunityCreateManyContactInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutContactInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutContactInput[] | Prisma.OpportunityUncheckedCreateWithoutContactInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutContactInput | Prisma.OpportunityCreateOrConnectWithoutContactInput[] + createMany?: Prisma.OpportunityCreateManyContactInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUpdateManyWithoutContactNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutContactInput[] | Prisma.OpportunityUncheckedCreateWithoutContactInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutContactInput | Prisma.OpportunityCreateOrConnectWithoutContactInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutContactInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutContactInput[] + createMany?: Prisma.OpportunityCreateManyContactInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutContactInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutContactInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutContactInput | Prisma.OpportunityUpdateManyWithWhereWithoutContactInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutContactNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutContactInput[] | Prisma.OpportunityUncheckedCreateWithoutContactInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutContactInput | Prisma.OpportunityCreateOrConnectWithoutContactInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutContactInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutContactInput[] + createMany?: Prisma.OpportunityCreateManyContactInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutContactInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutContactInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutContactInput | Prisma.OpportunityUpdateManyWithWhereWithoutContactInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityCreateNestedOneWithoutProductsInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutProductsInput + connect?: Prisma.OpportunityWhereUniqueInput +} + +export type OpportunityUpdateOneWithoutProductsNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutProductsInput + upsert?: Prisma.OpportunityUpsertWithoutProductsInput + disconnect?: Prisma.OpportunityWhereInput | boolean + delete?: Prisma.OpportunityWhereInput | boolean + connect?: Prisma.OpportunityWhereUniqueInput + update?: Prisma.XOR, Prisma.OpportunityUncheckedUpdateWithoutProductsInput> +} + +export type OpportunityCreateNestedManyWithoutStageInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutStageInput[] | Prisma.OpportunityUncheckedCreateWithoutStageInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutStageInput | Prisma.OpportunityCreateOrConnectWithoutStageInput[] + createMany?: Prisma.OpportunityCreateManyStageInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutStageInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutStageInput[] | Prisma.OpportunityUncheckedCreateWithoutStageInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutStageInput | Prisma.OpportunityCreateOrConnectWithoutStageInput[] + createMany?: Prisma.OpportunityCreateManyStageInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUpdateManyWithoutStageNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutStageInput[] | Prisma.OpportunityUncheckedCreateWithoutStageInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutStageInput | Prisma.OpportunityCreateOrConnectWithoutStageInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutStageInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutStageInput[] + createMany?: Prisma.OpportunityCreateManyStageInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutStageInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutStageInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutStageInput | Prisma.OpportunityUpdateManyWithWhereWithoutStageInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutStageNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutStageInput[] | Prisma.OpportunityUncheckedCreateWithoutStageInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutStageInput | Prisma.OpportunityCreateOrConnectWithoutStageInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutStageInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutStageInput[] + createMany?: Prisma.OpportunityCreateManyStageInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutStageInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutStageInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutStageInput | Prisma.OpportunityUpdateManyWithWhereWithoutStageInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityCreateNestedManyWithoutTypeInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutTypeInput[] | Prisma.OpportunityUncheckedCreateWithoutTypeInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutTypeInput | Prisma.OpportunityCreateOrConnectWithoutTypeInput[] + createMany?: Prisma.OpportunityCreateManyTypeInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutTypeInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutTypeInput[] | Prisma.OpportunityUncheckedCreateWithoutTypeInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutTypeInput | Prisma.OpportunityCreateOrConnectWithoutTypeInput[] + createMany?: Prisma.OpportunityCreateManyTypeInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUpdateManyWithoutTypeNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutTypeInput[] | Prisma.OpportunityUncheckedCreateWithoutTypeInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutTypeInput | Prisma.OpportunityCreateOrConnectWithoutTypeInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutTypeInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutTypeInput[] + createMany?: Prisma.OpportunityCreateManyTypeInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutTypeInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutTypeInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutTypeInput | Prisma.OpportunityUpdateManyWithWhereWithoutTypeInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutTypeNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutTypeInput[] | Prisma.OpportunityUncheckedCreateWithoutTypeInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutTypeInput | Prisma.OpportunityCreateOrConnectWithoutTypeInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutTypeInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutTypeInput[] + createMany?: Prisma.OpportunityCreateManyTypeInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutTypeInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutTypeInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutTypeInput | Prisma.OpportunityUpdateManyWithWhereWithoutTypeInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityCreateNestedManyWithoutStatusInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutStatusInput[] | Prisma.OpportunityUncheckedCreateWithoutStatusInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutStatusInput | Prisma.OpportunityCreateOrConnectWithoutStatusInput[] + createMany?: Prisma.OpportunityCreateManyStatusInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutStatusInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutStatusInput[] | Prisma.OpportunityUncheckedCreateWithoutStatusInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutStatusInput | Prisma.OpportunityCreateOrConnectWithoutStatusInput[] + createMany?: Prisma.OpportunityCreateManyStatusInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUpdateManyWithoutStatusNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutStatusInput[] | Prisma.OpportunityUncheckedCreateWithoutStatusInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutStatusInput | Prisma.OpportunityCreateOrConnectWithoutStatusInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutStatusInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutStatusInput[] + createMany?: Prisma.OpportunityCreateManyStatusInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutStatusInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutStatusInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutStatusInput | Prisma.OpportunityUpdateManyWithWhereWithoutStatusInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutStatusNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutStatusInput[] | Prisma.OpportunityUncheckedCreateWithoutStatusInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutStatusInput | Prisma.OpportunityCreateOrConnectWithoutStatusInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutStatusInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutStatusInput[] + createMany?: Prisma.OpportunityCreateManyStatusInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutStatusInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutStatusInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutStatusInput | Prisma.OpportunityUpdateManyWithWhereWithoutStatusInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + export type OpportunityCreateproductSequenceInput = { set: number[] } +export type NullableEnumOpportunityInterestFieldUpdateOperationsInput = { + set?: $Enums.OpportunityInterest | null +} + export type OpportunityUpdateproductSequenceInput = { set?: number[] push?: number | number[] @@ -1510,108 +1508,537 @@ export type OpportunityUpdateOneRequiredWithoutGeneratedQuotesNestedInput = { update?: Prisma.XOR, Prisma.OpportunityUncheckedUpdateWithoutGeneratedQuotesInput> } -export type OpportunityCreateWithoutCompanyInput = { - id?: string - cwOpportunityId: number +export type OpportunityCreateNestedManyWithoutTaxCodeInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutTaxCodeInput[] | Prisma.OpportunityUncheckedCreateWithoutTaxCodeInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutTaxCodeInput | Prisma.OpportunityCreateOrConnectWithoutTaxCodeInput[] + createMany?: Prisma.OpportunityCreateManyTaxCodeInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutTaxCodeInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutTaxCodeInput[] | Prisma.OpportunityUncheckedCreateWithoutTaxCodeInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutTaxCodeInput | Prisma.OpportunityCreateOrConnectWithoutTaxCodeInput[] + createMany?: Prisma.OpportunityCreateManyTaxCodeInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUpdateManyWithoutTaxCodeNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutTaxCodeInput[] | Prisma.OpportunityUncheckedCreateWithoutTaxCodeInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutTaxCodeInput | Prisma.OpportunityCreateOrConnectWithoutTaxCodeInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutTaxCodeInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutTaxCodeInput[] + createMany?: Prisma.OpportunityCreateManyTaxCodeInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutTaxCodeInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutTaxCodeInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutTaxCodeInput | Prisma.OpportunityUpdateManyWithWhereWithoutTaxCodeInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutTaxCodeNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutTaxCodeInput[] | Prisma.OpportunityUncheckedCreateWithoutTaxCodeInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutTaxCodeInput | Prisma.OpportunityCreateOrConnectWithoutTaxCodeInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutTaxCodeInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutTaxCodeInput[] + createMany?: Prisma.OpportunityCreateManyTaxCodeInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutTaxCodeInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutTaxCodeInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutTaxCodeInput | Prisma.OpportunityUpdateManyWithWhereWithoutTaxCodeInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityCreateWithoutPrimarySalesRepInput = { + id: number + uid?: string name: string notes?: string | null - typeName?: string | null - typeCwId?: number | null - stageName?: string | null - stageCwId?: number | null - statusName?: string | null - statusCwId?: number | null - priorityName?: string | null - priorityCwId?: number | null - ratingName?: string | null - ratingCwId?: number | null - source?: string | null - campaignName?: string | null - campaignCwId?: number | null - primarySalesRepName?: string | null - primarySalesRepIdentifier?: string | null - primarySalesRepCwId?: number | null - secondarySalesRepName?: string | null - secondarySalesRepIdentifier?: string | null - secondarySalesRepCwId?: number | null - companyCwId?: number | null - companyName?: string | null - contactCwId?: number | null - contactName?: string | null - siteCwId?: number | null - siteName?: string | null - customerPO?: string | null - totalSalesTax?: number + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null probability?: number - locationName?: string | null - locationCwId?: number | null - departmentName?: string | null - departmentCwId?: number | null + source?: string | null + customerPO?: string | null expectedCloseDate?: Date | string | null pipelineChangeDate?: Date | string | null dateBecameLead?: Date | string | null closedDate?: Date | string | null closedFlag?: boolean - closedByName?: string | null - closedByCwId?: number | null + closedById?: string | null productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] - cwLastUpdated?: Date | string | null - cwDateEntered?: Date | string | null + updatedBy: string + eneteredBy: string createdAt?: Date | string updatedAt?: Date | string generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput } -export type OpportunityUncheckedCreateWithoutCompanyInput = { - id?: string - cwOpportunityId: number +export type OpportunityUncheckedCreateWithoutPrimarySalesRepInput = { + id: number + uid?: string name: string notes?: string | null - typeName?: string | null - typeCwId?: number | null - stageName?: string | null - stageCwId?: number | null - statusName?: string | null - statusCwId?: number | null - priorityName?: string | null - priorityCwId?: number | null - ratingName?: string | null - ratingCwId?: number | null - source?: string | null - campaignName?: string | null - campaignCwId?: number | null - primarySalesRepName?: string | null - primarySalesRepIdentifier?: string | null - primarySalesRepCwId?: number | null - secondarySalesRepName?: string | null - secondarySalesRepIdentifier?: string | null - secondarySalesRepCwId?: number | null - companyCwId?: number | null - companyName?: string | null - contactCwId?: number | null - contactName?: string | null - siteCwId?: number | null - siteName?: string | null - customerPO?: string | null - totalSalesTax?: number + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null probability?: number - locationName?: string | null - locationCwId?: number | null - departmentName?: string | null - departmentCwId?: number | null + source?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null expectedCloseDate?: Date | string | null pipelineChangeDate?: Date | string | null dateBecameLead?: Date | string | null closedDate?: Date | string | null closedFlag?: boolean - closedByName?: string | null - closedByCwId?: number | null + closedById?: string | null productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] - cwLastUpdated?: Date | string | null - cwDateEntered?: Date | string | null + updatedBy: string + eneteredBy: string createdAt?: Date | string updatedAt?: Date | string generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutPrimarySalesRepInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManyPrimarySalesRepInputEnvelope = { + data: Prisma.OpportunityCreateManyPrimarySalesRepInput | Prisma.OpportunityCreateManyPrimarySalesRepInput[] + skipDuplicates?: boolean +} + +export type OpportunityCreateWithoutSecondarySalesRepInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + customerPO?: string | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityUncheckedCreateWithoutSecondarySalesRepInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutSecondarySalesRepInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManySecondarySalesRepInputEnvelope = { + data: Prisma.OpportunityCreateManySecondarySalesRepInput | Prisma.OpportunityCreateManySecondarySalesRepInput[] + skipDuplicates?: boolean +} + +export type OpportunityUpsertWithWhereUniqueWithoutPrimarySalesRepInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutPrimarySalesRepInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutPrimarySalesRepInput = { + where: Prisma.OpportunityScalarWhereInput + data: Prisma.XOR +} + +export type OpportunityScalarWhereInput = { + AND?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] + OR?: Prisma.OpportunityScalarWhereInput[] + NOT?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] + id?: Prisma.IntFilter<"Opportunity"> | number + uid?: Prisma.StringFilter<"Opportunity"> | string + name?: Prisma.StringFilter<"Opportunity"> | string + notes?: Prisma.StringNullableFilter<"Opportunity"> | string | null + oppNarrative?: Prisma.StringNullableFilter<"Opportunity"> | string | null + typeId?: Prisma.IntFilter<"Opportunity"> | number + stageId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + statusId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + taxCodeId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + interest?: Prisma.EnumOpportunityInterestNullableFilter<"Opportunity"> | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFilter<"Opportunity"> | number + source?: Prisma.StringNullableFilter<"Opportunity"> | string | null + primarySalesRepId?: Prisma.StringNullableFilter<"Opportunity"> | string | null + secondarySalesRepId?: Prisma.StringNullableFilter<"Opportunity"> | string | null + companyId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + contactId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + siteId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + customerPO?: Prisma.StringNullableFilter<"Opportunity"> | string | null + locationId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + departmentId?: Prisma.IntNullableFilter<"Opportunity"> | number | null + expectedCloseDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null + pipelineChangeDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null + dateBecameLead?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null + closedDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null + closedFlag?: Prisma.BoolFilter<"Opportunity"> | boolean + closedById?: Prisma.StringNullableFilter<"Opportunity"> | string | null + productSequence?: Prisma.IntNullableListFilter<"Opportunity"> + updatedBy?: Prisma.StringFilter<"Opportunity"> | string + eneteredBy?: Prisma.StringFilter<"Opportunity"> | string + createdAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string +} + +export type OpportunityUpsertWithWhereUniqueWithoutSecondarySalesRepInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutSecondarySalesRepInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutSecondarySalesRepInput = { + where: Prisma.OpportunityScalarWhereInput + data: Prisma.XOR +} + +export type OpportunityCreateWithoutLocationInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + customerPO?: string | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityUncheckedCreateWithoutLocationInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutLocationInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManyLocationInputEnvelope = { + data: Prisma.OpportunityCreateManyLocationInput | Prisma.OpportunityCreateManyLocationInput[] + skipDuplicates?: boolean +} + +export type OpportunityUpsertWithWhereUniqueWithoutLocationInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutLocationInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutLocationInput = { + where: Prisma.OpportunityScalarWhereInput + data: Prisma.XOR +} + +export type OpportunityCreateWithoutDepartmentInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + customerPO?: string | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityUncheckedCreateWithoutDepartmentInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutDepartmentInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManyDepartmentInputEnvelope = { + data: Prisma.OpportunityCreateManyDepartmentInput | Prisma.OpportunityCreateManyDepartmentInput[] + skipDuplicates?: boolean +} + +export type OpportunityUpsertWithWhereUniqueWithoutDepartmentInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutDepartmentInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutDepartmentInput = { + where: Prisma.OpportunityScalarWhereInput + data: Prisma.XOR +} + +export type OpportunityCreateWithoutCompanyInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + customerPO?: string | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityUncheckedCreateWithoutCompanyInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput } export type OpportunityCreateOrConnectWithoutCompanyInput = { @@ -1640,163 +2067,710 @@ export type OpportunityUpdateManyWithWhereWithoutCompanyInput = { data: Prisma.XOR } -export type OpportunityScalarWhereInput = { - AND?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] - OR?: Prisma.OpportunityScalarWhereInput[] - NOT?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] - id?: Prisma.StringFilter<"Opportunity"> | string - cwOpportunityId?: Prisma.IntFilter<"Opportunity"> | number - name?: Prisma.StringFilter<"Opportunity"> | string - notes?: Prisma.StringNullableFilter<"Opportunity"> | string | null - typeName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - typeCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - stageName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - stageCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - statusName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - statusCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - priorityName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - priorityCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - ratingName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - ratingCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - source?: Prisma.StringNullableFilter<"Opportunity"> | string | null - campaignName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - campaignCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - primarySalesRepName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - primarySalesRepIdentifier?: Prisma.StringNullableFilter<"Opportunity"> | string | null - primarySalesRepCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - secondarySalesRepName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - secondarySalesRepIdentifier?: Prisma.StringNullableFilter<"Opportunity"> | string | null - secondarySalesRepCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - companyCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - companyName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - contactCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - contactName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - siteCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - siteName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - customerPO?: Prisma.StringNullableFilter<"Opportunity"> | string | null - totalSalesTax?: Prisma.FloatFilter<"Opportunity"> | number - probability?: Prisma.FloatFilter<"Opportunity"> | number - locationName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - locationCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - departmentName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - departmentCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - expectedCloseDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null - pipelineChangeDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null - dateBecameLead?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null - closedDate?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null - closedFlag?: Prisma.BoolFilter<"Opportunity"> | boolean - closedByName?: Prisma.StringNullableFilter<"Opportunity"> | string | null - closedByCwId?: Prisma.IntNullableFilter<"Opportunity"> | number | null - companyId?: Prisma.StringNullableFilter<"Opportunity"> | string | null - productSequence?: Prisma.IntNullableListFilter<"Opportunity"> - cwLastUpdated?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null - cwDateEntered?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null - createdAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string - updatedAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string +export type OpportunityCreateWithoutSiteInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + customerPO?: string | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityUncheckedCreateWithoutSiteInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutSiteInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManySiteInputEnvelope = { + data: Prisma.OpportunityCreateManySiteInput | Prisma.OpportunityCreateManySiteInput[] + skipDuplicates?: boolean +} + +export type OpportunityUpsertWithWhereUniqueWithoutSiteInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutSiteInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutSiteInput = { + where: Prisma.OpportunityScalarWhereInput + data: Prisma.XOR +} + +export type OpportunityCreateWithoutContactInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + customerPO?: string | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityUncheckedCreateWithoutContactInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutContactInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManyContactInputEnvelope = { + data: Prisma.OpportunityCreateManyContactInput | Prisma.OpportunityCreateManyContactInput[] + skipDuplicates?: boolean +} + +export type OpportunityUpsertWithWhereUniqueWithoutContactInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutContactInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutContactInput = { + where: Prisma.OpportunityScalarWhereInput + data: Prisma.XOR +} + +export type OpportunityCreateWithoutProductsInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + customerPO?: string | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput +} + +export type OpportunityUncheckedCreateWithoutProductsInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutProductsInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityUpsertWithoutProductsInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.OpportunityWhereInput +} + +export type OpportunityUpdateToOneWithWhereWithoutProductsInput = { + where?: Prisma.OpportunityWhereInput + data: Prisma.XOR +} + +export type OpportunityUpdateWithoutProductsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput +} + +export type OpportunityUncheckedUpdateWithoutProductsInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityCreateWithoutStageInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + customerPO?: string | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityUncheckedCreateWithoutStageInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutStageInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManyStageInputEnvelope = { + data: Prisma.OpportunityCreateManyStageInput | Prisma.OpportunityCreateManyStageInput[] + skipDuplicates?: boolean +} + +export type OpportunityUpsertWithWhereUniqueWithoutStageInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutStageInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutStageInput = { + where: Prisma.OpportunityScalarWhereInput + data: Prisma.XOR +} + +export type OpportunityCreateWithoutTypeInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + customerPO?: string | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityUncheckedCreateWithoutTypeInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutTypeInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManyTypeInputEnvelope = { + data: Prisma.OpportunityCreateManyTypeInput | Prisma.OpportunityCreateManyTypeInput[] + skipDuplicates?: boolean +} + +export type OpportunityUpsertWithWhereUniqueWithoutTypeInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutTypeInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutTypeInput = { + where: Prisma.OpportunityScalarWhereInput + data: Prisma.XOR +} + +export type OpportunityCreateWithoutStatusInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + customerPO?: string | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityUncheckedCreateWithoutStatusInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutStatusInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManyStatusInputEnvelope = { + data: Prisma.OpportunityCreateManyStatusInput | Prisma.OpportunityCreateManyStatusInput[] + skipDuplicates?: boolean +} + +export type OpportunityUpsertWithWhereUniqueWithoutStatusInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutStatusInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutStatusInput = { + where: Prisma.OpportunityScalarWhereInput + data: Prisma.XOR } export type OpportunityCreateWithoutGeneratedQuotesInput = { - id?: string - cwOpportunityId: number + id: number + uid?: string name: string notes?: string | null - typeName?: string | null - typeCwId?: number | null - stageName?: string | null - stageCwId?: number | null - statusName?: string | null - statusCwId?: number | null - priorityName?: string | null - priorityCwId?: number | null - ratingName?: string | null - ratingCwId?: number | null - source?: string | null - campaignName?: string | null - campaignCwId?: number | null - primarySalesRepName?: string | null - primarySalesRepIdentifier?: string | null - primarySalesRepCwId?: number | null - secondarySalesRepName?: string | null - secondarySalesRepIdentifier?: string | null - secondarySalesRepCwId?: number | null - companyCwId?: number | null - companyName?: string | null - contactCwId?: number | null - contactName?: string | null - siteCwId?: number | null - siteName?: string | null - customerPO?: string | null - totalSalesTax?: number + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null probability?: number - locationName?: string | null - locationCwId?: number | null - departmentName?: string | null - departmentCwId?: number | null + source?: string | null + customerPO?: string | null expectedCloseDate?: Date | string | null pipelineChangeDate?: Date | string | null dateBecameLead?: Date | string | null closedDate?: Date | string | null closedFlag?: boolean - closedByName?: string | null - closedByCwId?: number | null + closedById?: string | null productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] - cwLastUpdated?: Date | string | null - cwDateEntered?: Date | string | null + updatedBy: string + eneteredBy: string createdAt?: Date | string updatedAt?: Date | string + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + taxCode?: Prisma.TaxCodeCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput } export type OpportunityUncheckedCreateWithoutGeneratedQuotesInput = { - id?: string - cwOpportunityId: number + id: number + uid?: string name: string notes?: string | null - typeName?: string | null - typeCwId?: number | null - stageName?: string | null - stageCwId?: number | null - statusName?: string | null - statusCwId?: number | null - priorityName?: string | null - priorityCwId?: number | null - ratingName?: string | null - ratingCwId?: number | null - source?: string | null - campaignName?: string | null - campaignCwId?: number | null - primarySalesRepName?: string | null - primarySalesRepIdentifier?: string | null - primarySalesRepCwId?: number | null - secondarySalesRepName?: string | null - secondarySalesRepIdentifier?: string | null - secondarySalesRepCwId?: number | null - companyCwId?: number | null - companyName?: string | null - contactCwId?: number | null - contactName?: string | null - siteCwId?: number | null - siteName?: string | null - customerPO?: string | null - totalSalesTax?: number + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null probability?: number - locationName?: string | null - locationCwId?: number | null - departmentName?: string | null - departmentCwId?: number | null + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null expectedCloseDate?: Date | string | null pipelineChangeDate?: Date | string | null dateBecameLead?: Date | string | null closedDate?: Date | string | null closedFlag?: boolean - closedByName?: string | null - closedByCwId?: number | null - companyId?: string | null + closedById?: string | null productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] - cwLastUpdated?: Date | string | null - cwDateEntered?: Date | string | null + updatedBy: string + eneteredBy: string createdAt?: Date | string updatedAt?: Date | string + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput } export type OpportunityCreateOrConnectWithoutGeneratedQuotesInput = { @@ -1816,311 +2790,1663 @@ export type OpportunityUpdateToOneWithWhereWithoutGeneratedQuotesInput = { } export type OpportunityUpdateWithoutGeneratedQuotesInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - stageName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - stageCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - statusName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - statusCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - priorityName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - priorityCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - ratingName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - ratingCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - primarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - secondarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - contactCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - contactName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - siteCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - siteName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - totalSalesTax?: Prisma.FloatFieldUpdateOperationsInput | number + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null probability?: Prisma.FloatFieldUpdateOperationsInput | number - locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - departmentCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean - closedByName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - closedByCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] - cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null - cwDateEntered?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput } export type OpportunityUncheckedUpdateWithoutGeneratedQuotesInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - stageName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - stageCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - statusName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - statusCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - priorityName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - priorityCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - ratingName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - ratingCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - primarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - secondarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - contactCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - contactName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - siteCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - siteName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - totalSalesTax?: Prisma.FloatFieldUpdateOperationsInput | number + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null probability?: Prisma.FloatFieldUpdateOperationsInput | number - locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - departmentCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean - closedByName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - closedByCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] - cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null - cwDateEntered?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput } -export type OpportunityCreateManyCompanyInput = { - id?: string - cwOpportunityId: number +export type OpportunityCreateWithoutTaxCodeInput = { + id: number + uid?: string name: string notes?: string | null - typeName?: string | null - typeCwId?: number | null - stageName?: string | null - stageCwId?: number | null - statusName?: string | null - statusCwId?: number | null - priorityName?: string | null - priorityCwId?: number | null - ratingName?: string | null - ratingCwId?: number | null - source?: string | null - campaignName?: string | null - campaignCwId?: number | null - primarySalesRepName?: string | null - primarySalesRepIdentifier?: string | null - primarySalesRepCwId?: number | null - secondarySalesRepName?: string | null - secondarySalesRepIdentifier?: string | null - secondarySalesRepCwId?: number | null - companyCwId?: number | null - companyName?: string | null - contactCwId?: number | null - contactName?: string | null - siteCwId?: number | null - siteName?: string | null - customerPO?: string | null - totalSalesTax?: number + oppNarrative?: string | null + interest?: $Enums.OpportunityInterest | null probability?: number - locationName?: string | null - locationCwId?: number | null - departmentName?: string | null - departmentCwId?: number | null + source?: string | null + customerPO?: string | null expectedCloseDate?: Date | string | null pipelineChangeDate?: Date | string | null dateBecameLead?: Date | string | null closedDate?: Date | string | null closedFlag?: boolean - closedByName?: string | null - closedByCwId?: number | null + closedById?: string | null productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] - cwLastUpdated?: Date | string | null - cwDateEntered?: Date | string | null + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutOpportunityInput + type: Prisma.OpportunityTypeCreateNestedOneWithoutOpportunitiesInput + stage?: Prisma.OpportunityStageCreateNestedOneWithoutOpportunitiesInput + status?: Prisma.OpportunityStatusCreateNestedOneWithoutOpportunitiesInput + primarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesInput + secondarySalesRep?: Prisma.UserCreateNestedOneWithoutOpportunitiesSecondaryInput + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput + contact?: Prisma.ContactCreateNestedOneWithoutOpportunitiesInput + site?: Prisma.CompanyAddressCreateNestedOneWithoutOppportunitiesInput + location?: Prisma.CorporateLocationCreateNestedOneWithoutOpportunitiesInput + department?: Prisma.InternalDepartmentCreateNestedOneWithoutOpportunitiesInput + products?: Prisma.ProductDataCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityUncheckedCreateWithoutTaxCodeInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutOpportunityInput + products?: Prisma.ProductDataUncheckedCreateNestedManyWithoutOpportunityInput +} + +export type OpportunityCreateOrConnectWithoutTaxCodeInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManyTaxCodeInputEnvelope = { + data: Prisma.OpportunityCreateManyTaxCodeInput | Prisma.OpportunityCreateManyTaxCodeInput[] + skipDuplicates?: boolean +} + +export type OpportunityUpsertWithWhereUniqueWithoutTaxCodeInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutTaxCodeInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutTaxCodeInput = { + where: Prisma.OpportunityScalarWhereInput + data: Prisma.XOR +} + +export type OpportunityCreateManyPrimarySalesRepInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityCreateManySecondarySalesRepInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateWithoutPrimarySalesRepInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateWithoutPrimarySalesRepInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateManyWithoutPrimarySalesRepInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityUpdateWithoutSecondarySalesRepInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateWithoutSecondarySalesRepInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateManyWithoutSecondarySalesRepInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityCreateManyLocationInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateWithoutLocationInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateWithoutLocationInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateManyWithoutLocationInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityCreateManyDepartmentInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateWithoutDepartmentInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateWithoutDepartmentInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateManyWithoutDepartmentInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityCreateManyCompanyInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string createdAt?: Date | string updatedAt?: Date | string } export type OpportunityUpdateWithoutCompanyInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - stageName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - stageCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - statusName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - statusCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - priorityName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - priorityCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - ratingName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - ratingCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - primarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - secondarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - contactCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - contactName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - siteCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - siteName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - totalSalesTax?: Prisma.FloatFieldUpdateOperationsInput | number + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null probability?: Prisma.FloatFieldUpdateOperationsInput | number - locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - departmentCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean - closedByName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - closedByCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] - cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null - cwDateEntered?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput } export type OpportunityUncheckedUpdateWithoutCompanyInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - stageName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - stageCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - statusName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - statusCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - priorityName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - priorityCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - ratingName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - ratingCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - primarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - secondarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - contactCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - contactName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - siteCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - siteName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - totalSalesTax?: Prisma.FloatFieldUpdateOperationsInput | number + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null probability?: Prisma.FloatFieldUpdateOperationsInput | number - locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - departmentCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean - closedByName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - closedByCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] - cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null - cwDateEntered?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput } export type OpportunityUncheckedUpdateManyWithoutCompanyInput = { - id?: Prisma.StringFieldUpdateOperationsInput | string - cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - typeCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - stageName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - stageCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - statusName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - statusCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - priorityName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - priorityCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - ratingName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - ratingCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - campaignCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - primarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - primarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - secondarySalesRepName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - secondarySalesRepCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - companyName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - contactCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - contactName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - siteCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - siteName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - totalSalesTax?: Prisma.FloatFieldUpdateOperationsInput | number + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null probability?: Prisma.FloatFieldUpdateOperationsInput | number - locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null - departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - departmentCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean - closedByName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - closedByCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] - cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null - cwDateEntered?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityCreateManySiteInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateWithoutSiteInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateWithoutSiteInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateManyWithoutSiteInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityCreateManyContactInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateWithoutContactInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateWithoutContactInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateManyWithoutContactInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityCreateManyStageInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateWithoutStageInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateWithoutStageInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateManyWithoutStageInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityCreateManyTypeInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + stageId?: number | null + statusId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateWithoutTypeInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateWithoutTypeInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateManyWithoutTypeInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityCreateManyStatusInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + taxCodeId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateWithoutStatusInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + taxCode?: Prisma.TaxCodeUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateWithoutStatusInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateManyWithoutStatusInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + taxCodeId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityCreateManyTaxCodeInput = { + id: number + uid?: string + name: string + notes?: string | null + oppNarrative?: string | null + typeId: number + stageId?: number | null + statusId?: number | null + interest?: $Enums.OpportunityInterest | null + probability?: number + source?: string | null + primarySalesRepId?: string | null + secondarySalesRepId?: string | null + companyId?: number | null + contactId?: number | null + siteId?: number | null + customerPO?: string | null + locationId?: number | null + departmentId?: number | null + expectedCloseDate?: Date | string | null + pipelineChangeDate?: Date | string | null + dateBecameLead?: Date | string | null + closedDate?: Date | string | null + closedFlag?: boolean + closedById?: string | null + productSequence?: Prisma.OpportunityCreateproductSequenceInput | number[] + updatedBy: string + eneteredBy: string + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateWithoutTaxCodeInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutOpportunityNestedInput + type?: Prisma.OpportunityTypeUpdateOneRequiredWithoutOpportunitiesNestedInput + stage?: Prisma.OpportunityStageUpdateOneWithoutOpportunitiesNestedInput + status?: Prisma.OpportunityStatusUpdateOneWithoutOpportunitiesNestedInput + primarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesNestedInput + secondarySalesRep?: Prisma.UserUpdateOneWithoutOpportunitiesSecondaryNestedInput + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput + contact?: Prisma.ContactUpdateOneWithoutOpportunitiesNestedInput + site?: Prisma.CompanyAddressUpdateOneWithoutOppportunitiesNestedInput + location?: Prisma.CorporateLocationUpdateOneWithoutOpportunitiesNestedInput + department?: Prisma.InternalDepartmentUpdateOneWithoutOpportunitiesNestedInput + products?: Prisma.ProductDataUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateWithoutTaxCodeInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutOpportunityNestedInput + products?: Prisma.ProductDataUncheckedUpdateManyWithoutOpportunityNestedInput +} + +export type OpportunityUncheckedUpdateManyWithoutTaxCodeInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number + uid?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + oppNarrative?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + typeId?: Prisma.IntFieldUpdateOperationsInput | number + stageId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + statusId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + interest?: Prisma.NullableEnumOpportunityInterestFieldUpdateOperationsInput | $Enums.OpportunityInterest | null + probability?: Prisma.FloatFieldUpdateOperationsInput | number + source?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + primarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + secondarySalesRepId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + contactId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + siteId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + customerPO?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + expectedCloseDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + pipelineChangeDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + dateBecameLead?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + closedFlag?: Prisma.BoolFieldUpdateOperationsInput | boolean + closedById?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + productSequence?: Prisma.OpportunityUpdateproductSequenceInput | number[] + updatedBy?: Prisma.StringFieldUpdateOperationsInput | string + eneteredBy?: Prisma.StringFieldUpdateOperationsInput | string createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } @@ -2132,10 +4458,12 @@ export type OpportunityUncheckedUpdateManyWithoutCompanyInput = { export type OpportunityCountOutputType = { generatedQuotes: number + products: number } export type OpportunityCountOutputTypeSelect = { generatedQuotes?: boolean | OpportunityCountOutputTypeCountGeneratedQuotesArgs + products?: boolean | OpportunityCountOutputTypeCountProductsArgs } /** @@ -2155,287 +4483,277 @@ export type OpportunityCountOutputTypeCountGeneratedQuotesArgs = { + where?: Prisma.ProductDataWhereInput +} + export type OpportunitySelect = runtime.Types.Extensions.GetSelect<{ id?: boolean - cwOpportunityId?: boolean + uid?: boolean name?: boolean notes?: boolean - typeName?: boolean - typeCwId?: boolean - stageName?: boolean - stageCwId?: boolean - statusName?: boolean - statusCwId?: boolean - priorityName?: boolean - priorityCwId?: boolean - ratingName?: boolean - ratingCwId?: boolean - source?: boolean - campaignName?: boolean - campaignCwId?: boolean - primarySalesRepName?: boolean - primarySalesRepIdentifier?: boolean - primarySalesRepCwId?: boolean - secondarySalesRepName?: boolean - secondarySalesRepIdentifier?: boolean - secondarySalesRepCwId?: boolean - companyCwId?: boolean - companyName?: boolean - contactCwId?: boolean - contactName?: boolean - siteCwId?: boolean - siteName?: boolean - customerPO?: boolean - totalSalesTax?: boolean + oppNarrative?: boolean + typeId?: boolean + stageId?: boolean + statusId?: boolean + taxCodeId?: boolean + interest?: boolean probability?: boolean - locationName?: boolean - locationCwId?: boolean - departmentName?: boolean - departmentCwId?: boolean + source?: boolean + primarySalesRepId?: boolean + secondarySalesRepId?: boolean + companyId?: boolean + contactId?: boolean + siteId?: boolean + customerPO?: boolean + locationId?: boolean + departmentId?: boolean expectedCloseDate?: boolean pipelineChangeDate?: boolean dateBecameLead?: boolean closedDate?: boolean closedFlag?: boolean - closedByName?: boolean - closedByCwId?: boolean - companyId?: boolean + closedById?: boolean productSequence?: boolean - cwLastUpdated?: boolean - cwDateEntered?: boolean + updatedBy?: boolean + eneteredBy?: boolean createdAt?: boolean updatedAt?: boolean generatedQuotes?: boolean | Prisma.Opportunity$generatedQuotesArgs + type?: boolean | Prisma.OpportunityTypeDefaultArgs + stage?: boolean | Prisma.Opportunity$stageArgs + status?: boolean | Prisma.Opportunity$statusArgs + taxCode?: boolean | Prisma.Opportunity$taxCodeArgs + primarySalesRep?: boolean | Prisma.Opportunity$primarySalesRepArgs + secondarySalesRep?: boolean | Prisma.Opportunity$secondarySalesRepArgs company?: boolean | Prisma.Opportunity$companyArgs + contact?: boolean | Prisma.Opportunity$contactArgs + site?: boolean | Prisma.Opportunity$siteArgs + location?: boolean | Prisma.Opportunity$locationArgs + department?: boolean | Prisma.Opportunity$departmentArgs + products?: boolean | Prisma.Opportunity$productsArgs _count?: boolean | Prisma.OpportunityCountOutputTypeDefaultArgs }, ExtArgs["result"]["opportunity"]> export type OpportunitySelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ id?: boolean - cwOpportunityId?: boolean + uid?: boolean name?: boolean notes?: boolean - typeName?: boolean - typeCwId?: boolean - stageName?: boolean - stageCwId?: boolean - statusName?: boolean - statusCwId?: boolean - priorityName?: boolean - priorityCwId?: boolean - ratingName?: boolean - ratingCwId?: boolean - source?: boolean - campaignName?: boolean - campaignCwId?: boolean - primarySalesRepName?: boolean - primarySalesRepIdentifier?: boolean - primarySalesRepCwId?: boolean - secondarySalesRepName?: boolean - secondarySalesRepIdentifier?: boolean - secondarySalesRepCwId?: boolean - companyCwId?: boolean - companyName?: boolean - contactCwId?: boolean - contactName?: boolean - siteCwId?: boolean - siteName?: boolean - customerPO?: boolean - totalSalesTax?: boolean + oppNarrative?: boolean + typeId?: boolean + stageId?: boolean + statusId?: boolean + taxCodeId?: boolean + interest?: boolean probability?: boolean - locationName?: boolean - locationCwId?: boolean - departmentName?: boolean - departmentCwId?: boolean + source?: boolean + primarySalesRepId?: boolean + secondarySalesRepId?: boolean + companyId?: boolean + contactId?: boolean + siteId?: boolean + customerPO?: boolean + locationId?: boolean + departmentId?: boolean expectedCloseDate?: boolean pipelineChangeDate?: boolean dateBecameLead?: boolean closedDate?: boolean closedFlag?: boolean - closedByName?: boolean - closedByCwId?: boolean - companyId?: boolean + closedById?: boolean productSequence?: boolean - cwLastUpdated?: boolean - cwDateEntered?: boolean + updatedBy?: boolean + eneteredBy?: boolean createdAt?: boolean updatedAt?: boolean + type?: boolean | Prisma.OpportunityTypeDefaultArgs + stage?: boolean | Prisma.Opportunity$stageArgs + status?: boolean | Prisma.Opportunity$statusArgs + taxCode?: boolean | Prisma.Opportunity$taxCodeArgs + primarySalesRep?: boolean | Prisma.Opportunity$primarySalesRepArgs + secondarySalesRep?: boolean | Prisma.Opportunity$secondarySalesRepArgs company?: boolean | Prisma.Opportunity$companyArgs + contact?: boolean | Prisma.Opportunity$contactArgs + site?: boolean | Prisma.Opportunity$siteArgs + location?: boolean | Prisma.Opportunity$locationArgs + department?: boolean | Prisma.Opportunity$departmentArgs }, ExtArgs["result"]["opportunity"]> export type OpportunitySelectUpdateManyAndReturn = runtime.Types.Extensions.GetSelect<{ id?: boolean - cwOpportunityId?: boolean + uid?: boolean name?: boolean notes?: boolean - typeName?: boolean - typeCwId?: boolean - stageName?: boolean - stageCwId?: boolean - statusName?: boolean - statusCwId?: boolean - priorityName?: boolean - priorityCwId?: boolean - ratingName?: boolean - ratingCwId?: boolean - source?: boolean - campaignName?: boolean - campaignCwId?: boolean - primarySalesRepName?: boolean - primarySalesRepIdentifier?: boolean - primarySalesRepCwId?: boolean - secondarySalesRepName?: boolean - secondarySalesRepIdentifier?: boolean - secondarySalesRepCwId?: boolean - companyCwId?: boolean - companyName?: boolean - contactCwId?: boolean - contactName?: boolean - siteCwId?: boolean - siteName?: boolean - customerPO?: boolean - totalSalesTax?: boolean + oppNarrative?: boolean + typeId?: boolean + stageId?: boolean + statusId?: boolean + taxCodeId?: boolean + interest?: boolean probability?: boolean - locationName?: boolean - locationCwId?: boolean - departmentName?: boolean - departmentCwId?: boolean + source?: boolean + primarySalesRepId?: boolean + secondarySalesRepId?: boolean + companyId?: boolean + contactId?: boolean + siteId?: boolean + customerPO?: boolean + locationId?: boolean + departmentId?: boolean expectedCloseDate?: boolean pipelineChangeDate?: boolean dateBecameLead?: boolean closedDate?: boolean closedFlag?: boolean - closedByName?: boolean - closedByCwId?: boolean - companyId?: boolean + closedById?: boolean productSequence?: boolean - cwLastUpdated?: boolean - cwDateEntered?: boolean + updatedBy?: boolean + eneteredBy?: boolean createdAt?: boolean updatedAt?: boolean + type?: boolean | Prisma.OpportunityTypeDefaultArgs + stage?: boolean | Prisma.Opportunity$stageArgs + status?: boolean | Prisma.Opportunity$statusArgs + taxCode?: boolean | Prisma.Opportunity$taxCodeArgs + primarySalesRep?: boolean | Prisma.Opportunity$primarySalesRepArgs + secondarySalesRep?: boolean | Prisma.Opportunity$secondarySalesRepArgs company?: boolean | Prisma.Opportunity$companyArgs + contact?: boolean | Prisma.Opportunity$contactArgs + site?: boolean | Prisma.Opportunity$siteArgs + location?: boolean | Prisma.Opportunity$locationArgs + department?: boolean | Prisma.Opportunity$departmentArgs }, ExtArgs["result"]["opportunity"]> export type OpportunitySelectScalar = { id?: boolean - cwOpportunityId?: boolean + uid?: boolean name?: boolean notes?: boolean - typeName?: boolean - typeCwId?: boolean - stageName?: boolean - stageCwId?: boolean - statusName?: boolean - statusCwId?: boolean - priorityName?: boolean - priorityCwId?: boolean - ratingName?: boolean - ratingCwId?: boolean - source?: boolean - campaignName?: boolean - campaignCwId?: boolean - primarySalesRepName?: boolean - primarySalesRepIdentifier?: boolean - primarySalesRepCwId?: boolean - secondarySalesRepName?: boolean - secondarySalesRepIdentifier?: boolean - secondarySalesRepCwId?: boolean - companyCwId?: boolean - companyName?: boolean - contactCwId?: boolean - contactName?: boolean - siteCwId?: boolean - siteName?: boolean - customerPO?: boolean - totalSalesTax?: boolean + oppNarrative?: boolean + typeId?: boolean + stageId?: boolean + statusId?: boolean + taxCodeId?: boolean + interest?: boolean probability?: boolean - locationName?: boolean - locationCwId?: boolean - departmentName?: boolean - departmentCwId?: boolean + source?: boolean + primarySalesRepId?: boolean + secondarySalesRepId?: boolean + companyId?: boolean + contactId?: boolean + siteId?: boolean + customerPO?: boolean + locationId?: boolean + departmentId?: boolean expectedCloseDate?: boolean pipelineChangeDate?: boolean dateBecameLead?: boolean closedDate?: boolean closedFlag?: boolean - closedByName?: boolean - closedByCwId?: boolean - companyId?: boolean + closedById?: boolean productSequence?: boolean - cwLastUpdated?: boolean - cwDateEntered?: boolean + updatedBy?: boolean + eneteredBy?: boolean createdAt?: boolean updatedAt?: boolean } -export type OpportunityOmit = runtime.Types.Extensions.GetOmit<"id" | "cwOpportunityId" | "name" | "notes" | "typeName" | "typeCwId" | "stageName" | "stageCwId" | "statusName" | "statusCwId" | "priorityName" | "priorityCwId" | "ratingName" | "ratingCwId" | "source" | "campaignName" | "campaignCwId" | "primarySalesRepName" | "primarySalesRepIdentifier" | "primarySalesRepCwId" | "secondarySalesRepName" | "secondarySalesRepIdentifier" | "secondarySalesRepCwId" | "companyCwId" | "companyName" | "contactCwId" | "contactName" | "siteCwId" | "siteName" | "customerPO" | "totalSalesTax" | "probability" | "locationName" | "locationCwId" | "departmentName" | "departmentCwId" | "expectedCloseDate" | "pipelineChangeDate" | "dateBecameLead" | "closedDate" | "closedFlag" | "closedByName" | "closedByCwId" | "companyId" | "productSequence" | "cwLastUpdated" | "cwDateEntered" | "createdAt" | "updatedAt", ExtArgs["result"]["opportunity"]> +export type OpportunityOmit = 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 = { generatedQuotes?: boolean | Prisma.Opportunity$generatedQuotesArgs + type?: boolean | Prisma.OpportunityTypeDefaultArgs + stage?: boolean | Prisma.Opportunity$stageArgs + status?: boolean | Prisma.Opportunity$statusArgs + taxCode?: boolean | Prisma.Opportunity$taxCodeArgs + primarySalesRep?: boolean | Prisma.Opportunity$primarySalesRepArgs + secondarySalesRep?: boolean | Prisma.Opportunity$secondarySalesRepArgs company?: boolean | Prisma.Opportunity$companyArgs + contact?: boolean | Prisma.Opportunity$contactArgs + site?: boolean | Prisma.Opportunity$siteArgs + location?: boolean | Prisma.Opportunity$locationArgs + department?: boolean | Prisma.Opportunity$departmentArgs + products?: boolean | Prisma.Opportunity$productsArgs _count?: boolean | Prisma.OpportunityCountOutputTypeDefaultArgs } export type OpportunityIncludeCreateManyAndReturn = { + type?: boolean | Prisma.OpportunityTypeDefaultArgs + stage?: boolean | Prisma.Opportunity$stageArgs + status?: boolean | Prisma.Opportunity$statusArgs + taxCode?: boolean | Prisma.Opportunity$taxCodeArgs + primarySalesRep?: boolean | Prisma.Opportunity$primarySalesRepArgs + secondarySalesRep?: boolean | Prisma.Opportunity$secondarySalesRepArgs company?: boolean | Prisma.Opportunity$companyArgs + contact?: boolean | Prisma.Opportunity$contactArgs + site?: boolean | Prisma.Opportunity$siteArgs + location?: boolean | Prisma.Opportunity$locationArgs + department?: boolean | Prisma.Opportunity$departmentArgs } export type OpportunityIncludeUpdateManyAndReturn = { + type?: boolean | Prisma.OpportunityTypeDefaultArgs + stage?: boolean | Prisma.Opportunity$stageArgs + status?: boolean | Prisma.Opportunity$statusArgs + taxCode?: boolean | Prisma.Opportunity$taxCodeArgs + primarySalesRep?: boolean | Prisma.Opportunity$primarySalesRepArgs + secondarySalesRep?: boolean | Prisma.Opportunity$secondarySalesRepArgs company?: boolean | Prisma.Opportunity$companyArgs + contact?: boolean | Prisma.Opportunity$contactArgs + site?: boolean | Prisma.Opportunity$siteArgs + location?: boolean | Prisma.Opportunity$locationArgs + department?: boolean | Prisma.Opportunity$departmentArgs } export type $OpportunityPayload = { name: "Opportunity" objects: { generatedQuotes: Prisma.$GeneratedQuotesPayload[] + type: Prisma.$OpportunityTypePayload + stage: Prisma.$OpportunityStagePayload | null + status: Prisma.$OpportunityStatusPayload | null + taxCode: Prisma.$TaxCodePayload | null + primarySalesRep: Prisma.$UserPayload | null + secondarySalesRep: Prisma.$UserPayload | null company: Prisma.$CompanyPayload | null + contact: Prisma.$ContactPayload | null + site: Prisma.$CompanyAddressPayload | null + location: Prisma.$CorporateLocationPayload | null + department: Prisma.$InternalDepartmentPayload | null + products: Prisma.$ProductDataPayload[] } scalars: runtime.Types.Extensions.GetPayloadResult<{ - id: string - cwOpportunityId: number + id: number + uid: string name: string notes: string | null - typeName: string | null - typeCwId: number | null - stageName: string | null - stageCwId: number | null - statusName: string | null - statusCwId: number | null - priorityName: string | null - priorityCwId: number | null - ratingName: string | null - ratingCwId: number | null - source: string | null - campaignName: string | null - campaignCwId: number | null - primarySalesRepName: string | null - primarySalesRepIdentifier: string | null - primarySalesRepCwId: number | null - secondarySalesRepName: string | null - secondarySalesRepIdentifier: string | null - secondarySalesRepCwId: number | null - companyCwId: number | null - companyName: string | null - contactCwId: number | null - contactName: string | null - siteCwId: number | null - siteName: string | null - customerPO: string | null - totalSalesTax: number + oppNarrative: string | null + typeId: number + stageId: number | null + statusId: number | null + taxCodeId: number | null + interest: $Enums.OpportunityInterest | null probability: number - locationName: string | null - locationCwId: number | null - departmentName: string | null - departmentCwId: number | null + source: string | null + primarySalesRepId: string | null + secondarySalesRepId: string | null + companyId: number | null + contactId: number | null + siteId: number | null + customerPO: string | null + locationId: number | null + departmentId: number | null expectedCloseDate: Date | null pipelineChangeDate: Date | null dateBecameLead: Date | null closedDate: Date | null closedFlag: boolean - closedByName: string | null - closedByCwId: number | null - companyId: string | null + closedById: string | null productSequence: number[] - cwLastUpdated: Date | null - cwDateEntered: Date | null + updatedBy: string + eneteredBy: string createdAt: Date updatedAt: Date }, ExtArgs["result"]["opportunity"]> @@ -2833,7 +5151,18 @@ readonly fields: OpportunityFieldRefs; export interface Prisma__OpportunityClient extends Prisma.PrismaPromise { readonly [Symbol.toStringTag]: "PrismaPromise" generatedQuotes = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + type = {}>(args?: Prisma.Subset>): Prisma.Prisma__OpportunityTypeClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + stage = {}>(args?: Prisma.Subset>): Prisma.Prisma__OpportunityStageClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + status = {}>(args?: Prisma.Subset>): Prisma.Prisma__OpportunityStatusClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + taxCode = {}>(args?: Prisma.Subset>): Prisma.Prisma__TaxCodeClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + primarySalesRep = {}>(args?: Prisma.Subset>): Prisma.Prisma__UserClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + secondarySalesRep = {}>(args?: Prisma.Subset>): Prisma.Prisma__UserClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> company = {}>(args?: Prisma.Subset>): Prisma.Prisma__CompanyClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + contact = {}>(args?: Prisma.Subset>): Prisma.Prisma__ContactClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + site = {}>(args?: Prisma.Subset>): Prisma.Prisma__CompanyAddressClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + location = {}>(args?: Prisma.Subset>): Prisma.Prisma__CorporateLocationClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + department = {}>(args?: Prisma.Subset>): Prisma.Prisma__InternalDepartmentClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + products = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -2863,53 +5192,35 @@ export interface Prisma__OpportunityClient - readonly cwOpportunityId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly id: Prisma.FieldRef<"Opportunity", 'Int'> + readonly uid: Prisma.FieldRef<"Opportunity", 'String'> readonly name: Prisma.FieldRef<"Opportunity", 'String'> readonly notes: Prisma.FieldRef<"Opportunity", 'String'> - readonly typeName: Prisma.FieldRef<"Opportunity", 'String'> - readonly typeCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly stageName: Prisma.FieldRef<"Opportunity", 'String'> - readonly stageCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly statusName: Prisma.FieldRef<"Opportunity", 'String'> - readonly statusCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly priorityName: Prisma.FieldRef<"Opportunity", 'String'> - readonly priorityCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly ratingName: Prisma.FieldRef<"Opportunity", 'String'> - readonly ratingCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly source: Prisma.FieldRef<"Opportunity", 'String'> - readonly campaignName: Prisma.FieldRef<"Opportunity", 'String'> - readonly campaignCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly primarySalesRepName: Prisma.FieldRef<"Opportunity", 'String'> - readonly primarySalesRepIdentifier: Prisma.FieldRef<"Opportunity", 'String'> - readonly primarySalesRepCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly secondarySalesRepName: Prisma.FieldRef<"Opportunity", 'String'> - readonly secondarySalesRepIdentifier: Prisma.FieldRef<"Opportunity", 'String'> - readonly secondarySalesRepCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly companyCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly companyName: Prisma.FieldRef<"Opportunity", 'String'> - readonly contactCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly contactName: Prisma.FieldRef<"Opportunity", 'String'> - readonly siteCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly siteName: Prisma.FieldRef<"Opportunity", 'String'> - readonly customerPO: Prisma.FieldRef<"Opportunity", 'String'> - readonly totalSalesTax: Prisma.FieldRef<"Opportunity", 'Float'> + readonly oppNarrative: Prisma.FieldRef<"Opportunity", 'String'> + readonly typeId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly stageId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly statusId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly taxCodeId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly interest: Prisma.FieldRef<"Opportunity", 'OpportunityInterest'> readonly probability: Prisma.FieldRef<"Opportunity", 'Float'> - readonly locationName: Prisma.FieldRef<"Opportunity", 'String'> - readonly locationCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly departmentName: Prisma.FieldRef<"Opportunity", 'String'> - readonly departmentCwId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly source: Prisma.FieldRef<"Opportunity", 'String'> + readonly primarySalesRepId: Prisma.FieldRef<"Opportunity", 'String'> + readonly secondarySalesRepId: Prisma.FieldRef<"Opportunity", 'String'> + readonly companyId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly contactId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly siteId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly customerPO: Prisma.FieldRef<"Opportunity", 'String'> + readonly locationId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly departmentId: Prisma.FieldRef<"Opportunity", 'Int'> readonly expectedCloseDate: Prisma.FieldRef<"Opportunity", 'DateTime'> readonly pipelineChangeDate: Prisma.FieldRef<"Opportunity", 'DateTime'> readonly dateBecameLead: Prisma.FieldRef<"Opportunity", 'DateTime'> readonly closedDate: Prisma.FieldRef<"Opportunity", 'DateTime'> readonly closedFlag: Prisma.FieldRef<"Opportunity", 'Boolean'> - readonly closedByName: Prisma.FieldRef<"Opportunity", 'String'> - readonly closedByCwId: Prisma.FieldRef<"Opportunity", 'Int'> - readonly companyId: Prisma.FieldRef<"Opportunity", 'String'> + readonly closedById: Prisma.FieldRef<"Opportunity", 'String'> readonly productSequence: Prisma.FieldRef<"Opportunity", 'Int[]'> - readonly cwLastUpdated: Prisma.FieldRef<"Opportunity", 'DateTime'> - readonly cwDateEntered: Prisma.FieldRef<"Opportunity", 'DateTime'> + readonly updatedBy: Prisma.FieldRef<"Opportunity", 'String'> + readonly eneteredBy: Prisma.FieldRef<"Opportunity", 'String'> readonly createdAt: Prisma.FieldRef<"Opportunity", 'DateTime'> readonly updatedAt: Prisma.FieldRef<"Opportunity", 'DateTime'> } @@ -3108,6 +5419,11 @@ export type OpportunityFindManyArgs = { + /** + * Select specific fields to fetch from the OpportunityStage + */ + select?: Prisma.OpportunityStageSelect | null + /** + * Omit specific fields from the OpportunityStage + */ + omit?: Prisma.OpportunityStageOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.OpportunityStageInclude | null + where?: Prisma.OpportunityStageWhereInput +} + +/** + * Opportunity.status + */ +export type Opportunity$statusArgs = { + /** + * Select specific fields to fetch from the OpportunityStatus + */ + select?: Prisma.OpportunityStatusSelect | null + /** + * Omit specific fields from the OpportunityStatus + */ + omit?: Prisma.OpportunityStatusOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.OpportunityStatusInclude | null + where?: Prisma.OpportunityStatusWhereInput +} + +/** + * Opportunity.taxCode + */ +export type Opportunity$taxCodeArgs = { + /** + * Select specific fields to fetch from the TaxCode + */ + select?: Prisma.TaxCodeSelect | null + /** + * Omit specific fields from the TaxCode + */ + omit?: Prisma.TaxCodeOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.TaxCodeInclude | null + where?: Prisma.TaxCodeWhereInput +} + +/** + * Opportunity.primarySalesRep + */ +export type Opportunity$primarySalesRepArgs = { + /** + * Select specific fields to fetch from the User + */ + select?: Prisma.UserSelect | null + /** + * Omit specific fields from the User + */ + omit?: Prisma.UserOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null + where?: Prisma.UserWhereInput +} + +/** + * Opportunity.secondarySalesRep + */ +export type Opportunity$secondarySalesRepArgs = { + /** + * Select specific fields to fetch from the User + */ + select?: Prisma.UserSelect | null + /** + * Omit specific fields from the User + */ + omit?: Prisma.UserOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.UserInclude | null + where?: Prisma.UserWhereInput +} + /** * Opportunity.company */ @@ -3350,6 +5761,106 @@ export type Opportunity$companyArgs = { + /** + * Select specific fields to fetch from the Contact + */ + select?: Prisma.ContactSelect | null + /** + * Omit specific fields from the Contact + */ + omit?: Prisma.ContactOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ContactInclude | null + where?: Prisma.ContactWhereInput +} + +/** + * Opportunity.site + */ +export type Opportunity$siteArgs = { + /** + * Select specific fields to fetch from the CompanyAddress + */ + select?: Prisma.CompanyAddressSelect | null + /** + * Omit specific fields from the CompanyAddress + */ + omit?: Prisma.CompanyAddressOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CompanyAddressInclude | null + where?: Prisma.CompanyAddressWhereInput +} + +/** + * Opportunity.location + */ +export type Opportunity$locationArgs = { + /** + * Select specific fields to fetch from the CorporateLocation + */ + select?: Prisma.CorporateLocationSelect | null + /** + * Omit specific fields from the CorporateLocation + */ + omit?: Prisma.CorporateLocationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CorporateLocationInclude | null + where?: Prisma.CorporateLocationWhereInput +} + +/** + * Opportunity.department + */ +export type Opportunity$departmentArgs = { + /** + * Select specific fields to fetch from the InternalDepartment + */ + select?: Prisma.InternalDepartmentSelect | null + /** + * Omit specific fields from the InternalDepartment + */ + omit?: Prisma.InternalDepartmentOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.InternalDepartmentInclude | null + where?: Prisma.InternalDepartmentWhereInput +} + +/** + * Opportunity.products + */ +export type Opportunity$productsArgs = { + /** + * Select specific fields to fetch from the ProductData + */ + select?: Prisma.ProductDataSelect | null + /** + * Omit specific fields from the ProductData + */ + omit?: Prisma.ProductDataOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ProductDataInclude | null + where?: Prisma.ProductDataWhereInput + orderBy?: Prisma.ProductDataOrderByWithRelationInput | Prisma.ProductDataOrderByWithRelationInput[] + cursor?: Prisma.ProductDataWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ProductDataScalarFieldEnum | Prisma.ProductDataScalarFieldEnum[] +} + /** * Opportunity without action */ diff --git a/api/generated/prisma/models/Role.ts b/api/generated/prisma/models/Role.ts index 6b592f1..12f7356 100644 --- a/api/generated/prisma/models/Role.ts +++ b/api/generated/prisma/models/Role.ts @@ -1175,6 +1175,11 @@ export type RoleFindManyArgs | Prisma.SessionCreateWithoutUserInput[] | Prisma.SessionUncheckedCreateWithoutUserInput[] connectOrCreate?: Prisma.SessionCreateOrConnectWithoutUserInput | Prisma.SessionCreateOrConnectWithoutUserInput[] @@ -1208,6 +1196,11 @@ export type SessionFindManyArgs | string name?: Prisma.StringFilter<"UnifiSite"> | string siteId?: Prisma.StringFilter<"UnifiSite"> | string - companyId?: Prisma.StringNullableFilter<"UnifiSite"> | string | null + companyId?: Prisma.IntNullableFilter<"UnifiSite"> | number | null createdAt?: Prisma.DateTimeFilter<"UnifiSite"> | Date | string updatedAt?: Prisma.DateTimeFilter<"UnifiSite"> | Date | string company?: Prisma.XOR | null @@ -210,7 +244,7 @@ export type UnifiSiteWhereUniqueInput = Prisma.AtLeast<{ OR?: Prisma.UnifiSiteWhereInput[] NOT?: Prisma.UnifiSiteWhereInput | Prisma.UnifiSiteWhereInput[] name?: Prisma.StringFilter<"UnifiSite"> | string - companyId?: Prisma.StringNullableFilter<"UnifiSite"> | string | null + companyId?: Prisma.IntNullableFilter<"UnifiSite"> | number | null createdAt?: Prisma.DateTimeFilter<"UnifiSite"> | Date | string updatedAt?: Prisma.DateTimeFilter<"UnifiSite"> | Date | string company?: Prisma.XOR | null @@ -224,8 +258,10 @@ export type UnifiSiteOrderByWithAggregationInput = { createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder _count?: Prisma.UnifiSiteCountOrderByAggregateInput + _avg?: Prisma.UnifiSiteAvgOrderByAggregateInput _max?: Prisma.UnifiSiteMaxOrderByAggregateInput _min?: Prisma.UnifiSiteMinOrderByAggregateInput + _sum?: Prisma.UnifiSiteSumOrderByAggregateInput } export type UnifiSiteScalarWhereWithAggregatesInput = { @@ -235,7 +271,7 @@ export type UnifiSiteScalarWhereWithAggregatesInput = { id?: Prisma.StringWithAggregatesFilter<"UnifiSite"> | string name?: Prisma.StringWithAggregatesFilter<"UnifiSite"> | string siteId?: Prisma.StringWithAggregatesFilter<"UnifiSite"> | string - companyId?: Prisma.StringNullableWithAggregatesFilter<"UnifiSite"> | string | null + companyId?: Prisma.IntNullableWithAggregatesFilter<"UnifiSite"> | number | null createdAt?: Prisma.DateTimeWithAggregatesFilter<"UnifiSite"> | Date | string updatedAt?: Prisma.DateTimeWithAggregatesFilter<"UnifiSite"> | Date | string } @@ -253,7 +289,7 @@ export type UnifiSiteUncheckedCreateInput = { id?: string name: string siteId: string - companyId?: string | null + companyId?: number | null createdAt?: Date | string updatedAt?: Date | string } @@ -271,7 +307,7 @@ export type UnifiSiteUncheckedUpdateInput = { id?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string siteId?: Prisma.StringFieldUpdateOperationsInput | string - companyId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } @@ -280,7 +316,7 @@ export type UnifiSiteCreateManyInput = { id?: string name: string siteId: string - companyId?: string | null + companyId?: number | null createdAt?: Date | string updatedAt?: Date | string } @@ -297,7 +333,7 @@ export type UnifiSiteUncheckedUpdateManyInput = { id?: Prisma.StringFieldUpdateOperationsInput | string name?: Prisma.StringFieldUpdateOperationsInput | string siteId?: Prisma.StringFieldUpdateOperationsInput | string - companyId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + companyId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } @@ -311,6 +347,10 @@ export type UnifiSiteCountOrderByAggregateInput = { updatedAt?: Prisma.SortOrder } +export type UnifiSiteAvgOrderByAggregateInput = { + companyId?: Prisma.SortOrder +} + export type UnifiSiteMaxOrderByAggregateInput = { id?: Prisma.SortOrder name?: Prisma.SortOrder @@ -329,6 +369,10 @@ export type UnifiSiteMinOrderByAggregateInput = { updatedAt?: Prisma.SortOrder } +export type UnifiSiteSumOrderByAggregateInput = { + companyId?: Prisma.SortOrder +} + export type UnifiSiteListRelationFilter = { every?: Prisma.UnifiSiteWhereInput some?: Prisma.UnifiSiteWhereInput @@ -430,7 +474,7 @@ export type UnifiSiteScalarWhereInput = { id?: Prisma.StringFilter<"UnifiSite"> | string name?: Prisma.StringFilter<"UnifiSite"> | string siteId?: Prisma.StringFilter<"UnifiSite"> | string - companyId?: Prisma.StringNullableFilter<"UnifiSite"> | string | null + companyId?: Prisma.IntNullableFilter<"UnifiSite"> | number | null createdAt?: Prisma.DateTimeFilter<"UnifiSite"> | Date | string updatedAt?: Prisma.DateTimeFilter<"UnifiSite"> | Date | string } @@ -528,7 +572,7 @@ export type $UnifiSitePayload @@ -958,7 +1002,7 @@ export interface UnifiSiteFieldRefs { readonly id: Prisma.FieldRef<"UnifiSite", 'String'> readonly name: Prisma.FieldRef<"UnifiSite", 'String'> readonly siteId: Prisma.FieldRef<"UnifiSite", 'String'> - readonly companyId: Prisma.FieldRef<"UnifiSite", 'String'> + readonly companyId: Prisma.FieldRef<"UnifiSite", 'Int'> readonly createdAt: Prisma.FieldRef<"UnifiSite", 'DateTime'> readonly updatedAt: Prisma.FieldRef<"UnifiSite", 'DateTime'> } @@ -1157,6 +1201,11 @@ export type UnifiSiteFindManyArgs | string permissions?: Prisma.StringNullableFilter<"User"> | string | null login?: Prisma.StringFilter<"User"> | string - name?: Prisma.StringNullableFilter<"User"> | string | null + firstName?: Prisma.StringNullableFilter<"User"> | string | null + lastName?: Prisma.StringNullableFilter<"User"> | string | null email?: Prisma.StringFilter<"User"> | string - emailVerified?: Prisma.DateTimeNullableFilter<"User"> | Date | string | null image?: Prisma.StringNullableFilter<"User"> | string | null + title?: Prisma.StringNullableFilter<"User"> | string | null + active?: Prisma.BoolFilter<"User"> | boolean + hidden?: Prisma.BoolFilter<"User"> | boolean cwIdentifier?: Prisma.StringNullableFilter<"User"> | string | null - userId?: Prisma.StringFilter<"User"> | string + cwMemberId?: Prisma.IntNullableFilter<"User"> | number | null + userId?: Prisma.StringNullableFilter<"User"> | string | null token?: Prisma.StringNullableFilter<"User"> | string | null + updatedBy?: Prisma.StringNullableFilter<"User"> | string | null createdAt?: Prisma.DateTimeFilter<"User"> | Date | string updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string roles?: Prisma.RoleListRelationFilter + cwMember?: Prisma.XOR | null sessions?: Prisma.SessionListRelationFilter + companiesDeleted?: Prisma.CompanyListRelationFilter + companiesEntered?: Prisma.CompanyListRelationFilter + opportunities?: Prisma.OpportunityListRelationFilter + opportunitiesSecondary?: Prisma.OpportunityListRelationFilter generatedQuotes?: Prisma.GeneratedQuotesListRelationFilter + companyAddresses?: Prisma.CompanyAddressListRelationFilter + serviceTicketsOwned?: Prisma.ServiceTicketListRelationFilter + serviceTicketsClosed?: Prisma.ServiceTicketListRelationFilter + serviceTicketsCreated?: Prisma.ServiceTicketListRelationFilter + serviceTicketsUpdated?: Prisma.ServiceTicketListRelationFilter + serviceTicketNotes?: Prisma.ServiceTicketNoteListRelationFilter } export type UserOrderByWithRelationInput = { id?: Prisma.SortOrder permissions?: Prisma.SortOrderInput | Prisma.SortOrder login?: Prisma.SortOrder - name?: Prisma.SortOrderInput | Prisma.SortOrder + firstName?: Prisma.SortOrderInput | Prisma.SortOrder + lastName?: Prisma.SortOrderInput | Prisma.SortOrder email?: Prisma.SortOrder - emailVerified?: Prisma.SortOrderInput | Prisma.SortOrder image?: Prisma.SortOrderInput | Prisma.SortOrder + title?: Prisma.SortOrderInput | Prisma.SortOrder + active?: Prisma.SortOrder + hidden?: Prisma.SortOrder cwIdentifier?: Prisma.SortOrderInput | Prisma.SortOrder - userId?: Prisma.SortOrder + cwMemberId?: Prisma.SortOrderInput | Prisma.SortOrder + userId?: Prisma.SortOrderInput | Prisma.SortOrder token?: Prisma.SortOrderInput | Prisma.SortOrder + updatedBy?: Prisma.SortOrderInput | Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder roles?: Prisma.RoleOrderByRelationAggregateInput + cwMember?: Prisma.CwMemberOrderByWithRelationInput sessions?: Prisma.SessionOrderByRelationAggregateInput + companiesDeleted?: Prisma.CompanyOrderByRelationAggregateInput + companiesEntered?: Prisma.CompanyOrderByRelationAggregateInput + opportunities?: Prisma.OpportunityOrderByRelationAggregateInput + opportunitiesSecondary?: Prisma.OpportunityOrderByRelationAggregateInput generatedQuotes?: Prisma.GeneratedQuotesOrderByRelationAggregateInput + companyAddresses?: Prisma.CompanyAddressOrderByRelationAggregateInput + serviceTicketsOwned?: Prisma.ServiceTicketOrderByRelationAggregateInput + serviceTicketsClosed?: Prisma.ServiceTicketOrderByRelationAggregateInput + serviceTicketsCreated?: Prisma.ServiceTicketOrderByRelationAggregateInput + serviceTicketsUpdated?: Prisma.ServiceTicketOrderByRelationAggregateInput + serviceTicketNotes?: Prisma.ServiceTicketNoteOrderByRelationAggregateInput } export type UserWhereUniqueInput = Prisma.AtLeast<{ id?: string login?: string email?: string + cwIdentifier?: string + cwMemberId?: number userId?: string AND?: Prisma.UserWhereInput | Prisma.UserWhereInput[] OR?: Prisma.UserWhereInput[] NOT?: Prisma.UserWhereInput | Prisma.UserWhereInput[] permissions?: Prisma.StringNullableFilter<"User"> | string | null - name?: Prisma.StringNullableFilter<"User"> | string | null - emailVerified?: Prisma.DateTimeNullableFilter<"User"> | Date | string | null + firstName?: Prisma.StringNullableFilter<"User"> | string | null + lastName?: Prisma.StringNullableFilter<"User"> | string | null image?: Prisma.StringNullableFilter<"User"> | string | null - cwIdentifier?: Prisma.StringNullableFilter<"User"> | string | null + title?: Prisma.StringNullableFilter<"User"> | string | null + active?: Prisma.BoolFilter<"User"> | boolean + hidden?: Prisma.BoolFilter<"User"> | boolean token?: Prisma.StringNullableFilter<"User"> | string | null + updatedBy?: Prisma.StringNullableFilter<"User"> | string | null createdAt?: Prisma.DateTimeFilter<"User"> | Date | string updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string roles?: Prisma.RoleListRelationFilter + cwMember?: Prisma.XOR | null sessions?: Prisma.SessionListRelationFilter + companiesDeleted?: Prisma.CompanyListRelationFilter + companiesEntered?: Prisma.CompanyListRelationFilter + opportunities?: Prisma.OpportunityListRelationFilter + opportunitiesSecondary?: Prisma.OpportunityListRelationFilter generatedQuotes?: Prisma.GeneratedQuotesListRelationFilter -}, "id" | "login" | "email" | "userId"> + companyAddresses?: Prisma.CompanyAddressListRelationFilter + serviceTicketsOwned?: Prisma.ServiceTicketListRelationFilter + serviceTicketsClosed?: Prisma.ServiceTicketListRelationFilter + serviceTicketsCreated?: Prisma.ServiceTicketListRelationFilter + serviceTicketsUpdated?: Prisma.ServiceTicketListRelationFilter + serviceTicketNotes?: Prisma.ServiceTicketNoteListRelationFilter +}, "id" | "login" | "email" | "cwIdentifier" | "cwMemberId" | "userId"> export type UserOrderByWithAggregationInput = { id?: Prisma.SortOrder permissions?: Prisma.SortOrderInput | Prisma.SortOrder login?: Prisma.SortOrder - name?: Prisma.SortOrderInput | Prisma.SortOrder + firstName?: Prisma.SortOrderInput | Prisma.SortOrder + lastName?: Prisma.SortOrderInput | Prisma.SortOrder email?: Prisma.SortOrder - emailVerified?: Prisma.SortOrderInput | Prisma.SortOrder image?: Prisma.SortOrderInput | Prisma.SortOrder + title?: Prisma.SortOrderInput | Prisma.SortOrder + active?: Prisma.SortOrder + hidden?: Prisma.SortOrder cwIdentifier?: Prisma.SortOrderInput | Prisma.SortOrder - userId?: Prisma.SortOrder + cwMemberId?: Prisma.SortOrderInput | Prisma.SortOrder + userId?: Prisma.SortOrderInput | Prisma.SortOrder token?: Prisma.SortOrderInput | Prisma.SortOrder + updatedBy?: Prisma.SortOrderInput | Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder _count?: Prisma.UserCountOrderByAggregateInput + _avg?: Prisma.UserAvgOrderByAggregateInput _max?: Prisma.UserMaxOrderByAggregateInput _min?: Prisma.UserMinOrderByAggregateInput + _sum?: Prisma.UserSumOrderByAggregateInput } export type UserScalarWhereWithAggregatesInput = { @@ -307,13 +431,18 @@ export type UserScalarWhereWithAggregatesInput = { id?: Prisma.StringWithAggregatesFilter<"User"> | string permissions?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null login?: Prisma.StringWithAggregatesFilter<"User"> | string - name?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null + firstName?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null + lastName?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null email?: Prisma.StringWithAggregatesFilter<"User"> | string - emailVerified?: Prisma.DateTimeNullableWithAggregatesFilter<"User"> | Date | string | null image?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null + title?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null + active?: Prisma.BoolWithAggregatesFilter<"User"> | boolean + hidden?: Prisma.BoolWithAggregatesFilter<"User"> | boolean cwIdentifier?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null - userId?: Prisma.StringWithAggregatesFilter<"User"> | string + cwMemberId?: Prisma.IntNullableWithAggregatesFilter<"User"> | number | null + userId?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null token?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null + updatedBy?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null createdAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string updatedAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string } @@ -322,85 +451,150 @@ export type UserCreateInput = { id?: string permissions?: string | null login: string - name?: string | null + firstName?: string | null + lastName?: string | null email: string - emailVerified?: Date | string | null image?: string | null + title?: string | null + active?: boolean + hidden?: boolean cwIdentifier?: string | null - userId: string + userId?: string | null token?: string | null + updatedBy?: string | null createdAt?: Date | string updatedAt?: Date | string roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput } export type UserUncheckedCreateInput = { id?: string permissions?: string | null login: string - name?: string | null + firstName?: string | null + lastName?: string | null email: string - emailVerified?: Date | string | null image?: string | null + title?: string | null + active?: boolean + hidden?: boolean cwIdentifier?: string | null - userId: string + cwMemberId?: number | null + userId?: string | null token?: string | null + updatedBy?: string | null createdAt?: Date | string updatedAt?: Date | string roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput } export type UserUpdateInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput } export type UserUncheckedUpdateInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput } export type UserCreateManyInput = { id?: string permissions?: string | null login: string - name?: string | null + firstName?: string | null + lastName?: string | null email: string - emailVerified?: Date | string | null image?: string | null + title?: string | null + active?: boolean + hidden?: boolean cwIdentifier?: string | null - userId: string + cwMemberId?: number | null + userId?: string | null token?: string | null + updatedBy?: string | null createdAt?: Date | string updatedAt?: Date | string } @@ -409,13 +603,17 @@ export type UserUpdateManyMutationInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } @@ -424,13 +622,18 @@ export type UserUncheckedUpdateManyInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } @@ -444,28 +647,42 @@ export type UserCountOrderByAggregateInput = { id?: Prisma.SortOrder permissions?: Prisma.SortOrder login?: Prisma.SortOrder - name?: Prisma.SortOrder + firstName?: Prisma.SortOrder + lastName?: Prisma.SortOrder email?: Prisma.SortOrder - emailVerified?: Prisma.SortOrder image?: Prisma.SortOrder + title?: Prisma.SortOrder + active?: Prisma.SortOrder + hidden?: Prisma.SortOrder cwIdentifier?: Prisma.SortOrder + cwMemberId?: Prisma.SortOrder userId?: Prisma.SortOrder token?: Prisma.SortOrder + updatedBy?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } +export type UserAvgOrderByAggregateInput = { + cwMemberId?: Prisma.SortOrder +} + export type UserMaxOrderByAggregateInput = { id?: Prisma.SortOrder permissions?: Prisma.SortOrder login?: Prisma.SortOrder - name?: Prisma.SortOrder + firstName?: Prisma.SortOrder + lastName?: Prisma.SortOrder email?: Prisma.SortOrder - emailVerified?: Prisma.SortOrder image?: Prisma.SortOrder + title?: Prisma.SortOrder + active?: Prisma.SortOrder + hidden?: Prisma.SortOrder cwIdentifier?: Prisma.SortOrder + cwMemberId?: Prisma.SortOrder userId?: Prisma.SortOrder token?: Prisma.SortOrder + updatedBy?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } @@ -474,17 +691,26 @@ export type UserMinOrderByAggregateInput = { id?: Prisma.SortOrder permissions?: Prisma.SortOrder login?: Prisma.SortOrder - name?: Prisma.SortOrder + firstName?: Prisma.SortOrder + lastName?: Prisma.SortOrder email?: Prisma.SortOrder - emailVerified?: Prisma.SortOrder image?: Prisma.SortOrder + title?: Prisma.SortOrder + active?: Prisma.SortOrder + hidden?: Prisma.SortOrder cwIdentifier?: Prisma.SortOrder + cwMemberId?: Prisma.SortOrder userId?: Prisma.SortOrder token?: Prisma.SortOrder + updatedBy?: Prisma.SortOrder createdAt?: Prisma.SortOrder updatedAt?: Prisma.SortOrder } +export type UserSumOrderByAggregateInput = { + cwMemberId?: Prisma.SortOrder +} + export type UserListRelationFilter = { every?: Prisma.UserWhereInput some?: Prisma.UserWhereInput @@ -514,8 +740,12 @@ export type UserUpdateOneRequiredWithoutSessionsNestedInput = { update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutSessionsInput> } -export type NullableStringFieldUpdateOperationsInput = { - set?: string | null +export type NullableIntFieldUpdateOperationsInput = { + set?: number | null + increment?: number + decrement?: number + multiply?: number + divide?: number } export type UserCreateNestedManyWithoutRolesInput = { @@ -556,6 +786,164 @@ export type UserUncheckedUpdateManyWithoutRolesNestedInput = { deleteMany?: Prisma.UserScalarWhereInput | Prisma.UserScalarWhereInput[] } +export type UserCreateNestedOneWithoutCompaniesDeletedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCompaniesDeletedInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserCreateNestedOneWithoutCompaniesEnteredInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCompaniesEnteredInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneWithoutCompaniesDeletedNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCompaniesDeletedInput + upsert?: Prisma.UserUpsertWithoutCompaniesDeletedInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutCompaniesDeletedInput> +} + +export type UserUpdateOneWithoutCompaniesEnteredNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCompaniesEnteredInput + upsert?: Prisma.UserUpsertWithoutCompaniesEnteredInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutCompaniesEnteredInput> +} + +export type UserCreateNestedOneWithoutCompanyAddressesInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCompanyAddressesInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneWithoutCompanyAddressesNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCompanyAddressesInput + upsert?: Prisma.UserUpsertWithoutCompanyAddressesInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutCompanyAddressesInput> +} + +export type UserCreateNestedOneWithoutServiceTicketsOwnedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutServiceTicketsOwnedInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserCreateNestedOneWithoutServiceTicketsClosedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutServiceTicketsClosedInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserCreateNestedOneWithoutServiceTicketsCreatedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutServiceTicketsCreatedInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserCreateNestedOneWithoutServiceTicketsUpdatedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutServiceTicketsUpdatedInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneWithoutServiceTicketsOwnedNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutServiceTicketsOwnedInput + upsert?: Prisma.UserUpsertWithoutServiceTicketsOwnedInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutServiceTicketsOwnedInput> +} + +export type UserUpdateOneWithoutServiceTicketsClosedNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutServiceTicketsClosedInput + upsert?: Prisma.UserUpsertWithoutServiceTicketsClosedInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutServiceTicketsClosedInput> +} + +export type UserUpdateOneWithoutServiceTicketsCreatedNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutServiceTicketsCreatedInput + upsert?: Prisma.UserUpsertWithoutServiceTicketsCreatedInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutServiceTicketsCreatedInput> +} + +export type UserUpdateOneWithoutServiceTicketsUpdatedNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutServiceTicketsUpdatedInput + upsert?: Prisma.UserUpsertWithoutServiceTicketsUpdatedInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutServiceTicketsUpdatedInput> +} + +export type UserCreateNestedOneWithoutServiceTicketNotesInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutServiceTicketNotesInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneRequiredWithoutServiceTicketNotesNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutServiceTicketNotesInput + upsert?: Prisma.UserUpsertWithoutServiceTicketNotesInput + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutServiceTicketNotesInput> +} + +export type UserCreateNestedOneWithoutOpportunitiesInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutOpportunitiesInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserCreateNestedOneWithoutOpportunitiesSecondaryInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutOpportunitiesSecondaryInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneWithoutOpportunitiesNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutOpportunitiesInput + upsert?: Prisma.UserUpsertWithoutOpportunitiesInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutOpportunitiesInput> +} + +export type UserUpdateOneWithoutOpportunitiesSecondaryNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutOpportunitiesSecondaryInput + upsert?: Prisma.UserUpsertWithoutOpportunitiesSecondaryInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutOpportunitiesSecondaryInput> +} + export type UserCreateNestedOneWithoutGeneratedQuotesInput = { create?: Prisma.XOR connectOrCreate?: Prisma.UserCreateOrConnectWithoutGeneratedQuotesInput @@ -572,38 +960,100 @@ export type UserUpdateOneWithoutGeneratedQuotesNestedInput = { update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutGeneratedQuotesInput> } +export type UserCreateNestedOneWithoutCwMemberInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCwMemberInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUncheckedCreateNestedOneWithoutCwMemberInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCwMemberInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneWithoutCwMemberNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCwMemberInput + upsert?: Prisma.UserUpsertWithoutCwMemberInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutCwMemberInput> +} + +export type UserUncheckedUpdateOneWithoutCwMemberNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutCwMemberInput + upsert?: Prisma.UserUpsertWithoutCwMemberInput + disconnect?: Prisma.UserWhereInput | boolean + delete?: Prisma.UserWhereInput | boolean + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutCwMemberInput> +} + export type UserCreateWithoutSessionsInput = { id?: string permissions?: string | null login: string - name?: string | null + firstName?: string | null + lastName?: string | null email: string - emailVerified?: Date | string | null image?: string | null + title?: string | null + active?: boolean + hidden?: boolean cwIdentifier?: string | null - userId: string + userId?: string | null token?: string | null + updatedBy?: string | null createdAt?: Date | string updatedAt?: Date | string roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput } export type UserUncheckedCreateWithoutSessionsInput = { id?: string permissions?: string | null login: string - name?: string | null + firstName?: string | null + lastName?: string | null email: string - emailVerified?: Date | string | null image?: string | null + title?: string | null + active?: boolean + hidden?: boolean cwIdentifier?: string | null - userId: string + cwMemberId?: number | null + userId?: string | null token?: string | null + updatedBy?: string | null createdAt?: Date | string updatedAt?: Date | string roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput } export type UserCreateOrConnectWithoutSessionsInput = { @@ -626,68 +1076,128 @@ export type UserUpdateWithoutSessionsInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput } export type UserUncheckedUpdateWithoutSessionsInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput } export type UserCreateWithoutRolesInput = { id?: string permissions?: string | null login: string - name?: string | null + firstName?: string | null + lastName?: string | null email: string - emailVerified?: Date | string | null image?: string | null + title?: string | null + active?: boolean + hidden?: boolean cwIdentifier?: string | null - userId: string + userId?: string | null token?: string | null + updatedBy?: string | null createdAt?: Date | string updatedAt?: Date | string + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput } export type UserUncheckedCreateWithoutRolesInput = { id?: string permissions?: string | null login: string - name?: string | null + firstName?: string | null + lastName?: string | null email: string - emailVerified?: Date | string | null image?: string | null + title?: string | null + active?: boolean + hidden?: boolean cwIdentifier?: string | null - userId: string + cwMemberId?: number | null + userId?: string | null token?: string | null + updatedBy?: string | null createdAt?: Date | string updatedAt?: Date | string sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput } export type UserCreateOrConnectWithoutRolesInput = { @@ -718,49 +1228,1524 @@ export type UserScalarWhereInput = { id?: Prisma.StringFilter<"User"> | string permissions?: Prisma.StringNullableFilter<"User"> | string | null login?: Prisma.StringFilter<"User"> | string - name?: Prisma.StringNullableFilter<"User"> | string | null + firstName?: Prisma.StringNullableFilter<"User"> | string | null + lastName?: Prisma.StringNullableFilter<"User"> | string | null email?: Prisma.StringFilter<"User"> | string - emailVerified?: Prisma.DateTimeNullableFilter<"User"> | Date | string | null image?: Prisma.StringNullableFilter<"User"> | string | null + title?: Prisma.StringNullableFilter<"User"> | string | null + active?: Prisma.BoolFilter<"User"> | boolean + hidden?: Prisma.BoolFilter<"User"> | boolean cwIdentifier?: Prisma.StringNullableFilter<"User"> | string | null - userId?: Prisma.StringFilter<"User"> | string + cwMemberId?: Prisma.IntNullableFilter<"User"> | number | null + userId?: Prisma.StringNullableFilter<"User"> | string | null token?: Prisma.StringNullableFilter<"User"> | string | null + updatedBy?: Prisma.StringNullableFilter<"User"> | string | null createdAt?: Prisma.DateTimeFilter<"User"> | Date | string updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string } +export type UserCreateWithoutCompaniesDeletedInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput +} + +export type UserUncheckedCreateWithoutCompaniesDeletedInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + cwMemberId?: number | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput +} + +export type UserCreateOrConnectWithoutCompaniesDeletedInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserCreateWithoutCompaniesEnteredInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput +} + +export type UserUncheckedCreateWithoutCompaniesEnteredInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + cwMemberId?: number | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput +} + +export type UserCreateOrConnectWithoutCompaniesEnteredInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutCompaniesDeletedInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutCompaniesDeletedInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutCompaniesDeletedInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput +} + +export type UserUncheckedUpdateWithoutCompaniesDeletedInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput +} + +export type UserUpsertWithoutCompaniesEnteredInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutCompaniesEnteredInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutCompaniesEnteredInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput +} + +export type UserUncheckedUpdateWithoutCompaniesEnteredInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput +} + +export type UserCreateWithoutCompanyAddressesInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput +} + +export type UserUncheckedCreateWithoutCompanyAddressesInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + cwMemberId?: number | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput +} + +export type UserCreateOrConnectWithoutCompanyAddressesInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutCompanyAddressesInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutCompanyAddressesInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutCompanyAddressesInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput +} + +export type UserUncheckedUpdateWithoutCompanyAddressesInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput +} + +export type UserCreateWithoutServiceTicketsOwnedInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput +} + +export type UserUncheckedCreateWithoutServiceTicketsOwnedInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + cwMemberId?: number | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput +} + +export type UserCreateOrConnectWithoutServiceTicketsOwnedInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserCreateWithoutServiceTicketsClosedInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput +} + +export type UserUncheckedCreateWithoutServiceTicketsClosedInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + cwMemberId?: number | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput +} + +export type UserCreateOrConnectWithoutServiceTicketsClosedInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserCreateWithoutServiceTicketsCreatedInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput +} + +export type UserUncheckedCreateWithoutServiceTicketsCreatedInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + cwMemberId?: number | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput +} + +export type UserCreateOrConnectWithoutServiceTicketsCreatedInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserCreateWithoutServiceTicketsUpdatedInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput +} + +export type UserUncheckedCreateWithoutServiceTicketsUpdatedInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + cwMemberId?: number | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput +} + +export type UserCreateOrConnectWithoutServiceTicketsUpdatedInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutServiceTicketsOwnedInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutServiceTicketsOwnedInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutServiceTicketsOwnedInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput +} + +export type UserUncheckedUpdateWithoutServiceTicketsOwnedInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput +} + +export type UserUpsertWithoutServiceTicketsClosedInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutServiceTicketsClosedInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutServiceTicketsClosedInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput +} + +export type UserUncheckedUpdateWithoutServiceTicketsClosedInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput +} + +export type UserUpsertWithoutServiceTicketsCreatedInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutServiceTicketsCreatedInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutServiceTicketsCreatedInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput +} + +export type UserUncheckedUpdateWithoutServiceTicketsCreatedInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput +} + +export type UserUpsertWithoutServiceTicketsUpdatedInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutServiceTicketsUpdatedInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutServiceTicketsUpdatedInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput +} + +export type UserUncheckedUpdateWithoutServiceTicketsUpdatedInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput +} + +export type UserCreateWithoutServiceTicketNotesInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput +} + +export type UserUncheckedCreateWithoutServiceTicketNotesInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + cwMemberId?: number | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput +} + +export type UserCreateOrConnectWithoutServiceTicketNotesInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutServiceTicketNotesInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutServiceTicketNotesInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutServiceTicketNotesInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput +} + +export type UserUncheckedUpdateWithoutServiceTicketNotesInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput +} + +export type UserCreateWithoutOpportunitiesInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput +} + +export type UserUncheckedCreateWithoutOpportunitiesInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + cwMemberId?: number | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput +} + +export type UserCreateOrConnectWithoutOpportunitiesInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserCreateWithoutOpportunitiesSecondaryInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput +} + +export type UserUncheckedCreateWithoutOpportunitiesSecondaryInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + cwMemberId?: number | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput +} + +export type UserCreateOrConnectWithoutOpportunitiesSecondaryInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutOpportunitiesInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutOpportunitiesInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutOpportunitiesInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput +} + +export type UserUncheckedUpdateWithoutOpportunitiesInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput +} + +export type UserUpsertWithoutOpportunitiesSecondaryInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutOpportunitiesSecondaryInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutOpportunitiesSecondaryInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput +} + +export type UserUncheckedUpdateWithoutOpportunitiesSecondaryInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput +} + export type UserCreateWithoutGeneratedQuotesInput = { id?: string permissions?: string | null login: string - name?: string | null + firstName?: string | null + lastName?: string | null email: string - emailVerified?: Date | string | null image?: string | null + title?: string | null + active?: boolean + hidden?: boolean cwIdentifier?: string | null - userId: string + userId?: string | null token?: string | null + updatedBy?: string | null createdAt?: Date | string updatedAt?: Date | string roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + cwMember?: Prisma.CwMemberCreateNestedOneWithoutUserInput sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput } export type UserUncheckedCreateWithoutGeneratedQuotesInput = { id?: string permissions?: string | null login: string - name?: string | null + firstName?: string | null + lastName?: string | null email: string - emailVerified?: Date | string | null image?: string | null + title?: string | null + active?: boolean + hidden?: boolean cwIdentifier?: string | null - userId: string + cwMemberId?: number | null + userId?: string | null token?: string | null + updatedBy?: string | null createdAt?: Date | string updatedAt?: Date | string roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput } export type UserCreateOrConnectWithoutGeneratedQuotesInput = { @@ -783,81 +2768,290 @@ export type UserUpdateWithoutGeneratedQuotesInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput } export type UserUncheckedUpdateWithoutGeneratedQuotesInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput +} + +export type UserCreateWithoutCwMemberInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteCreateNestedManyWithoutAuthorInput +} + +export type UserUncheckedCreateWithoutCwMemberInput = { + id?: string + permissions?: string | null + login: string + firstName?: string | null + lastName?: string | null + email: string + image?: string | null + title?: string | null + active?: boolean + hidden?: boolean + cwIdentifier?: string | null + userId?: string | null + token?: string | null + updatedBy?: string | null + createdAt?: Date | string + updatedAt?: Date | string + roles?: Prisma.RoleUncheckedCreateNestedManyWithoutUsersInput + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + companiesDeleted?: Prisma.CompanyUncheckedCreateNestedManyWithoutDeletedByInput + companiesEntered?: Prisma.CompanyUncheckedCreateNestedManyWithoutEnteredByInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutPrimarySalesRepInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedCreateNestedManyWithoutSecondarySalesRepInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedCreateNestedManyWithoutCreatedByInput + companyAddresses?: Prisma.CompanyAddressUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutTicketOwnerInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutClosedByInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutCreatedByInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedCreateNestedManyWithoutUpdatedByInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedCreateNestedManyWithoutAuthorInput +} + +export type UserCreateOrConnectWithoutCwMemberInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutCwMemberInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutCwMemberInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutCwMemberInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput +} + +export type UserUncheckedUpdateWithoutCwMemberInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + login?: Prisma.StringFieldUpdateOperationsInput | string + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + email?: Prisma.StringFieldUpdateOperationsInput | string + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean + cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + roles?: Prisma.RoleUncheckedUpdateManyWithoutUsersNestedInput + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput + generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput } export type UserUpdateWithoutRolesInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + cwMember?: Prisma.CwMemberUpdateOneWithoutUserNestedInput sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUpdateManyWithoutSecondarySalesRepNestedInput generatedQuotes?: Prisma.GeneratedQuotesUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUpdateManyWithoutAuthorNestedInput } export type UserUncheckedUpdateWithoutRolesInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + companiesDeleted?: Prisma.CompanyUncheckedUpdateManyWithoutDeletedByNestedInput + companiesEntered?: Prisma.CompanyUncheckedUpdateManyWithoutEnteredByNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutPrimarySalesRepNestedInput + opportunitiesSecondary?: Prisma.OpportunityUncheckedUpdateManyWithoutSecondarySalesRepNestedInput generatedQuotes?: Prisma.GeneratedQuotesUncheckedUpdateManyWithoutCreatedByNestedInput + companyAddresses?: Prisma.CompanyAddressUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketsOwned?: Prisma.ServiceTicketUncheckedUpdateManyWithoutTicketOwnerNestedInput + serviceTicketsClosed?: Prisma.ServiceTicketUncheckedUpdateManyWithoutClosedByNestedInput + serviceTicketsCreated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutCreatedByNestedInput + serviceTicketsUpdated?: Prisma.ServiceTicketUncheckedUpdateManyWithoutUpdatedByNestedInput + serviceTicketNotes?: Prisma.ServiceTicketNoteUncheckedUpdateManyWithoutAuthorNestedInput } export type UserUncheckedUpdateManyWithoutRolesInput = { id?: Prisma.StringFieldUpdateOperationsInput | string permissions?: Prisma.NullableStringFieldUpdateOperationsInput | string | null login?: Prisma.StringFieldUpdateOperationsInput | string - name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + firstName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + lastName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null email?: Prisma.StringFieldUpdateOperationsInput | string - emailVerified?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + title?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + active?: Prisma.BoolFieldUpdateOperationsInput | boolean + hidden?: Prisma.BoolFieldUpdateOperationsInput | boolean cwIdentifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null - userId?: Prisma.StringFieldUpdateOperationsInput | string + cwMemberId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null token?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + updatedBy?: Prisma.NullableStringFieldUpdateOperationsInput | string | null createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string } @@ -870,13 +3064,33 @@ export type UserUncheckedUpdateManyWithoutRolesInput = { export type UserCountOutputType = { roles: number sessions: number + companiesDeleted: number + companiesEntered: number + opportunities: number + opportunitiesSecondary: number generatedQuotes: number + companyAddresses: number + serviceTicketsOwned: number + serviceTicketsClosed: number + serviceTicketsCreated: number + serviceTicketsUpdated: number + serviceTicketNotes: number } export type UserCountOutputTypeSelect = { roles?: boolean | UserCountOutputTypeCountRolesArgs sessions?: boolean | UserCountOutputTypeCountSessionsArgs + companiesDeleted?: boolean | UserCountOutputTypeCountCompaniesDeletedArgs + companiesEntered?: boolean | UserCountOutputTypeCountCompaniesEnteredArgs + opportunities?: boolean | UserCountOutputTypeCountOpportunitiesArgs + opportunitiesSecondary?: boolean | UserCountOutputTypeCountOpportunitiesSecondaryArgs generatedQuotes?: boolean | UserCountOutputTypeCountGeneratedQuotesArgs + companyAddresses?: boolean | UserCountOutputTypeCountCompanyAddressesArgs + serviceTicketsOwned?: boolean | UserCountOutputTypeCountServiceTicketsOwnedArgs + serviceTicketsClosed?: boolean | UserCountOutputTypeCountServiceTicketsClosedArgs + serviceTicketsCreated?: boolean | UserCountOutputTypeCountServiceTicketsCreatedArgs + serviceTicketsUpdated?: boolean | UserCountOutputTypeCountServiceTicketsUpdatedArgs + serviceTicketNotes?: boolean | UserCountOutputTypeCountServiceTicketNotesArgs } /** @@ -903,6 +3117,34 @@ export type UserCountOutputTypeCountSessionsArgs = { + where?: Prisma.CompanyWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountCompaniesEnteredArgs = { + where?: Prisma.CompanyWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountOpportunitiesArgs = { + where?: Prisma.OpportunityWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountOpportunitiesSecondaryArgs = { + where?: Prisma.OpportunityWhereInput +} + /** * UserCountOutputType without action */ @@ -910,23 +3152,81 @@ export type UserCountOutputTypeCountGeneratedQuotesArgs = { + where?: Prisma.CompanyAddressWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountServiceTicketsOwnedArgs = { + where?: Prisma.ServiceTicketWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountServiceTicketsClosedArgs = { + where?: Prisma.ServiceTicketWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountServiceTicketsCreatedArgs = { + where?: Prisma.ServiceTicketWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountServiceTicketsUpdatedArgs = { + where?: Prisma.ServiceTicketWhereInput +} + +/** + * UserCountOutputType without action + */ +export type UserCountOutputTypeCountServiceTicketNotesArgs = { + where?: Prisma.ServiceTicketNoteWhereInput +} + export type UserSelect = runtime.Types.Extensions.GetSelect<{ id?: boolean permissions?: boolean login?: boolean - name?: boolean + firstName?: boolean + lastName?: boolean email?: boolean - emailVerified?: boolean image?: boolean + title?: boolean + active?: boolean + hidden?: boolean cwIdentifier?: boolean + cwMemberId?: boolean userId?: boolean token?: boolean + updatedBy?: boolean createdAt?: boolean updatedAt?: boolean roles?: boolean | Prisma.User$rolesArgs + cwMember?: boolean | Prisma.User$cwMemberArgs sessions?: boolean | Prisma.User$sessionsArgs + companiesDeleted?: boolean | Prisma.User$companiesDeletedArgs + companiesEntered?: boolean | Prisma.User$companiesEnteredArgs + opportunities?: boolean | Prisma.User$opportunitiesArgs + opportunitiesSecondary?: boolean | Prisma.User$opportunitiesSecondaryArgs generatedQuotes?: boolean | Prisma.User$generatedQuotesArgs + companyAddresses?: boolean | Prisma.User$companyAddressesArgs + serviceTicketsOwned?: boolean | Prisma.User$serviceTicketsOwnedArgs + serviceTicketsClosed?: boolean | Prisma.User$serviceTicketsClosedArgs + serviceTicketsCreated?: boolean | Prisma.User$serviceTicketsCreatedArgs + serviceTicketsUpdated?: boolean | Prisma.User$serviceTicketsUpdatedArgs + serviceTicketNotes?: boolean | Prisma.User$serviceTicketNotesArgs _count?: boolean | Prisma.UserCountOutputTypeDefaultArgs }, ExtArgs["result"]["user"]> @@ -934,75 +3234,123 @@ export type UserSelectCreateManyAndReturn }, ExtArgs["result"]["user"]> export type UserSelectUpdateManyAndReturn = runtime.Types.Extensions.GetSelect<{ id?: boolean permissions?: boolean login?: boolean - name?: boolean + firstName?: boolean + lastName?: boolean email?: boolean - emailVerified?: boolean image?: boolean + title?: boolean + active?: boolean + hidden?: boolean cwIdentifier?: boolean + cwMemberId?: boolean userId?: boolean token?: boolean + updatedBy?: boolean createdAt?: boolean updatedAt?: boolean + cwMember?: boolean | Prisma.User$cwMemberArgs }, ExtArgs["result"]["user"]> export type UserSelectScalar = { id?: boolean permissions?: boolean login?: boolean - name?: boolean + firstName?: boolean + lastName?: boolean email?: boolean - emailVerified?: boolean image?: boolean + title?: boolean + active?: boolean + hidden?: boolean cwIdentifier?: boolean + cwMemberId?: boolean userId?: boolean token?: boolean + updatedBy?: boolean createdAt?: boolean updatedAt?: boolean } -export type UserOmit = runtime.Types.Extensions.GetOmit<"id" | "permissions" | "login" | "name" | "email" | "emailVerified" | "image" | "cwIdentifier" | "userId" | "token" | "createdAt" | "updatedAt", ExtArgs["result"]["user"]> +export type UserOmit = runtime.Types.Extensions.GetOmit<"id" | "permissions" | "login" | "firstName" | "lastName" | "email" | "image" | "title" | "active" | "hidden" | "cwIdentifier" | "cwMemberId" | "userId" | "token" | "updatedBy" | "createdAt" | "updatedAt", ExtArgs["result"]["user"]> export type UserInclude = { roles?: boolean | Prisma.User$rolesArgs + cwMember?: boolean | Prisma.User$cwMemberArgs sessions?: boolean | Prisma.User$sessionsArgs + companiesDeleted?: boolean | Prisma.User$companiesDeletedArgs + companiesEntered?: boolean | Prisma.User$companiesEnteredArgs + opportunities?: boolean | Prisma.User$opportunitiesArgs + opportunitiesSecondary?: boolean | Prisma.User$opportunitiesSecondaryArgs generatedQuotes?: boolean | Prisma.User$generatedQuotesArgs + companyAddresses?: boolean | Prisma.User$companyAddressesArgs + serviceTicketsOwned?: boolean | Prisma.User$serviceTicketsOwnedArgs + serviceTicketsClosed?: boolean | Prisma.User$serviceTicketsClosedArgs + serviceTicketsCreated?: boolean | Prisma.User$serviceTicketsCreatedArgs + serviceTicketsUpdated?: boolean | Prisma.User$serviceTicketsUpdatedArgs + serviceTicketNotes?: boolean | Prisma.User$serviceTicketNotesArgs _count?: boolean | Prisma.UserCountOutputTypeDefaultArgs } -export type UserIncludeCreateManyAndReturn = {} -export type UserIncludeUpdateManyAndReturn = {} +export type UserIncludeCreateManyAndReturn = { + cwMember?: boolean | Prisma.User$cwMemberArgs +} +export type UserIncludeUpdateManyAndReturn = { + cwMember?: boolean | Prisma.User$cwMemberArgs +} export type $UserPayload = { name: "User" objects: { roles: Prisma.$RolePayload[] + cwMember: Prisma.$CwMemberPayload | null sessions: Prisma.$SessionPayload[] + companiesDeleted: Prisma.$CompanyPayload[] + companiesEntered: Prisma.$CompanyPayload[] + opportunities: Prisma.$OpportunityPayload[] + opportunitiesSecondary: Prisma.$OpportunityPayload[] generatedQuotes: Prisma.$GeneratedQuotesPayload[] + companyAddresses: Prisma.$CompanyAddressPayload[] + serviceTicketsOwned: Prisma.$ServiceTicketPayload[] + serviceTicketsClosed: Prisma.$ServiceTicketPayload[] + serviceTicketsCreated: Prisma.$ServiceTicketPayload[] + serviceTicketsUpdated: Prisma.$ServiceTicketPayload[] + serviceTicketNotes: Prisma.$ServiceTicketNotePayload[] } scalars: runtime.Types.Extensions.GetPayloadResult<{ id: string permissions: string | null login: string - name: string | null + firstName: string | null + lastName: string | null email: string - emailVerified: Date | null image: string | null + title: string | null + active: boolean + hidden: boolean cwIdentifier: string | null - userId: string + cwMemberId: number | null + userId: string | null token: string | null + updatedBy: string | null createdAt: Date updatedAt: Date }, ExtArgs["result"]["user"]> @@ -1400,8 +3748,19 @@ readonly fields: UserFieldRefs; export interface Prisma__UserClient extends Prisma.PrismaPromise { readonly [Symbol.toStringTag]: "PrismaPromise" roles = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + cwMember = {}>(args?: Prisma.Subset>): Prisma.Prisma__CwMemberClient, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> sessions = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + companiesDeleted = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + companiesEntered = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + opportunities = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + opportunitiesSecondary = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> generatedQuotes = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + companyAddresses = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + serviceTicketsOwned = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + serviceTicketsClosed = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + serviceTicketsCreated = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + serviceTicketsUpdated = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + serviceTicketNotes = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -1434,13 +3793,18 @@ export interface UserFieldRefs { readonly id: Prisma.FieldRef<"User", 'String'> readonly permissions: Prisma.FieldRef<"User", 'String'> readonly login: Prisma.FieldRef<"User", 'String'> - readonly name: Prisma.FieldRef<"User", 'String'> + readonly firstName: Prisma.FieldRef<"User", 'String'> + readonly lastName: Prisma.FieldRef<"User", 'String'> readonly email: Prisma.FieldRef<"User", 'String'> - readonly emailVerified: Prisma.FieldRef<"User", 'DateTime'> readonly image: Prisma.FieldRef<"User", 'String'> + readonly title: Prisma.FieldRef<"User", 'String'> + readonly active: Prisma.FieldRef<"User", 'Boolean'> + readonly hidden: Prisma.FieldRef<"User", 'Boolean'> readonly cwIdentifier: Prisma.FieldRef<"User", 'String'> + readonly cwMemberId: Prisma.FieldRef<"User", 'Int'> readonly userId: Prisma.FieldRef<"User", 'String'> readonly token: Prisma.FieldRef<"User", 'String'> + readonly updatedBy: Prisma.FieldRef<"User", 'String'> readonly createdAt: Prisma.FieldRef<"User", 'DateTime'> readonly updatedAt: Prisma.FieldRef<"User", 'DateTime'> } @@ -1639,6 +4003,11 @@ export type UserFindManyArgs | null } /** @@ -1762,6 +4135,10 @@ export type UserUpdateManyAndReturnArgs | null } /** @@ -1854,6 +4231,25 @@ export type User$rolesArgs = { + /** + * Select specific fields to fetch from the CwMember + */ + select?: Prisma.CwMemberSelect | null + /** + * Omit specific fields from the CwMember + */ + omit?: Prisma.CwMemberOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CwMemberInclude | null + where?: Prisma.CwMemberWhereInput +} + /** * User.sessions */ @@ -1878,6 +4274,102 @@ export type User$sessionsArgs = { + /** + * Select specific fields to fetch from the Company + */ + select?: Prisma.CompanySelect | null + /** + * Omit specific fields from the Company + */ + omit?: Prisma.CompanyOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CompanyInclude | null + where?: Prisma.CompanyWhereInput + orderBy?: Prisma.CompanyOrderByWithRelationInput | Prisma.CompanyOrderByWithRelationInput[] + cursor?: Prisma.CompanyWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.CompanyScalarFieldEnum | Prisma.CompanyScalarFieldEnum[] +} + +/** + * User.companiesEntered + */ +export type User$companiesEnteredArgs = { + /** + * Select specific fields to fetch from the Company + */ + select?: Prisma.CompanySelect | null + /** + * Omit specific fields from the Company + */ + omit?: Prisma.CompanyOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CompanyInclude | null + where?: Prisma.CompanyWhereInput + orderBy?: Prisma.CompanyOrderByWithRelationInput | Prisma.CompanyOrderByWithRelationInput[] + cursor?: Prisma.CompanyWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.CompanyScalarFieldEnum | Prisma.CompanyScalarFieldEnum[] +} + +/** + * User.opportunities + */ +export type User$opportunitiesArgs = { + /** + * Select specific fields to fetch from the Opportunity + */ + select?: Prisma.OpportunitySelect | null + /** + * Omit specific fields from the Opportunity + */ + omit?: Prisma.OpportunityOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.OpportunityInclude | null + where?: Prisma.OpportunityWhereInput + orderBy?: Prisma.OpportunityOrderByWithRelationInput | Prisma.OpportunityOrderByWithRelationInput[] + cursor?: Prisma.OpportunityWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.OpportunityScalarFieldEnum | Prisma.OpportunityScalarFieldEnum[] +} + +/** + * User.opportunitiesSecondary + */ +export type User$opportunitiesSecondaryArgs = { + /** + * Select specific fields to fetch from the Opportunity + */ + select?: Prisma.OpportunitySelect | null + /** + * Omit specific fields from the Opportunity + */ + omit?: Prisma.OpportunityOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.OpportunityInclude | null + where?: Prisma.OpportunityWhereInput + orderBy?: Prisma.OpportunityOrderByWithRelationInput | Prisma.OpportunityOrderByWithRelationInput[] + cursor?: Prisma.OpportunityWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.OpportunityScalarFieldEnum | Prisma.OpportunityScalarFieldEnum[] +} + /** * User.generatedQuotes */ @@ -1902,6 +4394,150 @@ export type User$generatedQuotesArgs = { + /** + * Select specific fields to fetch from the CompanyAddress + */ + select?: Prisma.CompanyAddressSelect | null + /** + * Omit specific fields from the CompanyAddress + */ + omit?: Prisma.CompanyAddressOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.CompanyAddressInclude | null + where?: Prisma.CompanyAddressWhereInput + orderBy?: Prisma.CompanyAddressOrderByWithRelationInput | Prisma.CompanyAddressOrderByWithRelationInput[] + cursor?: Prisma.CompanyAddressWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.CompanyAddressScalarFieldEnum | Prisma.CompanyAddressScalarFieldEnum[] +} + +/** + * User.serviceTicketsOwned + */ +export type User$serviceTicketsOwnedArgs = { + /** + * Select specific fields to fetch from the ServiceTicket + */ + select?: Prisma.ServiceTicketSelect | null + /** + * Omit specific fields from the ServiceTicket + */ + omit?: Prisma.ServiceTicketOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ServiceTicketInclude | null + where?: Prisma.ServiceTicketWhereInput + orderBy?: Prisma.ServiceTicketOrderByWithRelationInput | Prisma.ServiceTicketOrderByWithRelationInput[] + cursor?: Prisma.ServiceTicketWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ServiceTicketScalarFieldEnum | Prisma.ServiceTicketScalarFieldEnum[] +} + +/** + * User.serviceTicketsClosed + */ +export type User$serviceTicketsClosedArgs = { + /** + * Select specific fields to fetch from the ServiceTicket + */ + select?: Prisma.ServiceTicketSelect | null + /** + * Omit specific fields from the ServiceTicket + */ + omit?: Prisma.ServiceTicketOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ServiceTicketInclude | null + where?: Prisma.ServiceTicketWhereInput + orderBy?: Prisma.ServiceTicketOrderByWithRelationInput | Prisma.ServiceTicketOrderByWithRelationInput[] + cursor?: Prisma.ServiceTicketWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ServiceTicketScalarFieldEnum | Prisma.ServiceTicketScalarFieldEnum[] +} + +/** + * User.serviceTicketsCreated + */ +export type User$serviceTicketsCreatedArgs = { + /** + * Select specific fields to fetch from the ServiceTicket + */ + select?: Prisma.ServiceTicketSelect | null + /** + * Omit specific fields from the ServiceTicket + */ + omit?: Prisma.ServiceTicketOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ServiceTicketInclude | null + where?: Prisma.ServiceTicketWhereInput + orderBy?: Prisma.ServiceTicketOrderByWithRelationInput | Prisma.ServiceTicketOrderByWithRelationInput[] + cursor?: Prisma.ServiceTicketWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ServiceTicketScalarFieldEnum | Prisma.ServiceTicketScalarFieldEnum[] +} + +/** + * User.serviceTicketsUpdated + */ +export type User$serviceTicketsUpdatedArgs = { + /** + * Select specific fields to fetch from the ServiceTicket + */ + select?: Prisma.ServiceTicketSelect | null + /** + * Omit specific fields from the ServiceTicket + */ + omit?: Prisma.ServiceTicketOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ServiceTicketInclude | null + where?: Prisma.ServiceTicketWhereInput + orderBy?: Prisma.ServiceTicketOrderByWithRelationInput | Prisma.ServiceTicketOrderByWithRelationInput[] + cursor?: Prisma.ServiceTicketWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ServiceTicketScalarFieldEnum | Prisma.ServiceTicketScalarFieldEnum[] +} + +/** + * User.serviceTicketNotes + */ +export type User$serviceTicketNotesArgs = { + /** + * Select specific fields to fetch from the ServiceTicketNote + */ + select?: Prisma.ServiceTicketNoteSelect | null + /** + * Omit specific fields from the ServiceTicketNote + */ + omit?: Prisma.ServiceTicketNoteOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.ServiceTicketNoteInclude | null + where?: Prisma.ServiceTicketNoteWhereInput + orderBy?: Prisma.ServiceTicketNoteOrderByWithRelationInput | Prisma.ServiceTicketNoteOrderByWithRelationInput[] + cursor?: Prisma.ServiceTicketNoteWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.ServiceTicketNoteScalarFieldEnum | Prisma.ServiceTicketNoteScalarFieldEnum[] +} + /** * User without action */ diff --git a/api/package.json b/api/package.json index 470546c..6f45148 100644 --- a/api/package.json +++ b/api/package.json @@ -29,6 +29,7 @@ "utils:gen_private_keys": "bun ./utils/genPrivateKeys", "utils:create_admin_role": "bun ./utils/createAdminRole", "utils:assign_user_role": "bun ./utils/assignUserRole", + "utils:gen_access_token": "bun ./utils/generate24HourAccessToken.ts", "utils:test_webserver": "bun ./utils/testWebserver.ts", "utils:test_adjustments_poll": "bun ./utils/testAdjustmentsPoll.ts", "utils:analyze_cw": "python3 debug-scripts/analyze-cw-calls.py", @@ -45,6 +46,7 @@ "blakets": "^0.1.12", "cors": "^2.8.6", "cuid": "^3.0.0", + "dalpuri": "workspace:*", "hono": "^4.11.5", "ioredis": "^5.10.0", "jsonwebtoken": "^9.0.3", @@ -54,7 +56,6 @@ "pg-boss": "^12.14.0", "prisma": "^7.3.0", "socket.io": "^4.8.3", - "socket.io-client": "^4.8.3", "zod": "^4.3.6", "zon": "^1.0.3" } diff --git a/api/prisma/migrations/20260402000000_fix_severity_typo/migration.sql b/api/prisma/migrations/20260402000000_fix_severity_typo/migration.sql new file mode 100644 index 0000000..d012c84 --- /dev/null +++ b/api/prisma/migrations/20260402000000_fix_severity_typo/migration.sql @@ -0,0 +1,2 @@ +-- Rename the misspelled column serverityId -> severityId on ServiceTicket +ALTER TABLE "ServiceTicket" RENAME COLUMN "serverityId" TO "severityId"; diff --git a/api/prisma/migrations/20260402010000_add_sync_job_tracking/migration.sql b/api/prisma/migrations/20260402010000_add_sync_job_tracking/migration.sql new file mode 100644 index 0000000..4afa26b --- /dev/null +++ b/api/prisma/migrations/20260402010000_add_sync_job_tracking/migration.sql @@ -0,0 +1,41 @@ +-- CreateEnum +CREATE TYPE "SyncJobType" AS ENUM ('FULL_SYNC', 'INCREMENTAL_SYNC'); + +-- CreateEnum +CREATE TYPE "SyncJobStatus" AS ENUM ('QUEUED', 'RUNNING', 'COMPLETED', 'FAILED', 'TIMED_OUT'); + +-- CreateTable +CREATE TABLE "SyncJobRun" ( + "id" TEXT NOT NULL, + "jobType" "SyncJobType" NOT NULL, + "status" "SyncJobStatus" NOT NULL DEFAULT 'QUEUED', + "triggeredBy" TEXT NOT NULL DEFAULT 'system', + "startedAt" TIMESTAMP(3), + "completedAt" TIMESTAMP(3), + "errorSummary" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "SyncJobRun_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "SyncStepLog" ( + "id" TEXT NOT NULL, + "syncJobRunId" TEXT NOT NULL, + "tableName" TEXT NOT NULL, + "syncMode" TEXT NOT NULL, + "recordsProcessed" INTEGER NOT NULL DEFAULT 0, + "recordsInserted" INTEGER NOT NULL DEFAULT 0, + "recordsSkipped" INTEGER NOT NULL DEFAULT 0, + "recordsFailed" INTEGER NOT NULL DEFAULT 0, + "recordsDeleted" INTEGER NOT NULL DEFAULT 0, + "sampleErrors" JSONB NOT NULL DEFAULT '[]', + "durationMs" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "SyncStepLog_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "SyncStepLog" ADD CONSTRAINT "SyncStepLog_syncJobRunId_fkey" FOREIGN KEY ("syncJobRunId") REFERENCES "SyncJobRun"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/api/prisma/migrations/20260403000000_add_opportunity_stage/migration.sql b/api/prisma/migrations/20260403000000_add_opportunity_stage/migration.sql new file mode 100644 index 0000000..fc0e7e5 --- /dev/null +++ b/api/prisma/migrations/20260403000000_add_opportunity_stage/migration.sql @@ -0,0 +1,47 @@ +-- CreateTable (idempotent) +CREATE TABLE IF NOT EXISTS "OpportunityStage" ( + "uid" TEXT NOT NULL, + "id" INTEGER NOT NULL, + "name" TEXT NOT NULL, + "seqNbr" INTEGER, + "funnelColor" TEXT, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "OpportunityStage_pkey" PRIMARY KEY ("uid") +); + +-- CreateIndex (idempotent) +CREATE UNIQUE INDEX IF NOT EXISTS "OpportunityStage_id_key" ON "OpportunityStage"("id"); + +-- AlterTable: drop old columns if they exist, add stageId if it doesn't +DO $$ +BEGIN + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'stageName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "stageName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'stageCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "stageCwId"; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'stageId') THEN + ALTER TABLE "Opportunity" ADD COLUMN "stageId" INTEGER; + END IF; +END $$; + +-- Nullify any stageId values that reference non-existent OpportunityStage rows +UPDATE "Opportunity" SET "stageId" = NULL +WHERE "stageId" IS NOT NULL + AND "stageId" NOT IN (SELECT "id" FROM "OpportunityStage"); + +-- AddForeignKey (skip if already exists) +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE constraint_name = 'Opportunity_stageId_fkey' + ) THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_stageId_fkey" + FOREIGN KEY ("stageId") REFERENCES "OpportunityStage"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; diff --git a/api/prisma/schema.prisma b/api/prisma/schema.prisma index 93fef65..fd32f9e 100644 --- a/api/prisma/schema.prisma +++ b/api/prisma/schema.prisma @@ -27,6 +27,18 @@ enum FaxType { SITE // Main site fax line, not direct to contact } +enum BillingMethod { + ACTUAL_RATES + FIXED_FEE + NOT_TO_EXCEED // Requires "Bill Ticket Seperately" + OVERRIDE_RATE // Shows hourly rate field +} + +enum BillingType { + STANDARD + PROJECT +} + // By human nature, there are only two genders. enum GenderType { MALE @@ -96,6 +108,59 @@ enum OpportunityInterest { COLD } +// ---- Sync Job Tracking ---- + +enum SyncJobType { + FULL_SYNC + INCREMENTAL_SYNC +} + +enum SyncJobStatus { + QUEUED + RUNNING + COMPLETED + FAILED + TIMED_OUT +} + +model SyncJobRun { + id String @id @default(uuid()) + jobType SyncJobType + status SyncJobStatus @default(QUEUED) + triggeredBy String @default("system") + + startedAt DateTime? + completedAt DateTime? + + errorSummary String? + + steps SyncStepLog[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model SyncStepLog { + id String @id @default(uuid()) + + syncJobRunId String + syncJobRun SyncJobRun @relation(fields: [syncJobRunId], references: [id], onDelete: Cascade) + + tableName String + syncMode String // "full" or "incremental" + + recordsProcessed Int @default(0) + recordsInserted Int @default(0) + recordsSkipped Int @default(0) + recordsFailed Int @default(0) + recordsDeleted Int @default(0) + + sampleErrors Json @default("[]") + durationMs Int @default(0) + + createdAt DateTime @default(now()) +} + model Session { id String @id @default(uuid()) sessionKey String @unique @default(cuid()) @@ -108,21 +173,25 @@ model Session { } model User { - id String @id @default(cuid()) - roles Role[] - permissions String? - login String @unique - name String? - email String @unique - emailVerified DateTime? - image String? + id String @id @default(uuid()) + roles Role[] + permissions String? + login String @unique + firstName String? + lastName String? + email String @unique + image String? + + title String? active Boolean @default(true) hidden Boolean @default(false) - cwIdentifier String? @unique + cwIdentifier String? @unique + cwMemberId Int? @unique + cwMember CwMember? @relation(fields: [cwMemberId], references: [cwMemberId]) - userId String @unique + userId String? @unique token String? sessions Session[] @@ -136,8 +205,16 @@ model User { generatedQuotes GeneratedQuotes[] companyAddresses CompanyAddress[] + updatedBy String? + createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + + serviceTicketsOwned ServiceTicket[] @relation("ServiceTicketOwner") + serviceTicketsClosed ServiceTicket[] @relation("ServiceTicketClosedBy") + serviceTicketsCreated ServiceTicket[] @relation("ServiceTicketCreatedBy") + serviceTicketsUpdated ServiceTicket[] @relation("ServiceTicketUpdatedBy") + serviceTicketNotes ServiceTicketNote[] @relation("ServiceTicketNoteAuthor") } model Role { @@ -170,7 +247,9 @@ model CorporateLocation { inactiveFlag Boolean @default(false) // Optima Only field, not synced to CW - opportunities Opportunity[] + opportunities Opportunity[] + serviceBoards ServiceTicketBoard[] + productDataRecords ProductData[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -232,11 +311,13 @@ model Company { deletedBy User? @relation("DeletedBy", fields: [deletedById], references: [cwIdentifier]) enteredBy User? @relation("EnteredBy", fields: [enteredById], references: [cwIdentifier]) - enteredAt DateTime @default(now()) deletedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + + serviceTickets ServiceTicket[] + billingServiceTickets ServiceTicket[] @relation("BillingCompany") } model CompanyAddress { @@ -270,6 +351,8 @@ model CompanyAddress { contacts Contact[] oppportunities Opportunity[] + serviceTickets ServiceTicket[] + billingTickets ServiceTicket[] @relation("BillingAddress") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -304,29 +387,190 @@ model Contact { company Company? @relation(fields: [companyId], references: [id]) opportunities Opportunity[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + serviceTickets ServiceTicket[] +} + +model CatalogItemType { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + + inactiveFlag Boolean @default(false) + defaultFlag Boolean @default(false) + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model CatalogCategory { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + + inactiveFlag Boolean @default(false) + + catalogSubcategories CatalogSubcategory[] + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model CatalogSubcategory { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + + categoryId Int + category CatalogCategory @relation(fields: [categoryId], references: [id]) + + items CatalogItem[] + + inactiveFlag Boolean @default(false) + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model CatalogManufacturer { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + + inactiveFlag Boolean @default(false) + + items CatalogItem[] + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model WarehouseBin { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + + minQuantity Int + maxQuantity Int + + inactiveFlag Boolean @default(false) + defaultFlag Boolean @default(false) + + proeductInventories ProductInventory[] + + warehouse Warehouse? @relation(fields: [warehouseId], references: [id]) + warehouseId Int? + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ProductInventory { + id Int @unique + uid String @id @default(uuid()) + + qtyOnHand Int @default(0) + + warehouseBinId Int + warehouseBin WarehouseBin @relation(fields: [warehouseBinId], references: [id]) + + itemId Int? + item CatalogItem? @relation(fields: [itemId], references: [id]) + + warehouse Warehouse? @relation(fields: [warehouseId], references: [id]) + warehouseId Int? + + updatedById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Warehouse { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + + inactiveFlag Boolean @default(false) + lockedFlag Boolean @default(false) + + minimumStock MinimumStockByWarehouse[] + inventory ProductInventory[] + bins WarehouseBin[] + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model MinimumStockByWarehouse { + id Int @unique + uid String @id @default(uuid()) + + minQty Int @default(0) + + warehouseId Int + warehouse Warehouse @relation(fields: [warehouseId], references: [id]) + + itemId Int? + item CatalogItem? @relation(fields: [itemId], references: [id]) + + updatedById String? + enteredById String? + createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model CatalogItem { - id String @id @default(cuid()) - cwCatalogId Int @unique - identifier String? @unique - name String - description String? + id Int @unique + uid String @id @default(uuid()) + + identifier String? @unique + name String + description String? + customerDescription String? internalNotes String? linkedItems CatalogItem[] @relation("LinkedItems") linkedTo CatalogItem[] @relation("LinkedItems") - category String? - categoryCwId Int? - subcategory String? - subcategoryCwId Int? + subcategoryId Int + subcategory CatalogSubcategory @relation(fields: [subcategoryId], references: [id]) - manufacturer String? - manufactureCwId Int? + manufacturerId Int? + manufacturer CatalogManufacturer? @relation(fields: [manufacturerId], references: [id]) partNumber String? @@ -337,12 +581,403 @@ model CatalogItem { price Float cost Float + inventory ProductInventory[] + minimumStockByWarehouses MinimumStockByWarehouse[] + + productDataRecords ProductData[] + inactive Boolean @default(false) salesTaxable Boolean @default(true) onHand Int @default(0) cwLastUpdated DateTime? + // IV_Class_ID from CW: 'S' = Service/Labor, 'I' = Inventory, 'N' = Non-Inventory + classId String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ProductData { + id Int @unique + uid String @id @default(uuid()) + + qty Float @default(1) + internalNote String? + shortDescription String? + description String? + sequenceNumber Int? // This is the sequence number of the product on the ticket, which may be different from the sequence number of the product in the catalog, and is important for maintaining the order of products on the ticket as they were added. + + procurementNotes String? + productNarrative String? + + unitPrice Float @default(0) + unitCost Float @default(0) + listPrice Float @default(0) // The original price of the product before any discounts or promotions are applied, which may be different from the unit price if there are any overrides or promotions applied at the ticket level. + + discount Float @default(0) // The discount amount applied to this product, which may be different from the discount on the catalog item if there are any overrides or promotions applied at the ticket level. + + recurringRevenue Float @default(0) // For subscription products, the recurring revenue amount, which may be different from the unit price if there are any discounts or promotions applied. + recurringCost Float @default(0) // For subscription products, the recurring cost amount, which may be different from the unit cost if there are any discounts or promotions applied. + + qtyPicked Int @default(0) // How many of this product have been taken out of inventory + qtyShipped Int @default(0) // How many of this product have been recieved by the customer + + cancelReason String? // If this product was canceled or removed from the ticket after being added, what was the reason for that? + cancelQty Float? // If this product was canceled or removed from the ticket after being added, how many were canceled or removed? + + // ------ Flag Fields ------ + billableFlag Boolean @default(true) + taxableFlag Boolean @default(true) + invoiceFlag Boolean @default(true) + recurringFlag Boolean @default(false) // Is this product a subscription or recurring revenue product? + poApprovedFlag Boolean @default(false) // Was the purchase of this product approved as part of a purchase order process? + + calcPriceFlag Boolean @default(true) // Should the price of this product be calculated based on the catalog item price and any overrides, or should it use the unitPrice as is? + calcCostFlag Boolean @default(true) // Should the cost of this product be calculated based on the catalog item cost and any overrides, or should it use the unitCost as is? + + cancelFlag Boolean @default(false) // Has this product been canceled or removed from the ticket after being added, but we want to keep the record of it for historical and reporting purposes? + + // ------ Relational Fields ------ + catalogItemId Int + corporateLocationId Int + + serviceTicketId Int? + opportunityId Int? + + serviceTicket ServiceTicket? @relation(fields: [serviceTicketId], references: [id]) + opportunity Opportunity? @relation(fields: [opportunityId], references: [id]) + + catalogItem CatalogItem @relation(fields: [catalogItemId], references: [id]) + corporateLocation CorporateLocation @relation(fields: [corporateLocationId], references: [id]) + + updatedById String? + createdById String? + closedById String? + cancelById String + + closedAt DateTime? + cancelledAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +// +// SERVICE TICKETS +// + +model ServiceTicket { + id Int @unique + uid String @id @default(uuid()) + + // ------ Core ticket fields ------ + + summary String + + notes ServiceTicketNote[] + + addressLine1 String? + addressLine2 String? + city String? + state USState? + zipCode String? + country Country? + + contactName String? + phone String? + phoneExtension String? + phoneType PhoneType? + email String? + + // ------ Billing and invoicing fields ------ + + products ProductData[] // The products used on this ticket, which may be important for billing and invoicing, as well as reporting and analytics. + + poNumber String? + billCompleteFlag Boolean @default(false) // Bill after ticket is closed, not allowing any billing while open. + billUnapprovedFlag Boolean @default(false) // Can this ticket bill unapproved work or expenses? + billingAmount Float @default(0.00) + billingMethod BillingMethod @default(ACTUAL_RATES) + + timeBillableFlag Boolean @default(true) // Is the time spent on this ticket billable? + expenseBillableFlag Boolean @default(true) // Are the expenses incurred on this ticket billable? + productBillableFlag Boolean @default(true) // Are the products used on this ticket billable? + + timeInvoiceableFlag Boolean @default(true) // Should the billable time on this ticket be included on invoices? + expenseInvoiceableFlag Boolean @default(true) // Should the billable expenses on this ticket be included on invoices? + productInvoiceableFlag Boolean @default(true) // Should the billable products on this ticket be included on invoices? + + dateRequested DateTime? // The date the customer requested service, which may be different from the date the ticket was created in the system. + + billingType BillingType @default(STANDARD) // (CUSTOM FIELD) Standard billing or project billing, which may have different rules for how the ticket is billed. + billingInstructions String? // (CUSTOM FIELD) Any special instructions for billing this ticket, which may be important for project billing or non-standard billing arrangements. + + // ------ Flag Fields ------ + + rejectedFlag Boolean @default(false) // More used for denoting if a ticket was rejected by some automation. + closedFlag Boolean @default(false) + redFlag Boolean @default(false) // Carry over from CW, used for visibility and filtering. + publishFlag Boolean @default(false) // Should this ticket be visible to the customer in the portal or any other means? + + // ------ Relational Fields ------ + + ticketOwnerId String? + serviceTicketBoardId Int? + severityId Int + impactId Int + priorityId Int + sourceId Int + locationId Int + parentId Int? + + companyId Int? + contactId Int? + companyAddressId Int? + + billingCompanyId Int? + billingAddressId Int? + + severity ServiceTicketSeverity @relation(fields: [severityId], references: [id]) + impact ServiceTicketImpact @relation(fields: [impactId], references: [id]) + priority ServiceTicketPriority @relation(fields: [priorityId], references: [id]) + source ServiceTicketSource @relation(fields: [sourceId], references: [id]) + location ServiceTicketLocation @relation(fields: [locationId], references: [id]) + serviceTicketBoard ServiceTicketBoard? @relation(fields: [serviceTicketBoardId], references: [id]) + ticketOwner User? @relation("ServiceTicketOwner", fields: [ticketOwnerId], references: [cwIdentifier]) + + company Company? @relation(fields: [companyId], references: [id]) + contact Contact? @relation(fields: [contactId], references: [id]) + companyAddress CompanyAddress? @relation(fields: [companyAddressId], references: [id]) + + billingCompany Company? @relation("BillingCompany", fields: [billingCompanyId], references: [id]) + billingAddress CompanyAddress? @relation("BillingAddress", fields: [billingAddressId], references: [id]) + + // ------ Audit Fields ------ + + createdById String? + updatedById String? + closedById String? + + closedBy User? @relation("ServiceTicketClosedBy", fields: [closedById], references: [id]) + createdBy User? @relation("ServiceTicketCreatedBy", fields: [createdById], references: [id]) + updatedBy User? @relation("ServiceTicketUpdatedBy", fields: [updatedById], references: [id]) + + rejectedAt DateTime? + closedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ServiceTicketNote { + id Int @unique + uid String @id @default(uuid()) + + notes String + notesMd String + + authorId String + author User @relation("ServiceTicketNoteAuthor", fields: [authorId], references: [id]) + + problemFlag Boolean @default(false) // Is this note describing the problem? + resolutionFlag Boolean @default(false) // Is this note describing the resolution? + internalAnalysisFlag Boolean @default(false) // Is this note describing the internal analysis of the issue, such as root cause analysis or technical details that may not be relevant to the customer? + internalMemberFlag Boolean @default(false) // Is this note meant to be seen by internal team members only, not visible to the customer? + createdByParentFlag Boolean @default(false) // Is this note created by the parent entity. + mergedFlag Boolean @default(false) + bundledFlag Boolean @default(false) + + serviceTicketId Int + serviceTicket ServiceTicket @relation(fields: [serviceTicketId], references: [id]) + + createdById String? + updatedById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ServiceTicketType { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + + inactiveFlag Boolean @default(false) + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ServiceTicketBoard { + id Int @unique + uid String @id @default(uuid()) + + name String + + // Does this generate revenue for the company? + timeBillableFlag Boolean @default(false) + expenseBillableFlag Boolean @default(false) + productBillableFlag Boolean @default(false) + + // Can the customer see this on their invoice? + timeInvoiceableFlag Boolean @default(false) + expenseInvoiceableFlag Boolean @default(false) + productInvoiceableFlag Boolean @default(false) + + // These are auto assignment rule flags + // If/When I implement a system for auto assignement, + // these flags will determine which fields are considered + // for auto assignment when a ticket is created without an assignee. + + // These are a carry over from CW. + autoAssignNewFlag Boolean @default(false) + autoAssignEmailCreatedFlag Boolean @default(false) + autoAssignPortalCreatedFlag Boolean @default(false) + + projectFlag Boolean @default(false) + lockDescriptionFlag Boolean @default(false) // Should we lock the description field on tickets in this board after creation? + + emailContactFlag Boolean @default(false) // Should we email the contact when a ticket is updated? + emailResourceFlag Boolean @default(false) // Should we email the assigned resource(s) when a ticket is updated? + + resolutionSortOrder String @default("D") @db.Char(1) + internalAnalysisSortOrder String @default("D") @db.Char(1) + + locationId Int + location CorporateLocation @relation(fields: [locationId], references: [id]) + + serviceTickets ServiceTicket[] + + createdById String? + updatedById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ServiceTicketLocation { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + + defaultFlag Boolean @default(false) + + serviceTickets ServiceTicket[] + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ServiceTicketSource { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + + inactiveFlag Boolean @default(false) + defaultFlag Boolean @default(false) + + serviceTickets ServiceTicket[] + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +// How much does the issue affect the customer or their business operations +model ServiceTicketImpact { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? + + defaultFlag Boolean @default(false) + + serviceTickets ServiceTicket[] + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +// How soon does the issue need to be addressed/resolved +model ServiceTicketPriority { + id Int @unique + uid String @id @default(uuid()) + + name String + color String? // Hex color code for priority, e.g. "#FF0000" for red + description String? // Optima Only field, not synced to CW + + defaultFlag Boolean @default(false) + + serviceTickets ServiceTicket[] + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +// How bad is the Technical Issue +model ServiceTicketSeverity { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? + + defaultFlag Boolean @default(false) + + serviceTickets ServiceTicket[] + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@map("ServiceTicketServerity") +} + +// This data is populated asynchronously after a ticket is closed, +// so we can keep the critical path of closing a ticket fast and not +// dependent on any additional data processing. This will also allow +// us to be able to store the data AS IT was at the time of closing, +// without worrying about any additional updates that may come in after the fact. +model ServiceTicketFinalData { + id String @id @default(uuid()) +} + +model OpportunityStage { + id Int @unique + uid String @id @default(uuid()) + + name String + seqNbr Int? + funnelColor String? + + updatedById String? + + opportunities Opportunity[] + createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } @@ -393,17 +1028,22 @@ model Opportunity { name String notes String? + oppNarrative String? // A long form text field for the story of the opportunity, which may include details about the customer's needs, the proposed solution, and any other relevant information that doesn't fit into the structured fields. + generatedQuotes GeneratedQuotes[] typeId Int type OpportunityType @relation(fields: [typeId], references: [id]) - stageName String? - stageCwId Int? + stageId Int? + stage OpportunityStage? @relation(fields: [stageId], references: [id]) statusId Int? status OpportunityStatus? @relation(fields: [statusId], references: [id]) + taxCodeId Int? + taxCode TaxCode? @relation(fields: [taxCodeId], references: [id]) + interest OpportunityInterest? probability Float @default(0) @@ -447,6 +1087,111 @@ model Opportunity { // When present, fetchProducts() uses this order instead of CW sequenceNumber. productSequence Int[] @default([]) + products ProductData[] + + updatedBy String + eneteredBy String + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +// ------ Schedule / Calendar -------- + +model ScheduleStatus { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + color String? + + softFlag Boolean @default(false) + defaultFlag Boolean @default(false) + + schedules Schedule[] + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ScheduleType { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? // Optima Only field, not synced to CW + displayColor String? + + tableReference String? + moduleId String? @db.Char(2) + scheduleTypeId String? @db.Char(1) + + systemFlag Boolean @default(false) + displayFlag Boolean @default(false) + + schedules Schedule[] + + updatedById String? + createdById String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ScheduleSpan { + id Int @id @default(autoincrement()) + scheduleSpanId String? @db.Char(1) + spanDesc String? @db.Char(20) + + schedules Schedule[] +} + +model Schedule { + id Int @unique + uid String @id @default(uuid()) + + name String + description String? + + memberId String? + + closedFlag Boolean @default(false) + reminderFlag Boolean @default(false) + allDayFlag Boolean @default(false) + acknowledgementFlag Boolean @default(false) + meetingFlag Boolean @default(false) + recurringFlag Boolean @default(false) + + billableFlag Boolean @default(false) + + acknowledgedById String? + acknowledgedAt DateTime? + + startDate DateTime? + endDate DateTime? + hoursScheduled Float? + duration Int? // The number of days in between the start and end date. + hoursPerDay Float? + reminderMinutes Int? @default(15) + + statusId Int? + status ScheduleStatus? @relation(fields: [statusId], references: [id]) + + typeId Int? + type ScheduleType? @relation(fields: [typeId], references: [id]) + + scheduleSpanId Int? + scheduleSpan ScheduleSpan? @relation(fields: [scheduleSpanId], references: [id]) + + updatedById String? + createdById String? + closedById String? + + closedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } @@ -523,6 +1268,27 @@ model GeneratedQuotes { updatedAt DateTime @updatedAt } +model TaxCode { + id Int @unique + uid String @id @default(uuid()) + + opportunities Opportunity[] + + code String @unique + codeCaption String + description String? + + rate Float? + + defaultFlag Boolean @default(false) + + createdBy String + updatedBy String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + model CwMember { id String @id @default(cuid()) @@ -535,6 +1301,8 @@ model CwMember { apiKey String? + user User? + cwLastUpdated DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/api/src/api/auth/callback.ts b/api/src/api/auth/callback.ts new file mode 100644 index 0000000..c3a832e --- /dev/null +++ b/api/src/api/auth/callback.ts @@ -0,0 +1,24 @@ +import { createRoute } from "../../modules/api-utils/createRoute"; +import { redis } from "../../constants"; + +/* /v1/auth/callback/:callbackKey */ +export default createRoute("get", ["/callback/:callbackKey"], async (c) => { + const callbackKey = c.req.param("callbackKey"); + if (!callbackKey) { + c.status(400); + return c.json({ status: 400, message: "Missing callbackKey", successful: false }); + } + + const redisKey = `auth:cb:${callbackKey}`; + const raw = await redis.get(redisKey); + if (!raw) { + c.status(202); + return c.json({ status: 202, message: "Pending", successful: false }); + } + + // Delete immediately so tokens can't be replayed + await redis.del(redisKey); + + const tokens = JSON.parse(raw) as { accessToken: string; refreshToken: string }; + return c.json({ status: 200, message: "Auth callback resolved", data: tokens, successful: true }); +}); diff --git a/api/src/api/auth/index.ts b/api/src/api/auth/index.ts index 613953a..bfc3dc1 100644 --- a/api/src/api/auth/index.ts +++ b/api/src/api/auth/index.ts @@ -1,3 +1,4 @@ export { default as redirect } from "./redirect"; export { default as refresh } from "./refresh"; export { default as uri } from "./uri"; +export { default as callback } from "./callback"; diff --git a/api/src/api/auth/redirect.ts b/api/src/api/auth/redirect.ts index 6ac0063..8b581a0 100644 --- a/api/src/api/auth/redirect.ts +++ b/api/src/api/auth/redirect.ts @@ -1,9 +1,11 @@ import { Hono } from "hono/tiny"; import { createRoute } from "../../modules/api-utils/createRoute"; import * as msal from "@azure/msal-node"; -import { API_BASE_URL, io, msalClient } from "../../constants"; +import { API_BASE_URL, msalClient, redis } from "../../constants"; import { users } from "../../managers/users"; +const AUTH_CALLBACK_TTL_SECONDS = 300; // 5 minutes + /* /v1/auth/redirect */ export default createRoute("get", ["/redirect"], async (c) => { c.status(200); @@ -18,10 +20,13 @@ export default createRoute("get", ["/redirect"], async (c) => { const callbackKey = c.req.query().state as string; const tokens = await users.authenticate(authResult); - io.of(`/auth_callback`).emit(`auth:login:callback:${callbackKey}`, { - accessToken: tokens.accessToken, - refreshToken: tokens.refreshToken, - }); + // Store tokens in Redis so the UI can poll for them + await redis.set( + `auth:cb:${callbackKey}`, + JSON.stringify({ accessToken: tokens.accessToken, refreshToken: tokens.refreshToken }), + "EX", + AUTH_CALLBACK_TTL_SECONDS, + ); // Close the window because duh return c.html( @@ -29,11 +34,4 @@ export default createRoute("get", ["/redirect"], async (c) => { window.close(); `, ); - - return c.json({ - status: 200, - message: "Auth Redirect Endpoint", - data: authResult, - successful: true, - }); }); diff --git a/api/src/api/companies/[id]/fetch.ts b/api/src/api/companies/[id]/fetch.ts index d716f87..df9538e 100644 --- a/api/src/api/companies/[id]/fetch.ts +++ b/api/src/api/companies/[id]/fetch.ts @@ -4,7 +4,6 @@ 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] */ @@ -14,34 +13,17 @@ export default createRoute( async (c) => { const company = await companies.fetch(c.req.param("identifier")); - const includeAddress = c.req.query("includeAddress") === "true"; + const user = c.get("user"); + const includeAddress = + c.req.query("includeAddress") === "true" && + !!user && + (await user.hasPermission("company.fetch.address")); const includePrimaryContact = c.req.query("includePrimaryContact") === "true"; - const includeAllContacts = c.req.query("includeAllContacts") === "true"; - - // 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 includeAllContacts = + c.req.query("includeAllContacts") === "true" && + !!user && + (await user.hasPermission("company.fetch.contacts")); const companyData = company.toJson({ includeAddress, @@ -54,6 +36,13 @@ 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, diff --git a/api/src/api/cw/fetchMembers.ts b/api/src/api/cw/fetchMembers.ts index 67db7cd..387342f 100644 --- a/api/src/api/cw/fetchMembers.ts +++ b/api/src/api/cw/fetchMembers.ts @@ -2,36 +2,35 @@ import { createRoute } from "../../modules/api-utils/createRoute"; import { apiResponse } from "../../modules/api-utils/apiResponse"; import { ContentfulStatusCode } from "hono/utils/http-status"; import { authMiddleware } from "../middleware/authorization"; -import { getMemberCache } from "../../modules/cw-utils/members/memberCache"; +import { prisma } from "../../constants"; /* GET /v1/cw/members */ export default createRoute( "get", ["/members"], async (c) => { - const cache = await getMemberCache(); - const activeOnly = c.req.query("active") !== "false"; - const members = cache - .filter((m) => (activeOnly ? !m.inactiveFlag : true)) - .map((m) => ({ - id: m.id, - identifier: m.identifier, - firstName: m.firstName, - lastName: m.lastName, - name: `${m.firstName} ${m.lastName}`.trim(), - officeEmail: m.officeEmail, - inactive: m.inactiveFlag, - })); + const dbMembers = await prisma.cwMember.findMany({ + where: activeOnly ? { inactiveFlag: false } : undefined, + orderBy: [{ firstName: "asc" }, { lastName: "asc" }], + }); - const sorted = members.sort((a, b) => a.name.localeCompare(b.name)); + const members = dbMembers.map((m) => ({ + id: m.cwMemberId, + identifier: m.identifier, + firstName: m.firstName, + lastName: m.lastName, + name: `${m.firstName} ${m.lastName}`.trim(), + officeEmail: m.officeEmail, + inactive: m.inactiveFlag, + })); const response = apiResponse.successful( "CW members fetched successfully!", - sorted, + members ); return c.json(response, response.status as ContentfulStatusCode); }, - authMiddleware(), + authMiddleware() ); diff --git a/api/src/api/cw/index.ts b/api/src/api/cw/index.ts index 5b0269c..e4dcb0a 100644 --- a/api/src/api/cw/index.ts +++ b/api/src/api/cw/index.ts @@ -1,4 +1,6 @@ import { default as callback } from "./callback"; import { default as fetchMembers } from "./fetchMembers"; +import { default as syncFull } from "./sync"; +import { syncStatus, syncStatusById, syncHistory } from "./sync-status"; -export { callback, fetchMembers }; +export { callback, fetchMembers, syncFull, syncStatus, syncStatusById, syncHistory }; diff --git a/api/src/api/cw/sync-status.ts b/api/src/api/cw/sync-status.ts new file mode 100644 index 0000000..adae9cd --- /dev/null +++ b/api/src/api/cw/sync-status.ts @@ -0,0 +1,84 @@ +import { createRoute } from "../../modules/api-utils/createRoute"; +import { apiResponse } from "../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../middleware/authorization"; +import { prisma } from "../../constants"; + +/* GET /v1/cw/sync/status — latest sync job run with step logs */ +export const syncStatus = createRoute( + "get", + ["/sync/status"], + async (c) => { + const latest = await prisma.syncJobRun.findFirst({ + orderBy: { createdAt: "desc" }, + include: { + steps: { + orderBy: { createdAt: "asc" }, + }, + }, + }); + + if (!latest) { + const response = apiResponse.successful("No sync runs found", null); + return c.json(response, response.status as ContentfulStatusCode); + } + + const response = apiResponse.successful("Latest sync run", latest); + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware() +); + +/* GET /v1/cw/sync/status/:jobId — specific job details */ +export const syncStatusById = createRoute( + "get", + ["/sync/status/:jobId"], + async (c) => { + const jobId = c.req.param("jobId"); + + const run = await prisma.syncJobRun.findUnique({ + where: { id: jobId }, + include: { + steps: { + orderBy: { createdAt: "asc" }, + }, + }, + }); + + if (!run) { + const response = apiResponse.notFound("Sync run not found"); + return c.json(response, response.status as ContentfulStatusCode); + } + + const response = apiResponse.successful("Sync run details", run); + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware() +); + +/* GET /v1/cw/sync/history — last N sync runs */ +export const syncHistory = createRoute( + "get", + ["/sync/history"], + async (c) => { + const limitParam = c.req.query("limit"); + const limit = Math.min( + Math.max(Number.parseInt(limitParam || "20", 10) || 20, 1), + 100 + ); + + const runs = await prisma.syncJobRun.findMany({ + orderBy: { createdAt: "desc" }, + take: limit, + include: { + _count: { + select: { steps: true }, + }, + }, + }); + + const response = apiResponse.successful("Sync history", runs); + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware() +); diff --git a/api/src/api/cw/sync.ts b/api/src/api/cw/sync.ts new file mode 100644 index 0000000..49a474d --- /dev/null +++ b/api/src/api/cw/sync.ts @@ -0,0 +1,26 @@ +import { createRoute } from "../../modules/api-utils/createRoute"; +import { apiResponse } from "../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../middleware/authorization"; +import { getBoss } from "../../workert"; +import { WorkerQueue } from "../../modules/workers/queues"; + +/* POST /v1/cw/sync/full */ +export default createRoute( + "post", + ["/sync/full"], + async (c) => { + const jobId = await getBoss().send(WorkerQueue.DALPURI_FULL_SYNC, {}); + + if (!jobId) { + throw new Error("Failed to enqueue dalpuri full sync job"); + } + + const response = apiResponse.successful( + "Full sync enqueued successfully", + { jobId } + ); + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware() +); diff --git a/api/src/api/routers/scheduleRouter.ts b/api/src/api/routers/scheduleRouter.ts new file mode 100644 index 0000000..a8a9e14 --- /dev/null +++ b/api/src/api/routers/scheduleRouter.ts @@ -0,0 +1,7 @@ +import { Hono } from "hono"; +import * as scheduleRoutes from "../schedules"; + +const scheduleRouter = new Hono(); +Object.values(scheduleRoutes).map((r) => scheduleRouter.route("/", r)); + +export default scheduleRouter; diff --git a/api/src/api/sales/[id]/fetch.ts b/api/src/api/sales/[id]/fetch.ts index bc4c090..037666f 100644 --- a/api/src/api/sales/[id]/fetch.ts +++ b/api/src/api/sales/[id]/fetch.ts @@ -4,20 +4,6 @@ import { apiResponse } from "../../../modules/api-utils/apiResponse"; import { ContentfulStatusCode } from "hono/utils/http-status"; import { authMiddleware } from "../../middleware/authorization"; import { processObjectValuePerms } from "../../../modules/permission-utils/processObjectPermissions"; -import GenericError from "../../../Errors/GenericError"; -import { prisma } from "../../../constants"; -import { computeSubResourceCacheTTL } from "../../../modules/algorithms/computeSubResourceCacheTTL"; -import { computeProductsCacheTTL } from "../../../modules/algorithms/computeProductsCacheTTL"; -import { - getCachedSite, - getCachedNotes, - getCachedContacts, - getCachedProducts, - fetchAndCacheNotes, - fetchAndCacheContacts, - fetchAndCacheProducts, - fetchAndCacheSite, -} from "../../../modules/cache/opportunityCache"; import { generatedQuotes } from "../../../managers/generatedQuotes"; /* GET /v1/sales/opportunities/opportunity/:identifier?include=notes,contacts,products,quotes */ @@ -31,102 +17,13 @@ export default createRoute( includeParam .split(",") .map((s) => s.trim().toLowerCase()) - .filter(Boolean), + .filter(Boolean) ); - // ── Quick DB lookup (≈3ms) to get cwOpportunityId for pre-warming ── - const isNumeric = /^\d+$/.test(identifier); - const dbRecord = await prisma.opportunity.findFirst({ - where: isNumeric - ? { cwOpportunityId: Number(identifier) } - : { id: identifier }, - select: { - cwOpportunityId: true, - companyCwId: true, - siteCwId: true, - closedFlag: true, - closedDate: true, - expectedCloseDate: true, - cwLastUpdated: true, - statusCwId: true, - }, - }); + // Fetch the opportunity from local DB + const item = await opportunities.fetchItem(identifier); - if (!dbRecord) { - throw new GenericError({ - message: "Opportunity not found", - name: "OpportunityNotFound", - cause: `No opportunity exists with identifier '${identifier}'`, - status: 404, - }); - } - - // Compute TTLs from DB state - const subTtl = computeSubResourceCacheTTL({ - closedFlag: dbRecord.closedFlag, - closedDate: dbRecord.closedDate, - expectedCloseDate: dbRecord.expectedCloseDate, - lastUpdated: dbRecord.cwLastUpdated, - }); - const prodTtl = computeProductsCacheTTL({ - closedFlag: dbRecord.closedFlag, - closedDate: dbRecord.closedDate, - expectedCloseDate: dbRecord.expectedCloseDate, - lastUpdated: dbRecord.cwLastUpdated, - statusCwId: dbRecord.statusCwId, - }); - - // ── Pre-warm sub-resources only on cache miss ─────────────────────── - // Check Redis first — if the background refresh has kept the keys warm, - // skip the CW calls entirely. Only fetch-and-cache on a miss. - const cwOppId = dbRecord.cwOpportunityId; - const _ignoreErrors = (p: Promise) => p.catch(() => {}); - - const prewarmPromises: Promise[] = []; - if (dbRecord.companyCwId && dbRecord.siteCwId) { - const compId = dbRecord.companyCwId, - siteId = dbRecord.siteCwId; - prewarmPromises.push( - _ignoreErrors( - getCachedSite(compId, siteId).then( - (c) => c ?? fetchAndCacheSite(compId, siteId), - ), - ), - ); - } - if (includes.has("notes") && subTtl) - prewarmPromises.push( - _ignoreErrors( - getCachedNotes(cwOppId).then( - (c) => c ?? fetchAndCacheNotes(cwOppId, subTtl), - ), - ), - ); - if (includes.has("contacts") && subTtl) - prewarmPromises.push( - _ignoreErrors( - getCachedContacts(cwOppId).then( - (c) => c ?? fetchAndCacheContacts(cwOppId, subTtl), - ), - ), - ); - if (includes.has("products") && prodTtl) - prewarmPromises.push( - _ignoreErrors( - getCachedProducts(cwOppId).then( - (c) => c ?? fetchAndCacheProducts(cwOppId, prodTtl), - ), - ), - ); - - // fetchItem runs its own CW calls (opp, activities, company) — - // these execute concurrently with the sub-resource pre-warming above. - const [item] = await Promise.all([ - opportunities.fetchItem(identifier), - ...prewarmPromises, - ]); - - // Sub-resources now hit warm Redis cache (near-instant) + // Fetch sub-resources as requested const subResourcePromises: Record> = { _site: item.fetchSite(), }; @@ -154,7 +51,7 @@ export default createRoute( const gatedData = await processObjectValuePerms( item.toJson(), "obj.opportunity", - c.get("user"), + c.get("user") ); const originalOpportunityNoteText = (gatedData as any).notes; @@ -175,9 +72,9 @@ export default createRoute( const response = apiResponse.successful( "Opportunity fetched successfully!", - gatedData, + gatedData ); return c.json(response, response.status as ContentfulStatusCode); }, - authMiddleware({ permissions: ["sales.opportunity.fetch"] }), + authMiddleware({ permissions: ["sales.opportunity.fetch"] }) ); diff --git a/api/src/api/sales/fetchOpportunityTypes.ts b/api/src/api/sales/fetchOpportunityTypes.ts index ec2cc67..6ebc0c2 100644 --- a/api/src/api/sales/fetchOpportunityTypes.ts +++ b/api/src/api/sales/fetchOpportunityTypes.ts @@ -2,7 +2,7 @@ import { createRoute } from "../../modules/api-utils/createRoute"; import { apiResponse } from "../../modules/api-utils/apiResponse"; import { ContentfulStatusCode } from "hono/utils/http-status"; import { authMiddleware } from "../middleware/authorization"; -import { QUOTE_STATUSES } from "../../types/QuoteStatuses"; +import { prisma } from "../../constants"; /* GET /v1/sales/opportunity-types */ export default createRoute( @@ -10,9 +10,14 @@ export default createRoute( ["/opportunity-types"], async (c) => { + const types = await prisma.opportunityType.findMany({ + where: { inactiveFlag: false }, + orderBy: { name: "asc" }, + select: { id: true, name: true, inactiveFlag: true }, + }); const response = apiResponse.successful( "Opportunity Types Fetched Successfully!", - QUOTE_STATUSES, + types, ); return c.json(response, response.status as ContentfulStatusCode); }, diff --git a/api/src/api/sales/index.ts b/api/src/api/sales/index.ts index c544d01..5110699 100644 --- a/api/src/api/sales/index.ts +++ b/api/src/api/sales/index.ts @@ -27,6 +27,8 @@ import { default as fetchQuotes } from "./opportunities/[id]/quotes/fetchAll"; import { default as previewQuote } from "./opportunities/[id]/quotes/preview"; import { default as downloadQuote } from "./opportunities/[id]/quotes/download"; import { default as fetchDownloads } from "./opportunities/[id]/quotes/fetchDownloads"; +import { default as fetchNarrative } from "./opportunities/[id]/quotes/fetchNarrative"; +import { default as updateNarrative } from "./opportunities/[id]/quotes/updateNarrative"; import { default as fetchByUser } from "./opportunities/fetchByUser"; import { default as fetchByUserId } from "./opportunities/fetchByUserId"; import { default as workflowDispatch } from "./opportunities/[id]/workflow/dispatch"; @@ -63,6 +65,8 @@ export { previewQuote, downloadQuote, fetchDownloads, + fetchNarrative, + updateNarrative, refresh, updateOpportunity, workflowDispatch, diff --git a/api/src/api/sales/opportunities/[id]/fetch.ts b/api/src/api/sales/opportunities/[id]/fetch.ts index 5f44ebf..fbbfa26 100644 --- a/api/src/api/sales/opportunities/[id]/fetch.ts +++ b/api/src/api/sales/opportunities/[id]/fetch.ts @@ -5,19 +5,6 @@ import { ContentfulStatusCode } from "hono/utils/http-status"; import { authMiddleware } from "../../../middleware/authorization"; import { processObjectValuePerms } from "../../../../modules/permission-utils/processObjectPermissions"; import GenericError from "../../../../Errors/GenericError"; -import { prisma } from "../../../../constants"; -import { computeSubResourceCacheTTL } from "../../../../modules/algorithms/computeSubResourceCacheTTL"; -import { computeProductsCacheTTL } from "../../../../modules/algorithms/computeProductsCacheTTL"; -import { - getCachedSite, - getCachedNotes, - getCachedContacts, - getCachedProducts, - fetchAndCacheNotes, - fetchAndCacheContacts, - fetchAndCacheProducts, - fetchAndCacheSite, -} from "../../../../modules/cache/opportunityCache"; import { generatedQuotes } from "../../../../managers/generatedQuotes"; /* GET /v1/sales/opportunities/opportunity/:identifier?include=notes,contacts,products,quotes */ @@ -31,102 +18,15 @@ export default createRoute( includeParam .split(",") .map((s) => s.trim().toLowerCase()) - .filter(Boolean), + .filter(Boolean) ); - // ── Quick DB lookup (≈3ms) to get cwOpportunityId for pre-warming ── - const isNumeric = /^\d+$/.test(identifier); - const dbRecord = await prisma.opportunity.findFirst({ - where: isNumeric - ? { cwOpportunityId: Number(identifier) } - : { id: identifier }, - select: { - cwOpportunityId: true, - companyCwId: true, - siteCwId: true, - closedFlag: true, - closedDate: true, - expectedCloseDate: true, - cwLastUpdated: true, - statusCwId: true, - }, - }); + // Fetch the opportunity from local DB + const item = await opportunities.fetchItem(identifier); - if (!dbRecord) { - throw new GenericError({ - message: "Opportunity not found", - name: "OpportunityNotFound", - cause: `No opportunity exists with identifier '${identifier}'`, - status: 404, - }); - } + console.log("Fetched opportunity:", item ? item.toJson() : null); - // Compute TTLs from DB state - const subTtl = computeSubResourceCacheTTL({ - closedFlag: dbRecord.closedFlag, - closedDate: dbRecord.closedDate, - expectedCloseDate: dbRecord.expectedCloseDate, - lastUpdated: dbRecord.cwLastUpdated, - }); - const prodTtl = computeProductsCacheTTL({ - closedFlag: dbRecord.closedFlag, - closedDate: dbRecord.closedDate, - expectedCloseDate: dbRecord.expectedCloseDate, - lastUpdated: dbRecord.cwLastUpdated, - statusCwId: dbRecord.statusCwId, - }); - - // ── Pre-warm sub-resources only on cache miss ─────────────────────── - // Check Redis first — if the background refresh has kept the keys warm, - // skip the CW calls entirely. Only fetch-and-cache on a miss. - const cwOppId = dbRecord.cwOpportunityId; - const _ignoreErrors = (p: Promise) => p.catch(() => {}); - - const prewarmPromises: Promise[] = []; - if (dbRecord.companyCwId && dbRecord.siteCwId) { - const compId = dbRecord.companyCwId, - siteId = dbRecord.siteCwId; - prewarmPromises.push( - _ignoreErrors( - getCachedSite(compId, siteId).then( - (c) => c ?? fetchAndCacheSite(compId, siteId), - ), - ), - ); - } - if (includes.has("notes") && subTtl) - prewarmPromises.push( - _ignoreErrors( - getCachedNotes(cwOppId).then( - (c) => c ?? fetchAndCacheNotes(cwOppId, subTtl), - ), - ), - ); - if (includes.has("contacts") && subTtl) - prewarmPromises.push( - _ignoreErrors( - getCachedContacts(cwOppId).then( - (c) => c ?? fetchAndCacheContacts(cwOppId, subTtl), - ), - ), - ); - if (includes.has("products") && prodTtl) - prewarmPromises.push( - _ignoreErrors( - getCachedProducts(cwOppId).then( - (c) => c ?? fetchAndCacheProducts(cwOppId, prodTtl), - ), - ), - ); - - // fetchItem runs its own CW calls (opp, activities, company) — - // these execute concurrently with the sub-resource pre-warming above. - const [item] = await Promise.all([ - opportunities.fetchItem(identifier), - ...prewarmPromises, - ]); - - // Sub-resources now hit warm Redis cache (near-instant) + // Fetch sub-resources as requested const subResourcePromises: Record> = { _site: item.fetchSite(), }; @@ -147,7 +47,7 @@ export default createRoute( subResourcePromises.quotes = generatedQuotes .fetchByOpportunity(item.id) .then((quotes) => - quotes.map((q) => q.toJson({ includeRegenData, includeRegenParams })), + quotes.map((q) => q.toJson({ includeRegenData, includeRegenParams })) ); } @@ -158,7 +58,7 @@ export default createRoute( const gatedData = await processObjectValuePerms( item.toJson(), "obj.opportunity", - c.get("user"), + c.get("user") ); const originalOpportunityNoteText = (gatedData as any).notes; @@ -179,9 +79,9 @@ export default createRoute( const response = apiResponse.successful( "Opportunity fetched successfully!", - gatedData, + gatedData ); return c.json(response, response.status as ContentfulStatusCode); }, - authMiddleware({ permissions: ["sales.opportunity.fetch"] }), + authMiddleware({ permissions: ["sales.opportunity.fetch"] }) ); diff --git a/api/src/api/sales/opportunities/[id]/notes/create.ts b/api/src/api/sales/opportunities/[id]/notes/create.ts index dbcb9ee..1799503 100644 --- a/api/src/api/sales/opportunities/[id]/notes/create.ts +++ b/api/src/api/sales/opportunities/[id]/notes/create.ts @@ -3,9 +3,23 @@ import { opportunities } from "../../../../../managers/opportunities"; import { apiResponse } from "../../../../../modules/api-utils/apiResponse"; import { ContentfulStatusCode } from "hono/utils/http-status"; import { authMiddleware } from "../../../../middleware/authorization"; -import { resolveMember } from "../../../../../modules/cw-utils/members/memberCache"; +import { prisma } from "../../../../../constants"; import { z } from "zod"; +// Helper to resolve member from DB +async function resolveMember(identifier: string | null | undefined) { + if (!identifier) return null; + const member = await prisma.cwMember.findFirst({ where: { identifier } }); + return member + ? { + id: member.id, + identifier: member.identifier, + name: `${member.firstName} ${member.lastName}`.trim(), + cwMemberId: member.cwMemberId, + } + : { id: null, identifier, name: identifier, cwMemberId: null }; +} + /* POST /v1/sales/opportunities/opportunity/:identifier/notes */ export default createRoute( "post", @@ -38,10 +52,10 @@ export default createRoute( : null, flagged: created.flagged, enteredBy: await resolveMember(created.enteredBy), - }, + } ); return c.json(response, response.status as ContentfulStatusCode); }, - authMiddleware({ permissions: ["sales.opportunity.note.create"] }), + authMiddleware({ permissions: ["sales.opportunity.note.create"] }) ); diff --git a/api/src/api/sales/opportunities/[id]/notes/update.ts b/api/src/api/sales/opportunities/[id]/notes/update.ts index 0058edc..76f730f 100644 --- a/api/src/api/sales/opportunities/[id]/notes/update.ts +++ b/api/src/api/sales/opportunities/[id]/notes/update.ts @@ -4,9 +4,23 @@ 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 { resolveMember } from "../../../../../modules/cw-utils/members/memberCache"; +import { prisma } from "../../../../../constants"; import { z } from "zod"; +// Helper to resolve member from DB +async function resolveMember(identifier: string | null | undefined) { + if (!identifier) return null; + const member = await prisma.cwMember.findFirst({ where: { identifier } }); + return member + ? { + id: member.id, + identifier: member.identifier, + name: `${member.firstName} ${member.lastName}`.trim(), + cwMemberId: member.cwMemberId, + } + : { id: null, identifier, name: identifier, cwMemberId: null }; +} + /* PATCH /v1/sales/opportunities/opportunity/:identifier/notes/:noteId */ export default createRoute( "patch", @@ -48,10 +62,10 @@ export default createRoute( : null, flagged: updated.flagged, enteredBy: await resolveMember(updated.enteredBy), - }, + } ); return c.json(response, response.status as ContentfulStatusCode); }, - authMiddleware({ permissions: ["sales.opportunity.note.update"] }), + authMiddleware({ permissions: ["sales.opportunity.note.update"] }) ); diff --git a/api/src/api/sales/opportunities/[id]/products/add.ts b/api/src/api/sales/opportunities/[id]/products/add.ts index 4701b56..487df3b 100644 --- a/api/src/api/sales/opportunities/[id]/products/add.ts +++ b/api/src/api/sales/opportunities/[id]/products/add.ts @@ -4,6 +4,8 @@ import { apiResponse } from "../../../../../modules/api-utils/apiResponse"; import { ContentfulStatusCode } from "hono/utils/http-status"; import { authMiddleware } from "../../../../middleware/authorization"; import { processObjectValuePerms } from "../../../../../modules/permission-utils/processObjectPermissions"; +import { ForecastProductController } from "../../../../../controllers/ForecastProductController"; +import { opportunityCw } from "../../../../../modules/cw-utils/opportunities/opportunities"; import { z } from "zod"; const productItemSchema = z @@ -26,6 +28,8 @@ const productItemSchema = z recurringCost: z.number().optional(), cycles: z.number().int().min(0).optional(), sequenceNumber: z.number().int().min(0).optional(), + procurementNotes: z.string().optional(), + productNarrative: z.string().optional(), }) .strict(); @@ -34,12 +38,25 @@ const addProductSchema = z.union([ z.array(productItemSchema).min(1, "At least one product is required"), ]); +const procurementCreateSchema = z.object({ + catalogItem: z.object({ id: z.number().int().positive() }), + description: z.string().min(1), + customerDescription: z.string().optional(), + quantity: z.number().positive().optional(), + price: z.number().optional(), + cost: z.number().optional(), + taxableFlag: z.boolean().optional(), + billableOption: z.string().optional(), + procurementNotes: z.string().optional(), + productNarrative: z.string().optional(), +}); + /* POST /v1/sales/opportunities/opportunity/:identifier/products */ export default createRoute( "post", ["/opportunities/opportunity/:identifier/products"], async (c) => { - const identifier = c.req.param("identifier"); + const identifier = c.req.param("identifier") as string; const body = await c.req.json(); const validated = addProductSchema.parse(body); @@ -56,46 +73,182 @@ export default createRoute( const item = await opportunities.fetchRecord(identifier); - // Strip customerDescription from forecast payloads — CW only accepts - // it on procurement products, not forecast items. - const customerDescriptions = gatedItems.map( - (g: any) => g.customerDescription, - ); - const forecastPayloads = gatedItems.map( - ({ customerDescription, ...rest }: any) => rest, - ); + // Procurement-first: when catalogItem is available after field-level + // permission gating, create through procurement. + // Fallback: if catalogItem is gated/missing, use forecast creation so + // callers without catalog permissions can still add products. + const procurementInputs: Array<{ + index: number; + payload: z.infer; + }> = []; + const forecastInputs: Array<{ + index: number; + payload: Record; + customerDescription?: string; + }> = []; - const created = await item.addProducts(forecastPayloads); + for (const [index, g] of gatedItems.entries()) { + const hasCatalogItem = + typeof g?.catalogItem?.id === "number" && g.catalogItem.id > 0; - // If any items included customerDescription, patch the linked - // procurement products after creation. This is best-effort since - // newly created forecast items may not have a linked procurement - // product yet. - const procurementUpdates = created - .map((product, idx) => ({ - product, - customerDescription: customerDescriptions[idx], - })) - .filter((entry) => entry.customerDescription != null); + if (!hasCatalogItem) { + const { customerDescription, ...forecastPayload } = g as any; + const normalizedForecastPayload: Record = { + ...forecastPayload, + }; - if (procurementUpdates.length > 0) { - await Promise.all( - procurementUpdates.map(({ product, customerDescription }) => - item - .updateProcurementProductByForecastItem(product.cwForecastId, { - customerDescription, - }) - .catch(() => null), - ), - ); + const hasAnyDescription = + typeof normalizedForecastPayload.forecastDescription === "string" || + typeof normalizedForecastPayload.productDescription === "string"; + + // CW rejects bare forecast objects with only defaults (status/type). + // Ensure a minimal description exists for permission-reduced payloads. + if (!hasAnyDescription) { + normalizedForecastPayload.forecastDescription = "Line Item"; + } + + forecastInputs.push({ + index, + payload: normalizedForecastPayload, + customerDescription: + typeof customerDescription === "string" + ? customerDescription + : undefined, + }); + continue; + } + + const quantity = + typeof g.quantity === "number" && g.quantity > 0 ? g.quantity : undefined; + const totalRevenue = + typeof g.revenue === "number" && Number.isFinite(g.revenue) + ? g.revenue + : undefined; + const totalCost = + typeof g.cost === "number" && Number.isFinite(g.cost) + ? g.cost + : undefined; + + const price = + totalRevenue === undefined + ? undefined + : quantity && quantity > 0 + ? totalRevenue / quantity + : totalRevenue; + const cost = + totalCost === undefined + ? undefined + : quantity && quantity > 0 + ? totalCost / quantity + : totalCost; + + procurementInputs.push({ + index, + payload: procurementCreateSchema.parse({ + catalogItem: g.catalogItem, + description: + g.productDescription ?? + g.forecastDescription ?? + g.customerDescription ?? + "Line Item", + customerDescription: + typeof g.customerDescription === "string" + ? g.customerDescription + : undefined, + quantity, + price, + cost, + taxableFlag: + typeof g.taxableFlag === "boolean" ? g.taxableFlag : undefined, + billableOption: "Billable", + procurementNotes: + typeof (g as any).procurementNotes === "string" + ? (g as any).procurementNotes + : undefined, + productNarrative: + typeof (g as any).productNarrative === "string" + ? (g as any).productNarrative + : undefined, + }), + }); } + const createdByIndex = new Map(); + + if (procurementInputs.length > 0) { + const createdProcurement = await item.addProcurementProducts( + procurementInputs.map((entry) => entry.payload), + ); + + const indexByForecastId = new Map(); + for (const [createdIdx, proc] of createdProcurement.entries()) { + if (typeof proc.forecastDetailId === "number") { + indexByForecastId.set( + proc.forecastDetailId, + procurementInputs[createdIdx]!.index, + ); + } + } + + if (indexByForecastId.size > 0) { + const createdForecastItems = + (await opportunityCw.fetchProducts(item.cwOpportunityId)).forecastItems?.filter( + (fi) => indexByForecastId.has(fi.id), + ) ?? []; + + for (const fi of createdForecastItems) { + const originalIndex = indexByForecastId.get(fi.id); + if (typeof originalIndex === "number") { + createdByIndex.set(originalIndex, new ForecastProductController(fi)); + } + } + } + } + + if (forecastInputs.length > 0) { + const createdForecast = await item.addProducts( + forecastInputs.map((entry) => entry.payload), + ); + + for (const [createdIdx, createdItem] of createdForecast.entries()) { + const input = forecastInputs[createdIdx]!; + createdByIndex.set(input.index, createdItem); + } + + const procurementUpdates = createdForecast + .map((product, idx) => ({ + product, + customerDescription: forecastInputs[idx]?.customerDescription, + })) + .filter((entry) => entry.customerDescription != null); + + if (procurementUpdates.length > 0) { + await Promise.all( + procurementUpdates.map(({ product, customerDescription }) => + item + .updateProcurementProductByForecastItem(product.cwForecastId, { + customerDescription, + }) + .catch(() => null), + ), + ); + } + } + + const created = inputItems + .map((_, index) => createdByIndex.get(index)) + .filter( + (entry): entry is ForecastProductController => entry !== undefined, + ); + const isBatch = Array.isArray(body); const response = apiResponse.created( isBatch ? `${created.length} product(s) added to opportunity successfully!` : "Product added to opportunity successfully!", - isBatch ? created.map((p) => p.toJson()) : created[0]!.toJson(), + isBatch + ? created.map((p) => p.toJson()) + : created[0]?.toJson() ?? null, ); return c.json(response, response.status as ContentfulStatusCode); }, diff --git a/api/src/api/sales/opportunities/[id]/products/cancel.ts b/api/src/api/sales/opportunities/[id]/products/cancel.ts index d124656..910d4ba 100644 --- a/api/src/api/sales/opportunities/[id]/products/cancel.ts +++ b/api/src/api/sales/opportunities/[id]/products/cancel.ts @@ -8,7 +8,7 @@ import { z } from "zod"; const cancelProductSchema = z .object({ - quantityCancelled: z.number().int().min(0), + quantityCancelled: z.number().min(0), cancellationReason: z.string().nullable().optional(), }) .strict(); diff --git a/api/src/api/sales/opportunities/[id]/products/update.ts b/api/src/api/sales/opportunities/[id]/products/update.ts index b5625ac..212bb5b 100644 --- a/api/src/api/sales/opportunities/[id]/products/update.ts +++ b/api/src/api/sales/opportunities/[id]/products/update.ts @@ -4,6 +4,9 @@ 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 { ForecastProductController } from "../../../../../controllers/ForecastProductController"; +import { opportunityCw } from "../../../../../modules/cw-utils/opportunities/opportunities"; +import { prisma } from "../../../../../constants"; import { z } from "zod"; const PRODUCT_NARRATIVE_FIELD_ID = 46; @@ -24,14 +27,14 @@ const updateProductSchema = z .refine( (value) => Object.values(value).some((item) => item !== undefined && item !== null), - "At least one editable field is required", + "At least one editable field is required" ); const upsertCustomTextField = ( fields: Array>, fieldId: number, caption: string, - value: string, + value: string ) => { const next = [...fields]; const idx = next.findIndex((f) => Number(f.id) === fieldId); @@ -76,12 +79,47 @@ export default createRoute( const input = updateProductSchema.parse(body); const opportunity = await opportunities.fetchRecord(identifier); - const forecastItems = await opportunity.fetchProducts(); - const forecastItem = forecastItems.find( - (item) => item.cwForecastId === productId, + const cwForecast = await opportunityCw.fetchProducts( + opportunity.cwOpportunityId + ); + const cwForecastItems = cwForecast.forecastItems ?? []; + const cwForecastIds = new Set(cwForecastItems.map((item) => item.id)); + + let forecastItemId = productId; + if (!cwForecastIds.has(forecastItemId)) { + const procurementItems = await opportunityCw.fetchProcurementProducts( + opportunity.cwOpportunityId + ); + + const matchedProcurement = procurementItems.find( + (item) => Number((item as any).id) === productId + ); + const mappedForecastDetailId = Number( + (matchedProcurement as any)?.forecastDetailId + ); + + if ( + Number.isInteger(mappedForecastDetailId) && + mappedForecastDetailId > 0 && + cwForecastIds.has(mappedForecastDetailId) + ) { + forecastItemId = mappedForecastDetailId; + console.warn( + "[ProductUpdate] Resolved procurement product ID to forecast item ID", + { + opportunity: identifier, + requestedProductId: productId, + resolvedForecastItemId: forecastItemId, + } + ); + } + } + + const rawForecastItem = cwForecastItems.find( + (item) => item.id === forecastItemId ); - if (!forecastItem) { + if (!rawForecastItem) { throw new GenericError({ status: 404, name: "ForecastItemNotFound", @@ -89,6 +127,7 @@ export default createRoute( }); } + const forecastItem = new ForecastProductController(rawForecastItem); const forecastJson = forecastItem.toJson(); const effectiveQuantity = input.quantity ?? forecastJson.quantity ?? 1; @@ -101,12 +140,12 @@ export default createRoute( } if (input.unitPrice !== undefined) { forecastPatch.revenue = Number( - (input.unitPrice * effectiveQuantity).toFixed(2), + (input.unitPrice * effectiveQuantity).toFixed(2) ); } if (input.unitCost !== undefined) { forecastPatch.cost = Number( - (input.unitCost * effectiveQuantity).toFixed(2), + (input.unitCost * effectiveQuantity).toFixed(2) ); } if (input.taxableFlag !== undefined) { @@ -114,19 +153,22 @@ export default createRoute( } const existingProcurement = - await opportunity.fetchProcurementProductByForecastItem(productId); + await opportunity.fetchProcurementProductByForecastItem(forecastItemId); - if ( - (input.productNarrative !== undefined || - input.procurementNotes !== undefined) && - !existingProcurement - ) { - throw new GenericError({ - status: 400, - name: "ProcurementLinkRequired", - message: - "Product Narrative and Procurement Notes can only be updated on products linked to a procurement record", - }); + const hasNarrativeOrNotesValue = + (input.productNarrative !== undefined && + input.productNarrative !== null) || + (input.procurementNotes !== undefined && input.procurementNotes !== null); + + if (hasNarrativeOrNotesValue && !existingProcurement) { + console.warn( + "[ProductUpdate] Ignoring procurement-only narrative fields for non-linked product", + { + opportunity: identifier, + productId: forecastItemId, + requestedProductId: productId, + } + ); } let updatedProcurement = existingProcurement; @@ -164,7 +206,7 @@ export default createRoute( updatedFields, PROCUREMENT_NOTES_FIELD_ID, "Procurement Notes", - input.procurementNotes, + input.procurementNotes ); } if ( @@ -175,7 +217,7 @@ export default createRoute( updatedFields, PRODUCT_NARRATIVE_FIELD_ID, "Product Narrative", - input.productNarrative, + input.productNarrative ); } if ( @@ -190,28 +232,47 @@ export default createRoute( if (Object.keys(procurementPatch).length > 0) { updatedProcurement = await opportunity.updateProcurementProductByForecastItem( - productId, - procurementPatch, + forecastItemId, + procurementPatch ); } } let updatedForecast = forecastJson; if (Object.keys(forecastPatch).length > 0) { - const patched = await opportunity.updateProduct(productId, forecastPatch); + const patched = await opportunity.updateProduct( + forecastItemId, + forecastPatch + ); updatedForecast = patched.toJson(); } + // Write changed fields back to the local ProductData row so fetchProducts() + // reflects the edit immediately without waiting for the next dalpuri sync. + const localPatch: Record = {}; + if (input.quantity !== undefined) localPatch.qty = input.quantity; + if (input.unitPrice !== undefined) localPatch.unitPrice = input.unitPrice; + if (input.unitCost !== undefined) localPatch.unitCost = input.unitCost; + if (input.productDescription !== undefined) localPatch.shortDescription = input.productDescription; + if (input.taxableFlag !== undefined) localPatch.taxableFlag = input.taxableFlag; + if (input.productNarrative !== undefined) localPatch.productNarrative = input.productNarrative; + if (Object.keys(localPatch).length > 0) { + await prisma.productData.update({ + where: { id: productId }, + data: localPatch, + }); + } + const updatedFields = Array.isArray(updatedProcurement?.customFields) ? updatedProcurement.customFields : []; const procurementNotes = updatedFields.find( - (field: any) => field?.id === PROCUREMENT_NOTES_FIELD_ID, + (field: any) => field?.id === PROCUREMENT_NOTES_FIELD_ID )?.value ?? null; const productNarrative = updatedFields.find( - (field: any) => field?.id === PRODUCT_NARRATIVE_FIELD_ID, + (field: any) => field?.id === PRODUCT_NARRATIVE_FIELD_ID )?.value ?? null; const quantity = @@ -230,11 +291,13 @@ export default createRoute( quantity, unitPrice, unitCost, + requestedProductId: productId, + forecastItemId, procurementNotes, productNarrative, }); return c.json(response, response.status as ContentfulStatusCode); }, - authMiddleware({ permissions: ["sales.opportunity.product.update"] }), + authMiddleware({ permissions: ["sales.opportunity.product.update"] }) ); diff --git a/api/src/api/sales/opportunities/[id]/quotes/fetchNarrative.ts b/api/src/api/sales/opportunities/[id]/quotes/fetchNarrative.ts new file mode 100644 index 0000000..fdcbe73 --- /dev/null +++ b/api/src/api/sales/opportunities/[id]/quotes/fetchNarrative.ts @@ -0,0 +1,25 @@ +import { createRoute } from "../../../../../modules/api-utils/createRoute"; +import { opportunities } from "../../../../../managers/opportunities"; +import { apiResponse } from "../../../../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../../../../middleware/authorization"; + +/* GET /v1/sales/opportunities/opportunity/:identifier/quotes/narrative */ +export default createRoute( + "get", + ["/opportunities/opportunity/:identifier/quotes/narrative"], + async (c) => { + const identifier = c.req.param("identifier"); + const item = await opportunities.fetchRecord(identifier); + const quoteNarrative = await item.fetchQuoteNarrative(); + + const response = apiResponse.successful( + "Quote narrative fetched successfully!", + { + quoteNarrative, + } + ); + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["sales.opportunity.fetch"] }) +); diff --git a/api/src/api/sales/opportunities/[id]/quotes/preview.ts b/api/src/api/sales/opportunities/[id]/quotes/preview.ts index bd47321..ff08598 100644 --- a/api/src/api/sales/opportunities/[id]/quotes/preview.ts +++ b/api/src/api/sales/opportunities/[id]/quotes/preview.ts @@ -53,9 +53,9 @@ export default createRoute( { mimeType: "application/pdf", contentBase64: previewBase64, - }, + } ); return c.json(response, response.status as ContentfulStatusCode); }, - authMiddleware({ permissions: ["sales.opportunity.quote.preview"] }), + authMiddleware({ permissions: ["sales.opportunity.quote.preview"] }) ); diff --git a/api/src/api/sales/opportunities/[id]/quotes/updateNarrative.ts b/api/src/api/sales/opportunities/[id]/quotes/updateNarrative.ts new file mode 100644 index 0000000..e1a786b --- /dev/null +++ b/api/src/api/sales/opportunities/[id]/quotes/updateNarrative.ts @@ -0,0 +1,38 @@ +import { createRoute } from "../../../../../modules/api-utils/createRoute"; +import { opportunities } from "../../../../../managers/opportunities"; +import { apiResponse } from "../../../../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../../../../middleware/authorization"; +import { z } from "zod"; + +const quoteNarrativeSchema = z + .object({ + quoteNarrative: z.string().optional().nullable(), + }) + .strict(); + +/* PATCH /v1/sales/opportunities/opportunity/:identifier/quotes/narrative */ +export default createRoute( + "patch", + ["/opportunities/opportunity/:identifier/quotes/narrative"], + async (c) => { + const identifier = c.req.param("identifier"); + const body = quoteNarrativeSchema.parse( + await c.req.json().catch(() => ({})) + ); + + const item = await opportunities.fetchRecord(identifier); + const quoteNarrative = await item.updateQuoteNarrative( + body.quoteNarrative ?? null + ); + + const response = apiResponse.successful( + "Quote narrative saved successfully!", + { + quoteNarrative, + } + ); + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["sales.opportunity.update"] }) +); diff --git a/api/src/api/sales/opportunities/[id]/update.ts b/api/src/api/sales/opportunities/[id]/update.ts index de9b37d..7e4a57c 100644 --- a/api/src/api/sales/opportunities/[id]/update.ts +++ b/api/src/api/sales/opportunities/[id]/update.ts @@ -10,6 +10,7 @@ const updateSchema = z .object({ name: z.string().min(1).optional(), notes: z.string().optional(), + interest: z.enum(["HOT", "WARM", "COLD"]).nullable().optional(), rating: z.object({ id: z.number() }).optional(), type: z.object({ id: z.number() }).optional(), stage: z.object({ id: z.number() }).optional(), @@ -50,7 +51,7 @@ export default createRoute( const response = apiResponse.successful( "Opportunity updated successfully!", - updated.toJson(), + updated.toJson() ); return c.json(response, response.status as ContentfulStatusCode); @@ -77,7 +78,7 @@ export default createRoute( errors: cwErrors, meta: { timestamp: Date.now() }, }, - cwStatus as ContentfulStatusCode, + cwStatus as ContentfulStatusCode ); } @@ -89,5 +90,5 @@ export default createRoute( }); } }, - authMiddleware({ permissions: ["sales.opportunity.update"] }), + authMiddleware({ permissions: ["sales.opportunity.update"] }) ); diff --git a/api/src/api/sales/opportunities/create.ts b/api/src/api/sales/opportunities/create.ts index 583a3a3..5dac696 100644 --- a/api/src/api/sales/opportunities/create.ts +++ b/api/src/api/sales/opportunities/create.ts @@ -6,6 +6,7 @@ import { authMiddleware } from "../../middleware/authorization"; import GenericError from "../../../Errors/GenericError"; import { z } from "zod"; import { cwMembers } from "../../../managers/cwMembers"; +import { prisma } from "../../../constants"; import { createWorkflowActivity, OptimaType, @@ -18,6 +19,7 @@ const createSchema = z.object({ .min(1) .transform((v) => new Date(v).toISOString().replace(/\.\d{3}Z$/, "Z")), notes: z.string().optional(), + interest: z.enum(["HOT", "WARM", "COLD"]).nullable().optional(), rating: z.object({ id: z.number() }).optional(), type: z.object({ id: z.number() }).optional(), stage: z.object({ id: z.number() }).optional(), @@ -42,9 +44,18 @@ export default createRoute( async (c) => { const body = await c.req.json(); const data = createSchema.parse(body); + const { interest, ...cwCreateData } = data; try { - const item = await opportunities.createItem(data); + const item = await opportunities.createItem(cwCreateData); + + if (interest !== undefined) { + await prisma.opportunity.update({ + where: { uid: item.id }, + data: { interest }, + }); + item.interest = interest; + } // Create a workflow activity for the new opportunity try { @@ -69,14 +80,14 @@ export default createRoute( } catch (activityErr) { console.error( "[Opportunity Create] Failed to create workflow activity:", - activityErr, + activityErr ); // Don't fail the opportunity creation if the activity fails } const response = apiResponse.created( "Opportunity created successfully!", - item.toJson(), + item.toJson() ); return c.json(response, response.status as ContentfulStatusCode); @@ -103,7 +114,7 @@ export default createRoute( errors: cwErrors, meta: { timestamp: Date.now() }, }, - cwStatus as ContentfulStatusCode, + cwStatus as ContentfulStatusCode ); } @@ -115,5 +126,5 @@ export default createRoute( }); } }, - authMiddleware({ permissions: ["sales.opportunity.create"] }), + authMiddleware({ permissions: ["sales.opportunity.create"] }) ); diff --git a/api/src/api/sales/opportunities/metrics.ts b/api/src/api/sales/opportunities/metrics.ts index ca8ae57..a15ff55 100644 --- a/api/src/api/sales/opportunities/metrics.ts +++ b/api/src/api/sales/opportunities/metrics.ts @@ -2,12 +2,8 @@ import { createRoute } from "../../../modules/api-utils/createRoute"; 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 { - getSalesOpportunityMetricsAll, - getSalesOpportunityMetricsForMember, - refreshSalesOpportunityMetricsCache, -} from "../../../modules/cache/salesOpportunityMetricsCache"; +import { getSalesOpportunityMetricsForMember, getSalesOpportunityMetricsAll } from "../../../modules/cache/salesOpportunityMetricsCache"; +import { prisma } from "../../../constants"; /* GET /v1/sales/opportunities/metrics */ export default createRoute( @@ -15,60 +11,39 @@ export default createRoute( ["/opportunities/metrics"], async (c) => { const user = c.get("user"); - const scope = (c.req.query("scope") ?? "me").toLowerCase(); - const requestedIdentifier = c.req.query("identifier")?.trim().toLowerCase(); - const currentUserIdentifier = user?.cwIdentifier?.trim().toLowerCase(); - - if ( - scope === "all" && - !(await user.hasPermission("sales.opportunity.metrics.all")) - ) { - throw new GenericError({ - name: "InsufficientPermission", - message: - "You do not have permission to view metrics for all active members.", - status: 403, - }); - } - - const usingIdentifierOverride = - scope !== "all" && - !!requestedIdentifier && - requestedIdentifier !== currentUserIdentifier; - - if ( - usingIdentifierOverride && - !(await user.hasPermission( - "sales.opportunity.metrics.identifier.override", - )) - ) { - throw new GenericError({ - name: "InsufficientPermission", - message: - "You do not have permission to query metrics by overriding the member identifier.", - status: 403, - }); - } - - const requireWarmCache = async () => { - const all = await getSalesOpportunityMetricsAll(); - if (all) return all; - await refreshSalesOpportunityMetricsCache(); - return getSalesOpportunityMetricsAll(); - }; + const scope = c.req.query("scope"); + const identifierOverride = c.req.query("identifier"); + // scope=all — return metrics for all active members (requires permission) if (scope === "all") { - const all = await requireWarmCache(); + if (!(await user.hasPermission("sales.opportunity.metrics.all"))) { + return c.json({ status: 403, message: "Forbidden", successful: false }, 403); + } + const allMetrics = await getSalesOpportunityMetricsAll(); const response = apiResponse.successful( "Sales opportunity metrics fetched successfully!", - all, + allMetrics, ); return c.json(response, response.status as ContentfulStatusCode); } - const targetIdentifier = requestedIdentifier ?? currentUserIdentifier; + // Determine which CW identifier to look up + let cwIdentifier: string | null = null; - if (!targetIdentifier) { + if (identifierOverride) { + if (!(await user.hasPermission("sales.opportunity.metrics.identifier.override"))) { + return c.json({ status: 403, message: "Forbidden", successful: false }, 403); + } + cwIdentifier = identifierOverride; + } else { + const dbUser = await prisma.user.findFirst({ + where: { id: user.id }, + select: { cwIdentifier: true }, + }); + cwIdentifier = dbUser?.cwIdentifier ?? null; + } + + if (!cwIdentifier) { const response = apiResponse.successful( "Sales opportunity metrics fetched successfully!", null, @@ -76,18 +51,10 @@ export default createRoute( return c.json(response, response.status as ContentfulStatusCode); } - let metrics = await getSalesOpportunityMetricsForMember(targetIdentifier); - if (!metrics) { - await refreshSalesOpportunityMetricsCache(); - metrics = await getSalesOpportunityMetricsForMember(targetIdentifier); - } - + const metrics = await getSalesOpportunityMetricsForMember(cwIdentifier); const response = apiResponse.successful( "Sales opportunity metrics fetched successfully!", - { - identifier: targetIdentifier, - metrics, - }, + metrics, ); return c.json(response, response.status as ContentfulStatusCode); }, diff --git a/api/src/api/schedules/[id]/fetch.ts b/api/src/api/schedules/[id]/fetch.ts new file mode 100644 index 0000000..75a71ea --- /dev/null +++ b/api/src/api/schedules/[id]/fetch.ts @@ -0,0 +1,22 @@ +import { createRoute } from "../../../modules/api-utils/createRoute"; +import { schedules } from "../../../managers/schedules"; +import { apiResponse } from "../../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../../middleware/authorization"; + +/* /v1/schedule/schedules/:identifier */ +export default createRoute( + "get", + ["/schedules/:identifier"], + async (c) => { + const schedule = await schedules.fetch(c.req.param("identifier")); + + const response = apiResponse.successful( + "Schedule Fetched Successfully!", + schedule.toJson(), + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["schedule.fetch"] }), +); diff --git a/api/src/api/schedules/count.ts b/api/src/api/schedules/count.ts new file mode 100644 index 0000000..6db3b9a --- /dev/null +++ b/api/src/api/schedules/count.ts @@ -0,0 +1,22 @@ +import { createRoute } from "../../modules/api-utils/createRoute"; +import { schedules } from "../../managers/schedules"; +import { apiResponse } from "../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../middleware/authorization"; + +/* /v1/schedule/count */ +export default createRoute( + "get", + ["/count"], + async (c) => { + const count = await schedules.count(); + + const response = apiResponse.successful( + "Schedule count fetched successfully!", + { count }, + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["schedule.fetch.many"] }), +); diff --git a/api/src/api/schedules/fetchAll.ts b/api/src/api/schedules/fetchAll.ts new file mode 100644 index 0000000..d82e796 --- /dev/null +++ b/api/src/api/schedules/fetchAll.ts @@ -0,0 +1,42 @@ +import { createRoute } from "../../modules/api-utils/createRoute"; +import { schedules } from "../../managers/schedules"; +import { apiResponse } from "../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../middleware/authorization"; + +/* /v1/schedule/schedules */ +export default createRoute( + "get", + ["/schedules"], + async (c) => { + const page = new Number(c.req.query("page") ?? 1) as number; + const rpp = new Number(c.req.query("rpp") ?? 30) as number; + const search = c.req.query("search") as string; + + const data = search + ? await schedules.search(search, page, rpp) + : await schedules.fetchPages(page, rpp); + + const totalRecords = search + ? (await schedules.search(search, 1, 999999)).length + : await schedules.count(); + + const response = apiResponse.successful( + "Schedules Fetched Successfully!", + data.map((s) => s.toJson()), + { + pagination: { + previousPage: page == 1 ? null : page - 1, + currentPage: page, + nextPage: page >= totalRecords / rpp ? null : page + 1, + totalPages: Math.ceil(totalRecords / rpp), + totalRecords, + listedRecords: rpp, + }, + }, + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["schedule.fetch.many"] }), +); diff --git a/api/src/api/schedules/index.ts b/api/src/api/schedules/index.ts new file mode 100644 index 0000000..a21de37 --- /dev/null +++ b/api/src/api/schedules/index.ts @@ -0,0 +1,7 @@ +import { default as fetchAll } from "./fetchAll"; +import { default as fetch } from "./[id]/fetch"; +import { default as count } from "./count"; +import { default as fetchByDateRange } from "./member/fetchByDateRange"; +import { default as fetchMySchedule } from "./me/fetchMySchedule"; + +export { count, fetch, fetchAll, fetchByDateRange, fetchMySchedule }; diff --git a/api/src/api/schedules/me/fetchMySchedule.ts b/api/src/api/schedules/me/fetchMySchedule.ts new file mode 100644 index 0000000..61b6730 --- /dev/null +++ b/api/src/api/schedules/me/fetchMySchedule.ts @@ -0,0 +1,63 @@ +import { createRoute } from "../../../modules/api-utils/createRoute"; +import { schedules } from "../../../managers/schedules"; +import { apiResponse } from "../../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../../middleware/authorization"; +import GenericError from "../../../Errors/GenericError"; + +/* /v1/schedule/@me?start=&end= */ +export default createRoute( + "get", + ["/@me"], + async (c) => { + const user = c.get("user"); + + if (!user?.cwIdentifier) { + throw new GenericError({ + name: "BadRequest", + message: "Your account is not linked to a ConnectWise member. Cannot fetch schedule.", + status: 400, + }); + } + + const startParam = c.req.query("start"); + const endParam = c.req.query("end"); + + if (!startParam || !endParam) { + throw new GenericError({ + name: "BadRequest", + message: "Query params 'start' and 'end' are required (ISO 8601 date strings).", + status: 400, + }); + } + + const startDate = new Date(startParam); + const endDate = new Date(endParam); + + if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { + throw new GenericError({ + name: "BadRequest", + message: "Invalid date format. Use ISO 8601 (e.g. 2026-04-01T00:00:00Z).", + status: 400, + }); + } + + if (startDate >= endDate) { + throw new GenericError({ + name: "BadRequest", + message: "'start' must be before 'end'.", + status: 400, + }); + } + + const data = await schedules.fetchByMemberDateRange(user.cwIdentifier, startDate, endDate); + + const response = apiResponse.successful( + "Your schedule entries fetched successfully!", + data.map((s) => s.toJson()), + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["schedule.fetch"] }), +); diff --git a/api/src/api/schedules/member/fetchByDateRange.ts b/api/src/api/schedules/member/fetchByDateRange.ts new file mode 100644 index 0000000..82f4131 --- /dev/null +++ b/api/src/api/schedules/member/fetchByDateRange.ts @@ -0,0 +1,62 @@ +import { createRoute } from "../../../modules/api-utils/createRoute"; +import { schedules } from "../../../managers/schedules"; +import { apiResponse } from "../../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../../middleware/authorization"; +import GenericError from "../../../Errors/GenericError"; + +/* /v1/schedule/member/:memberId?start=&end= */ +export default createRoute( + "get", + ["/member/:memberId"], + async (c) => { + const memberId = c.req.param("memberId"); + const startParam = c.req.query("start"); + const endParam = c.req.query("end"); + + if (!memberId) { + throw new GenericError({ + name: "BadRequest", + message: "memberId path parameter is required.", + status: 400, + }); + } + + if (!startParam || !endParam) { + throw new GenericError({ + name: "BadRequest", + message: "Query params 'start' and 'end' are required (ISO 8601 date strings).", + status: 400, + }); + } + + const startDate = new Date(startParam); + const endDate = new Date(endParam); + + if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { + throw new GenericError({ + name: "BadRequest", + message: "Invalid date format. Use ISO 8601 (e.g. 2026-04-01T00:00:00Z).", + status: 400, + }); + } + + if (startDate >= endDate) { + throw new GenericError({ + name: "BadRequest", + message: "'start' must be before 'end'.", + status: 400, + }); + } + + const data = await schedules.fetchByMemberDateRange(memberId, startDate, endDate); + + const response = apiResponse.successful( + "Schedule entries fetched successfully!", + data.map((s) => s.toJson()), + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["schedule.fetch.many"] }), +); diff --git a/api/src/api/server.ts b/api/src/api/server.ts index 9c8d3cf..e52907e 100644 --- a/api/src/api/server.ts +++ b/api/src/api/server.ts @@ -4,6 +4,18 @@ import { ZodError } from "zod"; import { cors } from "hono/cors"; import GenericError from "../Errors/GenericError"; import teapot from "./teapot"; +import authRouter from "./routers/authRouter"; +import userRouter from "./routers/user"; +import companyRouter from "./routers/companyRouter"; +import credentialRouter from "./routers/credentialRouter"; +import credentialTypeRouter from "./routers/credentialTypeRouter"; +import roleRouter from "./routers/roleRouter"; +import permissionRouter from "./routers/permissionRouter"; +import unifiRouter from "./routers/unifiRouter"; +import procurementRouter from "./routers/procurementRouter"; +import salesRouter from "./routers/salesRouter"; +import cwRouter from "./routers/cwRouter"; +import scheduleRouter from "./routers/scheduleRouter"; const app = new Hono(); const v1 = new Hono(); @@ -24,7 +36,7 @@ app.onError((err, ctx) => { return ctx.json( apiResponse.zodError(err), //@ts-ignore - apiResponse.zodError(err).status, + apiResponse.zodError(err).status ); } @@ -41,23 +53,24 @@ app.notFound((c) => { message: `Cannot ${c.req.method.toUpperCase()} ${c.req.path}`, status: 404, cause: "Unknown", - }), + }) ); return c.json(response, response.status); }); v1.route("/teapot", teapot); -v1.route("/auth", require("./routers/authRouter").default); -v1.route("/user", require("./routers/user").default); -v1.route("/company", require("./routers/companyRouter").default); -v1.route("/credential", require("./routers/credentialRouter").default); -v1.route("/credential-type", require("./routers/credentialTypeRouter").default); -v1.route("/role", require("./routers/roleRouter").default); -v1.route("/permissions", require("./routers/permissionRouter").default); -v1.route("/unifi", require("./routers/unifiRouter").default); -v1.route("/procurement", require("./routers/procurementRouter").default); -v1.route("/sales", require("./routers/salesRouter").default); -v1.route("/cw", require("./routers/cwRouter").default); +v1.route("/auth", authRouter); +v1.route("/user", userRouter); +v1.route("/company", companyRouter); +v1.route("/credential", credentialRouter); +v1.route("/credential-type", credentialTypeRouter); +v1.route("/role", roleRouter); +v1.route("/permissions", permissionRouter); +v1.route("/unifi", unifiRouter); +v1.route("/procurement", procurementRouter); +v1.route("/sales", salesRouter); +v1.route("/cw", cwRouter); +v1.route("/schedule", scheduleRouter); app.route("/v1", v1); export default app; diff --git a/api/src/api/sockets/events/liveQuotePreview.ts b/api/src/api/sockets/events/liveQuotePreview.ts index 924c2ec..3080438 100644 --- a/api/src/api/sockets/events/liveQuotePreview.ts +++ b/api/src/api/sockets/events/liveQuotePreview.ts @@ -2,7 +2,7 @@ import { Socket } from "socket.io"; import { attachSocketEventPermissions } from "../middleware/authorization"; import { opportunities } from "../../../managers/opportunities"; -const LIVE_QUOTE_PREVIEW_PERMISSION = "sales.opportunity.fetch"; +const LIVE_QUOTE_PREVIEW_PERMISSION = "sales.opportunity.quote.preview"; export const registerLiveQuotePreviewHandlers = (socket: Socket) => { attachSocketEventPermissions(socket, { @@ -15,7 +15,7 @@ export const registerLiveQuotePreviewHandlers = (socket: Socket) => { "opp:live_quote_preview", async ( payload: { id?: string | number }, - ack?: (response: { ok: boolean; event?: string; error?: string }) => void, + ack?: (response: { ok: boolean; event?: string; error?: string }) => void ) => { const oppId = payload?.id; const normalizedId = @@ -97,6 +97,6 @@ export const registerLiveQuotePreviewHandlers = (socket: Socket) => { id: normalizedId, event: dataEvent, }); - }, + } ); }; diff --git a/api/src/api/unifi/site/wifi/fetchAll.ts b/api/src/api/unifi/site/wifi/fetchAll.ts index ce5132f..0a8d065 100644 --- a/api/src/api/unifi/site/wifi/fetchAll.ts +++ b/api/src/api/unifi/site/wifi/fetchAll.ts @@ -15,17 +15,15 @@ export default createRoute( const processWlans = await Promise.all( wlans.map((wlan) => - processObjectValuePerms(wlan, "unifi.site.wifi.read", c.get("user")), - ), + processObjectValuePerms(wlan, "unifi.site.wifi.read", c.get("user")) + ) ); - console.log(processWlans); - const response = apiResponse.successful( "UniFi WiFi Networks Fetched Successfully!", - processWlans, + processWlans ); return c.json(response, response.status as ContentfulStatusCode); }, - authMiddleware({ permissions: ["unifi.access", "unifi.site.wifi"] }), + authMiddleware({ permissions: ["unifi.access", "unifi.site.wifi"] }) ); diff --git a/api/src/api/user/@me/update.ts b/api/src/api/user/@me/update.ts index c6c52cc..c83bea7 100644 --- a/api/src/api/user/@me/update.ts +++ b/api/src/api/user/@me/update.ts @@ -7,6 +7,8 @@ import { authMiddleware } from "../../middleware/authorization"; const updateSchema = z .object({ name: z.string().optional(), + firstName: z.string().nullable().optional(), + lastName: z.string().nullable().optional(), image: z.string().optional(), }) .strict(); @@ -19,10 +21,10 @@ export default createRoute( const updatedUser = await c.get("user")?.update(body); const response = apiResponse.successful( "Successfully updated user.", - updatedUser?.toJson(), + updatedUser?.toJson() ); return c.json(response, response.status as ContentfulStatusCode); }, - authMiddleware({ scopes: ["user.write"] }), + authMiddleware({ scopes: ["user.write"] }) ); diff --git a/api/src/api/user/update.ts b/api/src/api/user/update.ts index 29009e9..39d1dcd 100644 --- a/api/src/api/user/update.ts +++ b/api/src/api/user/update.ts @@ -9,6 +9,8 @@ import GenericError from "../../Errors/GenericError"; const updateSchema = z .object({ name: z.string().optional(), + firstName: z.string().nullable().optional(), + lastName: z.string().nullable().optional(), image: z.string().optional(), roles: z.array(z.string()).optional(), permissions: z.array(z.string()).optional(), @@ -60,9 +62,9 @@ export default createRoute( const response = apiResponse.successful( "User Updated Successfully!", - user.toJson(), + user.toJson() ); return c.json(response, response.status as ContentfulStatusCode); }, - authMiddleware({ permissions: ["user.write.other"] }), + authMiddleware({ permissions: ["user.write.other"] }) ); diff --git a/api/src/constants.ts b/api/src/constants.ts index 7cb5663..ef9f6d4 100644 --- a/api/src/constants.ts +++ b/api/src/constants.ts @@ -4,7 +4,6 @@ import { Prisma, PrismaClient } from "../generated/prisma/client"; import * as msal from "@azure/msal-node"; import { Server } from "socket.io"; import { Server as Engine } from "@socket.io/bun-engine"; -import { io as createSocketClient } from "socket.io-client"; import axios from "axios"; import { UnifiClient } from "./modules/unifi-api/UnifiClient"; import { attachCwApiLogger } from "./modules/cw-utils/cwApiLogger"; @@ -13,18 +12,11 @@ import Redis from "ioredis"; const connectionString = `${process.env.DATABASE_URL}`; const adapter = new PrismaPg({ connectionString }); -interface EnvKey { - PORT: number; -} - // ENV CONSTANTS export const PORT = process.env.PORT; export const API_BASE_URL = process.env.API_BASE_URL || `http://localhost:${PORT || 3000}`; -export const COLLECTOR_WS_URL = - process.env.COLLECTOR_WS_URL || "http://localhost:7204"; -export const COLLECTOR_PSK = process.env.COLLECTOR_PSK || ""; export const prisma = new PrismaClient({ adapter }); @@ -77,23 +69,6 @@ const engine = new Engine(); io.bind(engine); export { io, engine }; -export const collectorSocket = createSocketClient(COLLECTOR_WS_URL, { - autoConnect: true, - reconnection: true, - reconnectionDelay: 1000, - reconnectionDelayMax: 5000, - transports: ["websocket"], - auth: COLLECTOR_PSK ? { psk: COLLECTOR_PSK } : undefined, -}); - -export const connectCollectorSocket = () => { - if (collectorSocket.connected) { - return; - } - - collectorSocket.connect(); -}; - // Connectwise API Client const connectWiseApi = axios.create({ diff --git a/api/src/controllers/ActivityController.ts b/api/src/controllers/ActivityController.ts index 61c4893..5cd26da 100644 --- a/api/src/controllers/ActivityController.ts +++ b/api/src/controllers/ActivityController.ts @@ -5,7 +5,6 @@ import { CWCreateActivity, } from "../modules/cw-utils/activities/activity.types"; import { activityCw } from "../modules/cw-utils/activities/activities"; -import { fetchActivity } from "../modules/cw-utils/activities/fetchActivity"; /** * Activity Controller @@ -127,26 +126,6 @@ export class ActivityController { this.cwUpdatedBy = data._info?.updatedBy ?? null; } - /** - * Refresh from ConnectWise - * - * Fetches the latest activity data from CW and returns - * a new ActivityController instance with updated state. - */ - public async refreshFromCW(): Promise { - const cwData = await fetchActivity(this.cwActivityId); - return new ActivityController(cwData); - } - - /** - * Fetch raw CW data - * - * Returns the raw ConnectWise activity object. - */ - public async fetchCwData(): Promise { - return fetchActivity(this.cwActivityId); - } - /** * Update in ConnectWise * diff --git a/api/src/controllers/CatalogItemController.ts b/api/src/controllers/CatalogItemController.ts index b16712d..3f5e029 100644 --- a/api/src/controllers/CatalogItemController.ts +++ b/api/src/controllers/CatalogItemController.ts @@ -1,9 +1,27 @@ -import { CatalogItem } from "../../generated/prisma/client"; +import { + CatalogItem, + CatalogCategory, + CatalogSubcategory, + CatalogManufacturer, +} from "../../generated/prisma/client"; import { prisma } from "../constants"; import { catalogCw } from "../modules/cw-utils/procurement/catalog"; import { CatalogItem as CWCatalogItem } from "../modules/cw-utils/procurement/catalog.types"; import GenericError from "../Errors/GenericError"; +/** + * Shape of the Prisma result when catalog items are queried with + * `include: { linkedItems, manufacturer, subcategory: { include: { category } } }`. + */ +type CatalogItemWithRelations = CatalogItem & { + linkedItems?: (CatalogItem & { + manufacturer?: CatalogManufacturer | null; + subcategory?: CatalogSubcategory & { category?: CatalogCategory | null }; + })[]; + manufacturer?: CatalogManufacturer | null; + subcategory?: (CatalogSubcategory & { category?: CatalogCategory | null }) | null; +}; + /** * Catalog Item Controller * @@ -12,13 +30,15 @@ import GenericError from "../Errors/GenericError"; * the internal database representation with ConnectWise catalog data. */ export class CatalogItemController { + /** The ConnectWise catalog record ID (`CatalogItem.id` in Prisma — Int @unique). */ + public readonly cwCatalogId: number; + /** The Prisma primary key (UUID). */ public readonly id: string; public name: string; public description: string | null; public customerDescription: string | null; public internalNotes: string | null; - public readonly cwCatalogId: number; public readonly identifier: string | null; public category: string | null; @@ -48,24 +68,29 @@ export class CatalogItemController { public readonly createdAt: Date; public updatedAt: Date; - constructor( - itemData: CatalogItem & { - linkedItems?: CatalogItem[]; - }, - ) { - this.id = itemData.id; + constructor(itemData: CatalogItemWithRelations) { + // `id` (Int @unique) is the ConnectWise catalog record ID. + // `uid` (String @id) is the Prisma primary key. + this.cwCatalogId = itemData.id; + this.id = itemData.uid; this.name = itemData.name; this.description = itemData.description; this.customerDescription = itemData.customerDescription; this.internalNotes = itemData.internalNotes; - this.cwCatalogId = itemData.cwCatalogId; this.identifier = itemData.identifier; - this.category = itemData.category; - this.categoryCwId = itemData.categoryCwId; - this.subcategory = itemData.subcategory; - this.subcategoryCwId = itemData.subcategoryCwId; - this.manufacturer = itemData.manufacturer; - this.manufactureCwId = itemData.manufactureCwId; + + // Extract relation data into flat fields + const sub = itemData.subcategory; + const cat = sub?.category; + const mfr = itemData.manufacturer; + + this.category = cat?.name ?? null; + this.categoryCwId = cat?.id ?? null; + this.subcategory = sub?.name ?? null; + this.subcategoryCwId = sub?.id ?? null; + this.manufacturer = mfr?.name ?? null; + this.manufactureCwId = mfr?.id ?? null; + this.partNumber = itemData.partNumber; this.vendorName = itemData.vendorName; this.vendorSku = itemData.vendorSku; @@ -97,7 +122,7 @@ export class CatalogItemController { if (onHand !== this.onHand) { await prisma.catalogItem.update({ - where: { id: this.id }, + where: { uid: this.id }, data: { onHand }, }); this.onHand = onHand; @@ -137,7 +162,7 @@ export class CatalogItemController { } const target = await prisma.catalogItem.findFirst({ - where: { id: targetId }, + where: { uid: targetId }, }); if (!target) { @@ -150,9 +175,9 @@ export class CatalogItemController { } const updated = await prisma.catalogItem.update({ - where: { id: this.id }, + where: { uid: this.id }, data: { - linkedItems: { connect: { id: targetId } }, + linkedItems: { connect: { uid: targetId } }, }, include: { linkedItems: true }, }); @@ -174,9 +199,9 @@ export class CatalogItemController { */ public async unlinkItem(targetId: string): Promise { const updated = await prisma.catalogItem.update({ - where: { id: this.id }, + where: { uid: this.id }, data: { - linkedItems: { disconnect: { id: targetId } }, + linkedItems: { disconnect: { uid: targetId } }, }, include: { linkedItems: true }, }); diff --git a/api/src/controllers/CompanyController.ts b/api/src/controllers/CompanyController.ts index 4b5342b..7126b39 100644 --- a/api/src/controllers/CompanyController.ts +++ b/api/src/controllers/CompanyController.ts @@ -1,204 +1,270 @@ -import { Company } from "../../generated/prisma/client"; -import { connectWiseApi } from "../constants"; -import { fetchCwCompanyById } from "../modules/cw-utils/fetchCompany"; -import { fetchCompanyConfigurations } from "../modules/cw-utils/configurations/fetchCompanyConfigurations"; -import { updateCwInternalCompany } from "../modules/cw-utils/updateCompany"; import { - fetchCompanySites, - fetchCompanySite, - serializeCwSite, -} from "../modules/cw-utils/sites/companySites"; -import { Company as CWCompany, Contact } from "../types/ConnectWiseTypes"; + Company, + CompanyAddress, + Contact, +} from "../../generated/prisma/client"; +import { connectWiseApi, prisma } from "../constants"; +import GenericError from "../Errors/GenericError"; +import { processConfigurationResponse } from "../modules/cw-utils/configurations/processConfigurationResponse"; +import { withCwRetry } from "../modules/cw-utils/withCwRetry"; +import type { ConfigurationResponse } from "../types/ConnectWiseTypes"; + +// Type for company data with relations +type CompanyWithRelations = Company & { + contacts?: Contact[]; + companyAddresses?: CompanyAddress[]; +}; /** * Company Controller * - * This class is for creating a controller that can manage company data, - * synchronize with external systems, and provide methods for accessing - * and updating company information within our internal system. + * This class manages company data from the local database. + * Data is synced from ConnectWise via the dalpuri service. */ export class CompanyController { - public readonly id: string; + public readonly id: number; + public readonly uid: string; public name: string; - public readonly cw_Identifier: string; - public readonly cw_CompanyId: number; - public cw_Data?: { - company: CWCompany; - defaultContact: Contact | null; - allContacts: Contact[]; - }; + public phone: string | null; + public website: string | null; - constructor(companyData: Company, cwData?: typeof this.cw_Data) { + private _contacts: Contact[] = []; + private _addresses: CompanyAddress[] = []; + private _defaultContact: Contact | null = null; + private _defaultAddress: CompanyAddress | null = null; + + constructor(companyData: CompanyWithRelations) { this.id = companyData.id; + this.uid = companyData.uid; this.name = companyData.name; - this.cw_Identifier = companyData.cw_Identifier; - this.cw_CompanyId = companyData.cw_CompanyId; - this.cw_Data = cwData; + this.phone = companyData.phone; + this.website = companyData.website; + + if (companyData.contacts) { + this._contacts = companyData.contacts; + this._defaultContact = + companyData.contacts.find((c) => c.default) ?? null; + } + + if (companyData.companyAddresses) { + this._addresses = companyData.companyAddresses; + this._defaultAddress = + companyData.companyAddresses.find((a) => a.defaultFlag) ?? null; + } } /** - * Hydrate CW Data + * Hydrate Data * - * Fetches and populates the full ConnectWise company data - * (company, default contact, all contacts) if not already loaded. - * - * @returns {ThisType} + * Loads contacts and addresses from the local database if not already loaded. */ - public async hydrateCwData() { - if (this.cw_Data) return this; + public async hydrateData() { + if (this._contacts.length === 0) { + this._contacts = await prisma.contact.findMany({ + where: { companyId: this.id }, + }); + this._defaultContact = this._contacts.find((c) => c.default) ?? null; + } - const cwCompany = await fetchCwCompanyById(this.cw_CompanyId); - if (!cwCompany) return this; - - const allContactsData = await connectWiseApi.get( - `${cwCompany._info.contacts_href}&pageSize=1000`, - ); - - // Derive default contact from allContacts instead of a separate CW call - const defaultContactId = cwCompany.defaultContact?.id; - const defaultContactData = defaultContactId - ? ((allContactsData.data as any[]).find( - (c: any) => c.id === defaultContactId, - ) ?? null) - : null; - - this.cw_Data = { - company: cwCompany, - defaultContact: defaultContactData, - allContacts: allContactsData.data, - }; + if (this._addresses.length === 0) { + this._addresses = await prisma.companyAddress.findMany({ + where: { companyId: this.id }, + }); + this._defaultAddress = this._addresses.find((a) => a.defaultFlag) ?? null; + } return this; } /** - * Refresh Internal Company Data from ConnectWise + * Refresh from DB * - * This method fetches the latest company data from ConnectWise and updates - * the internal company information accordingly. - * - * @returns {ThisType} - Updated Controller + * Reloads the company data from the local database. */ - public async refreshFromCW() { - const data = await updateCwInternalCompany(this.cw_CompanyId); + public async refreshFromDb() { + const data = await prisma.company.findUnique({ + where: { id: this.id }, + }); + + if (data) { + this.name = data.name; + this.phone = data.phone; + this.website = data.website; + } - this.name = data?.name || this.name; return this; } - /** - * Fetch ConnectWise Company Data - * - * This method retrieves the latest company data directly from ConnectWise - * using the stored ConnectWise Company ID. - * - * @returns {Company} - */ - public async fetchCwData(): Promise { - const data = await fetchCwCompanyById(this.cw_CompanyId); - return data; - } - /** * Fetch Company Configurations * - * This method retrieves the configurations associated with - * the company from ConnectWise. - * - * @returns {ProcessedConfiguration} + * Fetches configurations directly from ConnectWise. */ public async fetchConfigurations() { - const data = await fetchCompanyConfigurations(this.cw_CompanyId); - return data; + const pageSize = 1000; + const conditions = encodeURIComponent(`company/id=${this.id}`); + const configurations: ConfigurationResponse = []; + + try { + for (let page = 1; ; page++) { + const response = await withCwRetry( + () => + connectWiseApi.get( + `/company/configurations?page=${page}&pageSize=${pageSize}&conditions=${conditions}`, + ), + { label: `company-configurations:${this.id}:page-${page}` }, + ); + + const items = Array.isArray(response.data) ? response.data : []; + configurations.push(...items); + + if (items.length < pageSize) break; + } + + return processConfigurationResponse(configurations); + } catch (error: any) { + const cwStatus = Number(error?.response?.status); + const status = cwStatus >= 400 && cwStatus <= 599 ? cwStatus : 502; + + throw new GenericError({ + message: `Failed to fetch company configurations from ConnectWise`, + name: "ConnectWiseFetchFailed", + cause: + error?.response?.data?.message ?? + error?.response?.statusText ?? + error?.message ?? + "Unknown ConnectWise error", + status, + }); + } } /** * Fetch Company Sites * - * Retrieves all sites for this company from ConnectWise - * and returns them as serialized site objects. + * Retrieves all sites (addresses) for this company from local DB. */ public async fetchSites() { - const sites = await fetchCompanySites(this.cw_CompanyId); - return sites.map(serializeCwSite); + const sites = await prisma.companyAddress.findMany({ + where: { companyId: this.id }, + }); + + return sites.map((site) => ({ + id: site.id, + name: site.name, + addressLine1: site.addressLine1, + addressLine2: site.addressLine2, + city: site.city, + state: site.state, + zip: site.zipCode, + country: site.country, + phone: site.phone, + fax: site.fax, + defaultFlag: site.defaultFlag, + inactiveFlag: site.inactiveFlag, + })); } /** * Fetch Company Site by ID * - * Retrieves a single site by its ConnectWise site ID - * and returns a serialized site object. - * - * @param cwSiteId - The ConnectWise site ID + * Retrieves a single site by its ID from local DB. */ - public async fetchSite(cwSiteId: number) { - const site = await fetchCompanySite(this.cw_CompanyId, cwSiteId); - return serializeCwSite(site); + public async fetchSite(siteId: number) { + const site = await prisma.companyAddress.findFirst({ + where: { id: siteId, companyId: this.id }, + }); + + if (!site) return null; + + return { + id: site.id, + name: site.name, + addressLine1: site.addressLine1, + addressLine2: site.addressLine2, + city: site.city, + state: site.state, + zip: site.zipCode, + country: site.country, + phone: site.phone, + fax: site.fax, + defaultFlag: site.defaultFlag, + inactiveFlag: site.inactiveFlag, + }; + } + + /** + * Get all contacts + */ + public getContacts() { + return this._contacts; + } + + /** + * Get default contact + */ + public getDefaultContact() { + return this._defaultContact; + } + + /** + * Get default address + */ + public getDefaultAddress() { + return this._defaultAddress; } public toJson(opts?: { - includeAddress: boolean; - includePrimaryContact: boolean; + includeAddress?: boolean; + includePrimaryContact?: boolean; includeAllContacts?: boolean; }) { + const cw_Data: Record = {}; + + if (opts?.includeAddress) { + cw_Data.address = this._defaultAddress + ? { + line1: this._defaultAddress.addressLine1, + line2: this._defaultAddress.addressLine2, + city: this._defaultAddress.city, + state: this._defaultAddress.state, + zip: this._defaultAddress.zipCode, + country: this._defaultAddress.country ?? "US", + } + : null; + } + + if (opts?.includePrimaryContact) { + cw_Data.primaryContact = this._defaultContact + ? { + firstName: this._defaultContact.firstName, + lastName: this._defaultContact.lastName, + cwId: this._defaultContact.id, + inactive: !this._defaultContact.active, + title: this._defaultContact.title, + phone: this._defaultContact.phone, + email: this._defaultContact.email, + } + : null; + } + + if (opts?.includeAllContacts) { + cw_Data.allContacts = this._contacts.map((contact) => ({ + firstName: contact.firstName, + lastName: contact.lastName, + cwId: contact.id, + inactive: !contact.active, + title: contact.title, + phone: contact.phone, + email: contact.email, + })); + } + return { - id: this.id, + id: this.uid, name: this.name, - cw_Identifier: this.cw_Identifier, - cw_CompanyId: this.cw_CompanyId, - cw_Data: { - address: !opts?.includeAddress - ? undefined - : { - line1: this.cw_Data?.company.addressLine1, - line2: this.cw_Data?.company.addressLine2 ?? null, - city: this.cw_Data?.company.city, - state: this.cw_Data?.company.state, - zip: this.cw_Data?.company.zip, - country: this.cw_Data?.company.country - ? this.cw_Data.company.country.name - : "United States", - }, - primaryContact: !opts?.includePrimaryContact - ? undefined - : this.cw_Data?.defaultContact - ? { - firstName: this.cw_Data.defaultContact.firstName, - lastName: this.cw_Data.defaultContact.lastName, - cwId: this.cw_Data.defaultContact.id, - inactive: this.cw_Data.defaultContact.inactiveFlag, - title: this.cw_Data.defaultContact.title, - phone: this.cw_Data.defaultContact.defaultPhoneNbr, - email: (() => { - if (!this.cw_Data?.defaultContact?.communicationItems) - return null; - return ( - this.cw_Data.defaultContact.communicationItems.find( - (v) => v.type.name === "Email", - )?.value ?? null - ); - })(), - } - : null, - allContacts: !opts?.includeAllContacts - ? undefined - : this.cw_Data?.allContacts.map((contact) => ({ - firstName: contact.firstName, - lastName: contact.lastName, - cwId: contact.id, - inactive: contact.inactiveFlag, - title: contact.title, - phone: contact.defaultPhoneNbr, - email: (() => { - if (!contact.communicationItems) return null; - return ( - contact.communicationItems.find( - (v) => v.type.name === "Email", - )?.value ?? null - ); - })(), - })), - }, + cw_CompanyId: this.id, + cw_Data, }; } } diff --git a/api/src/controllers/CwMemberController.ts b/api/src/controllers/CwMemberController.ts index 87d1835..7ce5fcf 100644 --- a/api/src/controllers/CwMemberController.ts +++ b/api/src/controllers/CwMemberController.ts @@ -1,5 +1,4 @@ import type { CwMember } from "../../generated/prisma/client"; -import type { CWMember } from "../modules/cw-utils/members/fetchAllMembers"; /** * CW Member Controller @@ -44,24 +43,6 @@ export class CwMemberController { return name || this.identifier; } - /** - * Map CW Member → Prisma create/update payload - * - * Static helper used by both the controller and the refresh sync. - */ - public static mapCwToDb(item: CWMember) { - return { - identifier: item.identifier, - firstName: item.firstName ?? "", - lastName: item.lastName ?? "", - officeEmail: item.officeEmail ?? null, - inactiveFlag: item.inactiveFlag ?? false, - cwLastUpdated: item._info?.lastUpdated - ? new Date(item._info.lastUpdated) - : new Date(), - }; - } - /** * To JSON * diff --git a/api/src/controllers/ForecastProductController.ts b/api/src/controllers/ForecastProductController.ts index 25291d8..301dcfc 100644 --- a/api/src/controllers/ForecastProductController.ts +++ b/api/src/controllers/ForecastProductController.ts @@ -8,6 +8,9 @@ import { CWForecastItem } from "../modules/cw-utils/opportunities/opportunity.ty * locally — all data is sourced directly from the ConnectWise API. */ export class ForecastProductController { + private static readonly PROCUREMENT_NOTES_FIELD_ID = 29; + private static readonly PRODUCT_NARRATIVE_FIELD_ID = 46; + public readonly cwForecastId: number; public forecastDescription: string; @@ -24,6 +27,8 @@ export class ForecastProductController { public productDescription: string; public customerDescription: string | null; + public description: string | null; + public procurementNotes: string | null; public productNarrative: string | null; public productClass: string; public forecastType: string; @@ -49,6 +54,11 @@ export class ForecastProductController { public cwLastUpdated: Date | null; public cwUpdatedBy: string | null; + // Pricing data (from local ProductData) + public unitPrice: number; + public listPrice: number; + public discount: number; + // Cancellation data (from procurement products endpoint) public cancelledFlag: boolean; public quantityCancelled: number; @@ -77,8 +87,23 @@ export class ForecastProductController { this.productDescription = data.productDescription; this.customerDescription = data.customerDescription ?? null; + this.description = null; + this.procurementNotes = + data.procurementNotes ?? + data.customFields + ?.find( + (f) => f.id === ForecastProductController.PROCUREMENT_NOTES_FIELD_ID + ) + ?.value?.toString() ?? + null; this.productNarrative = - data.customFields?.find((f) => f.id === 46)?.value?.toString() ?? null; + data.productNarrative ?? + data.customFields + ?.find( + (f) => f.id === ForecastProductController.PRODUCT_NARRATIVE_FIELD_ID + ) + ?.value?.toString() ?? + null; this.productClass = data.productClass; this.forecastType = data.forecastType; @@ -105,6 +130,11 @@ export class ForecastProductController { : null; this.cwUpdatedBy = data._info?.updatedBy ?? null; + // Pricing defaults — enriched later via applyPricingData() + this.unitPrice = 0; + this.listPrice = 0; + this.discount = 0; + // Cancellation defaults — enriched later via applyCancellationData() this.cancelledFlag = false; this.quantityCancelled = 0; @@ -133,8 +163,19 @@ export class ForecastProductController { public applyProcurementCustomFields(data: { customFields?: Array<{ id: number; value?: unknown }>; }): void { + const notes = data.customFields + ?.find( + (f) => f.id === ForecastProductController.PROCUREMENT_NOTES_FIELD_ID + ) + ?.value?.toString(); + if (notes) { + this.procurementNotes = notes; + } + const narrative = data.customFields - ?.find((f) => f.id === 46) + ?.find( + (f) => f.id === ForecastProductController.PRODUCT_NARRATIVE_FIELD_ID + ) ?.value?.toString(); if (narrative) { this.productNarrative = narrative; @@ -257,6 +298,7 @@ export class ForecastProductController { : null, productDescription: this.productDescription, customerDescription: this.customerDescription, + procurementNotes: this.procurementNotes, productNarrative: this.productNarrative, productClass: this.productClass, forecastType: this.forecastType, @@ -281,6 +323,25 @@ export class ForecastProductController { cwUpdatedBy: this.cwUpdatedBy, onHand: this.onHand, inStock: this.inStock, + unitPrice: this.unitPrice, + listPrice: this.listPrice, + discount: this.discount, }; } + + /** + * Apply Pricing Data + * + * Enriches this forecast product with pricing data from the local + * ProductData table. + */ + public applyPricingData(data: { + unitPrice?: number; + listPrice?: number; + discount?: number; + }): void { + this.unitPrice = data.unitPrice ?? 0; + this.listPrice = data.listPrice ?? 0; + this.discount = data.discount ?? 0; + } } diff --git a/api/src/controllers/GeneratedQuoteController.ts b/api/src/controllers/GeneratedQuoteController.ts index 1b44842..1a46c17 100644 --- a/api/src/controllers/GeneratedQuoteController.ts +++ b/api/src/controllers/GeneratedQuoteController.ts @@ -33,7 +33,7 @@ export class GeneratedQuoteController { data: GeneratedQuotes & { opportunity?: Opportunity | null; createdBy?: (User & { roles: Role[] }) | null; - }, + } ) { this.id = data.id; @@ -67,7 +67,7 @@ export class GeneratedQuoteController { if (this._opportunity) return this._opportunity; const opportunity = await prisma.opportunity.findFirst({ - where: { id: this.opportunityId }, + where: { uid: this.opportunityId }, }); if (!opportunity) return null; @@ -114,8 +114,8 @@ export class GeneratedQuoteController { quoteFile: !opts?.includeFile ? undefined : opts?.encodeFileAsBase64 - ? Buffer.from(this.quoteFile).toString("base64") - : this.quoteFile, + ? Buffer.from(this.quoteFile).toString("base64") + : this.quoteFile, opportunity: opts?.includeOpportunity && this._opportunity ? this._opportunity.toJson() diff --git a/api/src/controllers/OpportunityController.ts b/api/src/controllers/OpportunityController.ts index c247031..1f53c86 100644 --- a/api/src/controllers/OpportunityController.ts +++ b/api/src/controllers/OpportunityController.ts @@ -1,16 +1,17 @@ -import { Company, Opportunity } from "../../generated/prisma/client"; +import { + Company, + Opportunity, + OpportunityInterest, + Role, + User, +} from "../../generated/prisma/client"; import { prisma } from "../constants"; import { CompanyController } from "./CompanyController"; import { ActivityController } from "./ActivityController"; -import { fetchOpportunity } from "../modules/cw-utils/opportunities/fetchOpportunity"; import { opportunityCw } from "../modules/cw-utils/opportunities/opportunities"; -import { activityCw } from "../modules/cw-utils/activities/activities"; -import { - fetchCompanySite, - serializeCwSite, -} from "../modules/cw-utils/sites/companySites"; import { CWCustomField, + CWForecastItem, CWForecastItemCreate, CWOpportunity, CWOpportunityNote, @@ -18,36 +19,63 @@ import { CWProcurementProduct, CWProcurementProductCreate, } from "../modules/cw-utils/opportunities/opportunity.types"; -import { - resolveMember, - resolveMembers, - getMemberCache, -} from "../modules/cw-utils/members/memberCache"; import { ForecastProductController } from "./ForecastProductController"; import GenericError from "../Errors/GenericError"; -import { computeSubResourceCacheTTL } from "../modules/algorithms/computeSubResourceCacheTTL"; -import { computeProductsCacheTTL } from "../modules/algorithms/computeProductsCacheTTL"; import UserController from "./UserController"; -import { - getCachedNotes, - getCachedContacts, - getCachedProducts, - getCachedSite, - fetchAndCacheNotes, - fetchAndCacheContacts, - fetchAndCacheProducts, - fetchAndCacheSite, - invalidateNotesCache, - invalidateProductsCache, -} from "../modules/cache/opportunityCache"; import { generateQuote as generateQuotePdf, type QuoteMetadata, } from "../modules/pdf-utils"; import { generatedQuotes } from "../managers/generatedQuotes"; -import { getExpectedSalesTaxRate } from "../modules/sales-utils/expectedSalesTax"; import { normalizeProbabilityPercent } from "../modules/sales-utils/normalizeProbability"; +// Helper to resolve member info from DB +async function resolveMember(identifier: string | null | undefined) { + if (!identifier) return null; + const member = await prisma.cwMember.findFirst({ + where: { identifier }, + }); + return member + ? { + id: member.id, + identifier: member.identifier, + name: `${member.firstName} ${member.lastName}`.trim(), + cwMemberId: member.cwMemberId, + } + : { id: null, identifier, name: identifier, cwMemberId: null }; +} + +async function resolveMembers(identifiers: string[]) { + const unique = [...new Set(identifiers.filter(Boolean))]; + if (unique.length === 0) return new Map(); + + const members = await prisma.cwMember.findMany({ + where: { identifier: { in: unique } }, + }); + + const map = new Map(); + for (const m of members) { + map.set(m.identifier, { + id: m.id, + identifier: m.identifier, + name: `${m.firstName} ${m.lastName}`.trim(), + cwMemberId: m.cwMemberId, + }); + } + return map; +} + +function mapRatingNameToInterest( + ratingName?: string | null +): OpportunityInterest | null { + const normalized = (ratingName ?? "").trim().toUpperCase(); + if (!normalized) return null; + if (normalized.includes("HOT")) return "HOT"; + if (normalized.includes("WARM") || normalized.includes("MED")) return "WARM"; + if (normalized.includes("COLD") || normalized.includes("COOL")) return "COLD"; + return null; +} + /** * Opportunity Controller * @@ -56,6 +84,8 @@ import { normalizeProbabilityPercent } from "../modules/sales-utils/normalizePro * opportunity data. */ export class OpportunityController { + private static readonly QUOTE_NARRATIVE_FIELD_ID = 35; + public readonly id: string; public readonly cwOpportunityId: number; public name: string; @@ -69,8 +99,7 @@ export class OpportunityController { public statusCwId: number | null; public priorityName: string | null; public priorityCwId: number | null; - public ratingName: string | null; - public ratingCwId: number | null; + public interest: OpportunityInterest | null; public source: string | null; public campaignName: string | null; public campaignCwId: number | null; @@ -110,6 +139,9 @@ export class OpportunityController { public cwLastUpdated: Date | null; public cwDateEntered: Date | null; + public taxCodeDescription: string | null; + public taxCodeRate: number | null; + // Local product display order — array of CW forecast item IDs. // When non-empty, fetchProducts() uses this instead of CW sequenceNumber. public productSequence: number[]; @@ -118,116 +150,179 @@ export class OpportunityController { public updatedAt: Date; private _company: CompanyController | null = null; - private _siteData: ReturnType | null = null; + private _primarySalesRep: UserController | null = null; + private _secondarySalesRep: UserController | null = null; + private _siteData: any | null = null; private _customFields: CWCustomField[] | null = null; private _activities: ActivityController[] | null = null; - /** Compute the sub-resource cache TTL from this opportunity's fields. */ - private _subResourceTTL(): number | null { - return computeSubResourceCacheTTL({ - closedFlag: this.closedFlag, - closedDate: this.closedDate, - expectedCloseDate: this.expectedCloseDate, - lastUpdated: this.cwLastUpdated, - }); - } - - /** Compute the products-specific cache TTL from this opportunity's fields. */ - private _productsTTL(): number | null { - return computeProductsCacheTTL({ - closedFlag: this.closedFlag, - closedDate: this.closedDate, - expectedCloseDate: this.expectedCloseDate, - lastUpdated: this.cwLastUpdated, - statusCwId: this.statusCwId, - }); - } - /** * Resolve primary sales rep info for quote generation. * - * Looks up the primary sales rep in the CW member cache and returns + * Looks up the primary sales rep in the DB and returns * their name and email. Returns undefined if no rep is assigned. */ private async _resolveSalesRep(): Promise< { name: string; email?: string } | undefined > { if (!this.primarySalesRepIdentifier) return undefined; - const cache = await getMemberCache(); - const member = cache.get(this.primarySalesRepIdentifier); - const name = member - ? `${member.firstName} ${member.lastName}`.trim() || - this.primarySalesRepName || - this.primarySalesRepIdentifier - : (this.primarySalesRepName ?? this.primarySalesRepIdentifier); + const user = await prisma.user.findFirst({ + where: { cwIdentifier: this.primarySalesRepIdentifier }, + select: { + firstName: true, + lastName: true, + email: true, + login: true, + }, + }); + const resolvedName = user + ? `${user.firstName ?? ""} ${user.lastName ?? ""}`.trim() || + user.login || + user.email + : null; + const name = + resolvedName ?? + this.primarySalesRepName ?? + this.primarySalesRepIdentifier; return { name, - email: member?.officeEmail ?? undefined, + email: user?.email ?? undefined, }; } constructor( - data: Opportunity & { company?: Company | null }, + data: Opportunity & { + company?: + | (Company & { contacts?: any[]; companyAddresses?: any[] }) + | null; + primarySalesRep?: (User & { roles: Role[] }) | null; + secondarySalesRep?: (User & { roles: Role[] }) | null; + }, opts?: { company?: CompanyController; + primarySalesRep?: UserController; + secondarySalesRep?: UserController; customFields?: CWCustomField[]; activities?: ActivityController[]; - }, + } ) { - this.id = data.id; - this.cwOpportunityId = data.cwOpportunityId; + console.log(data.primarySalesRep); + + // New schema: uid is the internal PK (string), id is the CW opportunity ID (Int) + this.id = data.uid; + this.cwOpportunityId = data.id; this.name = data.name; - this.notes = data.notes; + this.notes = data.notes ?? null; - this.typeName = data.typeName; - this.typeCwId = data.typeCwId; - this.stageName = data.stageName; - this.stageCwId = data.stageCwId; - this.statusName = data.statusName; - this.statusCwId = data.statusCwId; - this.priorityName = data.priorityName; - this.priorityCwId = data.priorityCwId; - this.ratingName = data.ratingName; - this.ratingCwId = data.ratingCwId; - this.source = data.source; - this.campaignName = data.campaignName; - this.campaignCwId = data.campaignCwId; + // Type from included relation (typeId is the CW type Int ID) + const typeRel = (data as any).type as + | { id: number; name: string } + | null + | undefined; + this.typeName = typeRel?.name ?? null; + this.typeCwId = data.typeId ?? null; - this.primarySalesRepName = data.primarySalesRepName; - this.primarySalesRepIdentifier = data.primarySalesRepIdentifier; - this.primarySalesRepCwId = data.primarySalesRepCwId; - this.secondarySalesRepName = data.secondarySalesRepName; - this.secondarySalesRepIdentifier = data.secondarySalesRepIdentifier; - this.secondarySalesRepCwId = data.secondarySalesRepCwId; + // Stage from included relation + const stageRel = (data as any).stage as + | { id: number; name: string } + | null + | undefined; + this.stageName = stageRel?.name ?? null; + this.stageCwId = stageRel?.id ?? null; - this.companyCwId = data.companyCwId; - this.companyName = data.companyName; - this.contactCwId = data.contactCwId; - this.contactName = data.contactName; - this.siteCwId = data.siteCwId; - this.siteName = data.siteName; - this.customerPO = data.customerPO; + // Status from included relation + const statusRel = (data as any).status as + | { id: number; name: string } + | null + | undefined; + this.statusName = statusRel?.name ?? null; + this.statusCwId = data.statusId ?? null; - this.totalSalesTax = data.totalSalesTax; - this.probability = data.probability; + // Priority and campaign are not stored in new schema + this.priorityName = null; + this.priorityCwId = null; + this.interest = data.interest ?? null; + this.campaignName = null; + this.campaignCwId = null; - this.locationName = data.locationName; - this.locationCwId = data.locationCwId; - this.departmentName = data.departmentName; - this.departmentCwId = data.departmentCwId; + this.source = data.source ?? null; - this.expectedCloseDate = data.expectedCloseDate; - this.pipelineChangeDate = data.pipelineChangeDate; - this.dateBecameLead = data.dateBecameLead; - this.closedDate = data.closedDate; - this.closedFlag = data.closedFlag; - this.closedByName = data.closedByName; - this.closedByCwId = data.closedByCwId; + const primarySalesRepFromData = (data as any).primarySalesRep as + | (User & { roles: Role[] }) + | null + | undefined; + const secondarySalesRepFromData = (data as any).secondarySalesRep as + | (User & { roles: Role[] }) + | null + | undefined; - this.companyId = data.companyId; - this.cwLastUpdated = data.cwLastUpdated; - this.cwDateEntered = data.cwDateEntered ?? null; - this.productSequence = data.productSequence; + this._primarySalesRep = + opts?.primarySalesRep ?? + (primarySalesRepFromData + ? new UserController(primarySalesRepFromData) + : null); + this._secondarySalesRep = + opts?.secondarySalesRep ?? + (secondarySalesRepFromData + ? new UserController(secondarySalesRepFromData) + : null); + + // Sales reps — identifier strings are persisted; names are enriched from included user relations when available. + this.primarySalesRepIdentifier = data.primarySalesRepId ?? null; + this.primarySalesRepName = this._primarySalesRep?.name ?? null; + this.primarySalesRepCwId = null; + this.secondarySalesRepIdentifier = data.secondarySalesRepId ?? null; + this.secondarySalesRepName = this._secondarySalesRep?.name ?? null; + this.secondarySalesRepCwId = null; + + // Company (companyId is the CW company Int ID = Company.id) + this.companyCwId = data.companyId ?? null; + this.companyName = data.company?.name ?? null; + + // Contact + this.contactCwId = data.contactId ?? null; + const contactRel = (data as any).contact as + | { firstName: string; lastName: string } + | null + | undefined; + this.contactName = contactRel + ? `${contactRel.firstName} ${contactRel.lastName}`.trim() + : null; + + // Site + this.siteCwId = data.siteId ?? null; + this.siteName = (data as any).site?.name ?? null; + + this.customerPO = data.customerPO ?? null; + this.totalSalesTax = 0; // not stored in new schema + this.probability = data.probability ?? 0; + + // Location / department from included relations + this.locationCwId = data.locationId ?? null; + this.locationName = (data as any).location?.name ?? null; + this.departmentCwId = data.departmentId ?? null; + this.departmentName = (data as any).department?.name ?? null; + + this.expectedCloseDate = data.expectedCloseDate ?? null; + this.pipelineChangeDate = data.pipelineChangeDate ?? null; + this.dateBecameLead = data.dateBecameLead ?? null; + this.closedDate = data.closedDate ?? null; + this.closedFlag = data.closedFlag ?? false; + this.closedByName = data.closedById ?? null; + this.closedByCwId = null; // not stored in new schema + + // companyId stored as uid string for CompanyController lookups + this.companyId = data.company?.uid ?? null; + this.cwLastUpdated = data.updatedAt ?? null; + this.cwDateEntered = data.createdAt ?? null; + this.productSequence = data.productSequence ?? []; + + const taxCodeRel = (data as any).taxCode as + | { description: string | null; rate: number | null } + | null + | undefined; + this.taxCodeDescription = taxCodeRel?.description ?? null; + this.taxCodeRate = taxCodeRel?.rate ?? null; this.createdAt = data.createdAt; this.updatedAt = data.updatedAt; @@ -248,8 +343,72 @@ export class OpportunityController { */ private async _hydrateCustomFields(): Promise { if (this._customFields !== null) return; - const cwData = await fetchOpportunity(this.cwOpportunityId); - this._customFields = cwData.customFields ?? []; + const record = await opportunityCw.fetch(this.cwOpportunityId); + this._customFields = record.customFields ?? []; + } + + /** + * Fetch Quote Narrative + */ + public async fetchQuoteNarrative(): Promise { + await this._hydrateCustomFields(); + return ( + this._customFields + ?.find( + (field) => field.id === OpportunityController.QUOTE_NARRATIVE_FIELD_ID + ) + ?.value?.toString() ?? null + ); + } + + /** + * Update Quote Narrative + */ + public async updateQuoteNarrative( + quoteNarrative: string | null + ): Promise { + await this._hydrateCustomFields(); + + const nextValue = quoteNarrative ?? ""; + const existingFields = [...(this._customFields ?? [])]; + const existingIndex = existingFields.findIndex( + (field) => field.id === OpportunityController.QUOTE_NARRATIVE_FIELD_ID + ); + + if (existingIndex >= 0) { + const existingField = existingFields[existingIndex] as CWCustomField; + existingFields[existingIndex] = { + ...existingField, + value: nextValue, + }; + } else { + existingFields.push({ + id: OpportunityController.QUOTE_NARRATIVE_FIELD_ID, + caption: "Quote Narrative", + type: "Text", + entryMethod: "EntryField", + numberOfDecimals: 0, + value: nextValue, + connectWiseId: "", + rowNum: 0, + userDefinedFieldRecId: OpportunityController.QUOTE_NARRATIVE_FIELD_ID, + podId: "", + }); + } + + const updated = await opportunityCw.update(this.cwOpportunityId, { + customFields: existingFields, + } as CWOpportunityUpdate); + + this._customFields = updated.customFields ?? existingFields; + + return ( + this._customFields + ?.find( + (field) => field.id === OpportunityController.QUOTE_NARRATIVE_FIELD_ID + ) + ?.value?.toString() ?? null + ); } /** @@ -262,19 +421,20 @@ export class OpportunityController { */ public async fetchCompany(): Promise { if (this._company) { - await this._company.hydrateCwData(); + await this._company.hydrateData(); return this._company; } if (!this.companyId) return null; - const companyData = await prisma.company.findUnique({ - where: { id: this.companyId }, + const companyData = await prisma.company.findFirst({ + where: { uid: this.companyId }, + include: { contacts: true, companyAddresses: true }, }); if (!companyData) return null; this._company = new CompanyController(companyData); - await this._company.hydrateCwData(); + await this._company.hydrateData(); return this._company; } @@ -285,16 +445,40 @@ export class OpportunityController { * the local database record and controller state. */ public async refreshFromCW(): Promise { - const cwData = await fetchOpportunity(this.cwOpportunityId); - const mapped = OpportunityController.mapCwToDb(cwData); - - const updated = await prisma.opportunity.update({ - where: { id: this.id }, - data: mapped, - include: { company: true }, + const record = await prisma.opportunity.findFirst({ + where: { uid: this.id }, + include: { + company: { include: { contacts: true, companyAddresses: true } }, + contact: true, + type: true, + status: true, + site: true, + primarySalesRep: { include: { roles: true } }, + secondarySalesRep: { include: { roles: true } }, + stage: true, + taxCode: true, + }, }); - return new OpportunityController(updated); + if (!record) { + throw new GenericError({ + status: 404, + name: "OpportunityNotFound", + message: "Opportunity not found", + }); + } + + return new OpportunityController(record, { + company: record.company + ? new CompanyController(record.company) + : undefined, + primarySalesRep: record.primarySalesRep + ? new UserController(record.primarySalesRep) + : undefined, + secondarySalesRep: record.secondarySalesRep + ? new UserController(record.secondarySalesRep) + : undefined, + }); } /** @@ -307,18 +491,46 @@ export class OpportunityController { * @returns A fresh OpportunityController with the updated data */ public async updateOpportunity( - data: CWOpportunityUpdate, + data: CWOpportunityUpdate & { interest?: OpportunityInterest | null } ): Promise { - const cwData = await opportunityCw.update(this.cwOpportunityId, data); - const mapped = OpportunityController.mapCwToDb(cwData); + const { interest, ...cwPatch } = data; + const hasCwPatch = Object.keys(cwPatch).length > 0; + const cwMapped = hasCwPatch + ? OpportunityController.mapCwToDb( + await opportunityCw.update(this.cwOpportunityId, cwPatch) + ) + : {}; + + const mapped = + interest !== undefined ? { ...cwMapped, interest } : cwMapped; const updated = await prisma.opportunity.update({ - where: { id: this.id }, + where: { uid: this.id }, data: mapped, - include: { company: true }, + include: { + company: { include: { contacts: true, companyAddresses: true } }, + contact: true, + type: true, + status: true, + site: true, + primarySalesRep: { include: { roles: true } }, + secondarySalesRep: { include: { roles: true } }, + stage: true, + taxCode: true, + }, }); - return new OpportunityController(updated); + return new OpportunityController(updated, { + company: updated.company + ? new CompanyController(updated.company) + : undefined, + primarySalesRep: updated.primarySalesRep + ? new UserController(updated.primarySalesRep) + : undefined, + secondarySalesRep: updated.secondarySalesRep + ? new UserController(updated.secondarySalesRep) + : undefined, + }); } /** @@ -327,7 +539,12 @@ export class OpportunityController { * Returns the raw ConnectWise opportunity object without updating the DB. */ public async fetchCwData(): Promise { - return fetchOpportunity(this.cwOpportunityId); + throw new GenericError({ + status: 501, + name: "NotSupported", + message: + "Direct CW data fetch is no longer supported — data comes from local DB", + }); } /** @@ -340,42 +557,31 @@ export class OpportunityController { name: item.name, notes: item.notes ?? null, - typeName: item.type?.name ?? null, - typeCwId: item.type?.id ?? null, - stageName: item.stage?.name ?? null, - stageCwId: item.stage?.id ?? null, - statusName: item.status?.name ?? null, - statusCwId: item.status?.id ?? null, - priorityName: item.priority?.name ?? null, - priorityCwId: item.priority?.id ?? null, - ratingName: item.rating?.name ?? null, - ratingCwId: item.rating?.id ?? null, + // typeId references OpportunityType.id (the CW type Int ID) + typeId: item.type?.id, + stageId: item.stage?.id ?? null, + + // statusId references OpportunityStatus.id (the CW status Int ID) + statusId: item.status?.id ?? null, + + interest: mapRatingNameToInterest(item.rating?.name), + source: item.source ?? null, - campaignName: item.campaign?.name ?? null, - campaignCwId: item.campaign?.id ?? null, - primarySalesRepName: item.primarySalesRep?.name ?? null, - primarySalesRepIdentifier: item.primarySalesRep?.identifier ?? null, - primarySalesRepCwId: item.primarySalesRep?.id ?? null, - secondarySalesRepName: item.secondarySalesRep?.name ?? null, - secondarySalesRepIdentifier: item.secondarySalesRep?.identifier ?? null, - secondarySalesRepCwId: item.secondarySalesRep?.id ?? null, + // Sales reps stored as identifier strings (FK to User.cwIdentifier) + primarySalesRepId: item.primarySalesRep?.identifier ?? null, + secondarySalesRepId: item.secondarySalesRep?.identifier ?? null, - companyCwId: item.company?.id ?? null, - companyName: item.company?.name ?? null, - contactCwId: item.contact?.id ?? null, - contactName: item.contact?.name ?? null, - siteCwId: item.site?.id ?? null, - siteName: item.site?.name ?? null, + // Relation IDs — CW IDs match the local DB Int IDs + companyId: item.company?.id ?? null, + contactId: item.contact?.id ?? null, + siteId: item.site?.id ?? null, customerPO: item.customerPO ?? null, - totalSalesTax: item.totalSalesTax ?? 0, probability: normalizeProbabilityPercent(item.probability?.name), - locationName: item.location?.name ?? null, - locationCwId: item.location?.id ?? null, - departmentName: item.department?.name ?? null, - departmentCwId: item.department?.id ?? null, + locationId: item.location?.id ?? null, + departmentId: item.department?.id ?? null, expectedCloseDate: item.expectedCloseDate ? new Date(item.expectedCloseDate) @@ -388,25 +594,18 @@ export class OpportunityController { : null, closedDate: item.closedDate ? new Date(item.closedDate) : null, closedFlag: item.closedFlag ?? false, - closedByName: item.closedBy?.name ?? null, - closedByCwId: item.closedBy?.id ?? null, + closedById: item.closedBy?.identifier ?? item.closedBy?.name ?? null, - cwLastUpdated: item._info?.lastUpdated - ? new Date(item._info.lastUpdated) - : new Date(), - cwDateEntered: item._info?.dateEntered - ? new Date(item._info.dateEntered) - : null, + updatedBy: item._info?.updatedBy ?? "", + eneteredBy: item._info?.enteredBy ?? "", }; } /** * Fetch Site * - * Fetches the full site details (address, phone, flags) from ConnectWise - * for the site associated with this opportunity. - * Checks the Redis cache first (30-min TTL); on miss, calls CW and caches. - * Requires both companyCwId and siteCwId to be set. + * Fetches the site details from the local database. + * Uses the CompanyAddress table. * * @returns Serialized site object or null */ @@ -414,53 +613,70 @@ export class OpportunityController { if (this._siteData) return this._siteData; if (!this.companyCwId || !this.siteCwId) return null; - // Try cache first - const cached = await getCachedSite(this.companyCwId, this.siteCwId); - if (cached) { - this._siteData = serializeCwSite(cached); - return this._siteData; - } + const site = await prisma.companyAddress.findFirst({ + where: { id: this.siteCwId, companyId: this.companyCwId }, + }); - // Cache miss — fetch from CW and cache - const cwSite = await fetchAndCacheSite(this.companyCwId, this.siteCwId); - if (!cwSite) return null; + if (!site) return null; - this._siteData = serializeCwSite(cwSite); + this._siteData = { + id: site.id, + name: site.name, + address: { + line1: site.addressLine1, + line2: site.addressLine2, + city: site.city, + state: site.state, + zip: site.zipCode, + country: site.country, + }, + phone: site.phone, + fax: site.fax, + defaultFlag: site.defaultFlag, + inactiveFlag: site.inactiveFlag, + }; return this._siteData; } /** * Fetch Contacts * - * Fetches contacts associated with this opportunity. Checks the Redis - * cache first; on miss, calls ConnectWise and caches the raw response. - * - * @param opts.fresh - Bypass cache and fetch directly from CW. + * Returns the primary contact for this opportunity from the local database. + * TODO: Expand when OpportunityContact junction table is added. */ public async fetchContacts(opts?: { fresh?: boolean }) { - const ttl = this._subResourceTTL(); + // Currently only the primary contact is stored on the opportunity + if (!this.contactCwId) return []; - // Try cache first (unless forced fresh) - if (!opts?.fresh && ttl !== null) { - const cached = await getCachedContacts(this.cwOpportunityId); - if (cached) return this._serializeContacts(cached); - } + const contact = await prisma.contact.findFirst({ + where: { id: this.contactCwId }, + include: { company: true }, + }); - // Fetch from CW (fetchAndCache* handles 404 internally) - try { - const contacts = - ttl !== null - ? await fetchAndCacheContacts(this.cwOpportunityId, ttl) - : await opportunityCw.fetchContacts(this.cwOpportunityId); + if (!contact) return []; - return this._serializeContacts(contacts); - } catch (err: any) { - if (err?.isAxiosError && err?.response?.status === 404) return []; - throw err; - } + return [ + { + id: contact.id, + contact: { + id: contact.id, + name: `${contact.firstName} ${contact.lastName}`.trim(), + }, + company: contact.company + ? { + id: contact.company.id, + identifier: null, + name: contact.company.name, + } + : null, + role: null, + notes: null, + referralFlag: false, + }, + ]; } - /** Serialize raw CW contact data into the API response shape. */ + /** Serialize raw contact data into the API response shape. */ private _serializeContacts(contacts: any[]) { return contacts.map((ct: any) => ({ id: ct.id, @@ -481,37 +697,28 @@ export class OpportunityController { /** * Fetch Notes * - * Fetches notes associated with this opportunity. Checks the Redis - * cache first; on miss, calls ConnectWise and caches the raw response. - * - * @param opts.fresh - Bypass cache and fetch directly from CW. + * Returns notes for this opportunity. + * Currently returns the single notes field; TODO: expand when OpportunityNote table is added. */ public async fetchNotes(opts?: { fresh?: boolean }) { - const ttl = this._subResourceTTL(); + // The Opportunity model has a single 'notes' field, not a collection + // Return it as a single-item array for now + if (!this.notes) return []; - // Try cache first (unless forced fresh) - if (!opts?.fresh && ttl !== null) { - const cached = await getCachedNotes(this.cwOpportunityId); - if (cached) return this._serializeNotes(cached); - } - - // Fetch from CW (fetchAndCache* handles 404 internally) - try { - const notes = - ttl !== null - ? await fetchAndCacheNotes(this.cwOpportunityId, ttl) - : await opportunityCw.fetchNotes(this.cwOpportunityId); - - return this._serializeNotes(notes); - } catch (err: any) { - if (err?.isAxiosError && err?.response?.status === 404) return []; - throw err; - } + return [ + { + id: 1, + text: this.notes, + type: null, + flagged: false, + dateEntered: this.cwLastUpdated, + enteredBy: null, + }, + ]; } - /** Serialize raw CW note data into the API response shape. */ + /** Serialize raw note data into the API response shape. */ private async _serializeNotes(notes: any[]) { - // Batch-resolve all member identifiers in a single DB query const identifiers = notes .map((n: any) => n.enteredBy as string) .filter(Boolean); @@ -535,82 +742,168 @@ export class OpportunityController { /** * Fetch Single Note * - * Fetches a single note by its ID from ConnectWise. - * - * @param noteId - The CW note ID + * Fetches a single note by its ID. + * TODO: Implement when OpportunityNote table is added. */ public async fetchNote(noteId: number) { - const note = await opportunityCw.fetchNote(this.cwOpportunityId, noteId); - - return { - id: note.id, - text: note.text, - type: note.type ? { id: note.type.id, name: note.type.name } : null, - flagged: note.flagged, - enteredBy: await resolveMember(note.enteredBy), - }; + // Currently not supported - notes are stored as a single text field + throw new GenericError({ + message: + "Individual note fetch not supported - notes are synced via dalpuri", + name: "NotSupported", + status: 501, + }); } /** * Fetch Activities * - * Fetches activities associated with this opportunity from ConnectWise - * and returns an array of ActivityController instances. - * Results are cached after the first call. + * Returns activities for this opportunity. + * TODO: Implement when Activity table is synced via dalpuri. */ public async fetchActivities(): Promise { if (this._activities) return this._activities; - const collection = await activityCw.fetchByOpportunity( - this.cwOpportunityId, - ); - this._activities = collection.map((item) => new ActivityController(item)); + // Activities are not yet synced to local DB - return empty for now + this._activities = []; return this._activities; } /** * Fetch Products * - * Fetches products (forecast/revenue items) for this opportunity. - * Checks the Redis cache first; on miss, calls ConnectWise and - * caches the raw response using the products-specific TTL algorithm. + * Fetches products (forecast/revenue items) for this opportunity from + * the local ProductData table synced via dalpuri. * - * @param opts.fresh - Bypass cache and fetch directly from CW. + * @param opts.fresh - Ignored (kept for API compatibility) */ public async fetchProducts(opts?: { fresh?: boolean; }): Promise { - const ttl = this._productsTTL(); + // Intentionally ignored; retained for API compatibility with legacy callers. + void opts; - let forecast: any; - let procProducts: any[]; + const rows = await prisma.productData.findMany({ + where: { opportunityId: this.cwOpportunityId }, + include: { + catalogItem: { + select: { + id: true, + identifier: true, + name: true, + customerDescription: true, + onHand: true, + classId: true, + }, + }, + }, + }); - // Try cache first (unless forced fresh) - if (!opts?.fresh && ttl !== null) { - const cached = await getCachedProducts(this.cwOpportunityId); - if (cached) { - forecast = cached.forecast; - procProducts = cached.procProducts; - } else { - // Cache miss — fetch from CW and cache - const blob = await fetchAndCacheProducts(this.cwOpportunityId, ttl); - forecast = blob.forecast; - procProducts = blob.procProducts; - } + let ordered = rows; + if (this.productSequence.length > 0) { + const byId = new Map(rows.map((row) => [row.id, row])); + const sequenced = this.productSequence + .map((id) => byId.get(id)) + .filter((row): row is NonNullable => row !== undefined); + const sequencedIds = new Set(this.productSequence); + const unsequenced = rows + .filter((row) => !sequencedIds.has(row.id)) + .sort( + (a, b) => + (a.sequenceNumber ?? Number.MAX_SAFE_INTEGER) - + (b.sequenceNumber ?? Number.MAX_SAFE_INTEGER) + ); + ordered = [...sequenced, ...unsequenced]; } else { - // No caching (won/lost/pending or forced fresh) — fetch directly - try { - [forecast, procProducts] = await Promise.all([ - opportunityCw.fetchProducts(this.cwOpportunityId), - opportunityCw.fetchProcurementProducts(this.cwOpportunityId), - ]); - } catch (err: any) { - if (err?.isAxiosError && err?.response?.status === 404) return []; - throw err; - } + ordered = [...rows].sort( + (a, b) => + (a.sequenceNumber ?? Number.MAX_SAFE_INTEGER) - + (b.sequenceNumber ?? Number.MAX_SAFE_INTEGER) + ); } - return this._buildProductControllers(forecast, procProducts); + return ordered.map((row) => { + const quantity = Math.max(0, row.qty ?? 0); + const revenue = Number(((row.unitPrice ?? 0) * quantity).toFixed(2)); + const cost = Number(((row.unitCost ?? 0) * quantity).toFixed(2)); + const margin = Number((revenue - cost).toFixed(2)); + const percentage = + revenue > 0 ? Number(((margin / revenue) * 100).toFixed(2)) : 0; + + const cwForecastLike: CWForecastItem = { + id: row.id, + forecastDescription: + row.shortDescription?.trim() || + row.catalogItem?.name || + row.catalogItem?.identifier || + `Product ${row.id}`, + opportunity: { + id: this.cwOpportunityId, + name: this.name, + }, + quantity, + status: { + id: 0, + name: row.cancelFlag ? "Cancelled" : "Active", + }, + catalogItem: { + id: row.catalogItem.id, + identifier: row.catalogItem.identifier ?? row.catalogItem.name, + }, + productDescription: + row.shortDescription?.trim() || + row.catalogItem?.name || + row.catalogItem?.identifier || + "Line Item", + customerDescription: row.catalogItem.customerDescription ?? undefined, + productClass: row.catalogItem.classId === "S" ? "Service" : (row.recurringFlag ? "Service" : "Product"), + revenue, + cost, + margin, + percentage, + includeFlag: !row.cancelFlag, + quoteWerksQuantity: 0, + forecastType: row.recurringFlag ? "Recurring" : "Product", + linkFlag: false, + recurringRevenue: row.recurringRevenue ?? 0, + recurringCost: row.recurringCost ?? 0, + cycles: 0, + recurringFlag: row.recurringFlag, + sequenceNumber: row.sequenceNumber ?? 0, + subNumber: 0, + taxableFlag: row.taxableFlag, + procurementNotes: row.procurementNotes ?? null, + productNarrative: row.productNarrative ?? null, + customFields: undefined, + _info: { + lastUpdated: row.updatedAt.toISOString(), + updatedBy: row.updatedById ?? "", + }, + }; + + const ctrl = new ForecastProductController(cwForecastLike); + + ctrl.applyCancellationData({ + cancelledFlag: row.cancelFlag, + quantityCancelled: row.cancelQty ?? 0, + cancelledReason: row.cancelReason ?? undefined, + cancelledDate: row.cancelledAt?.toISOString(), + }); + + if (typeof row.catalogItem.onHand === "number") { + ctrl.applyInventoryData({ onHand: row.catalogItem.onHand }); + } + + ctrl.applyPricingData({ + unitPrice: row.unitPrice ?? 0, + listPrice: row.listPrice ?? 0, + discount: row.discount ?? 0, + }); + + ctrl.description = row.description ?? null; + + return ctrl; + }); } /** @@ -637,7 +930,7 @@ export class OpportunityController { const products = await this.fetchProducts(); const activeProducts = products.filter( - (item) => item.includeFlag && item.cancellationType !== "full", + (item) => item.includeFlag && item.cancellationType !== "full" ); if (activeProducts.length === 0) { @@ -691,7 +984,8 @@ export class OpportunityController { return { qty: isLabor ? 1 : quantity, - description: item.productDescription || "Line Item", + description: + item.description || item.customerDescription || item.productDescription || "Line Item", unitPrice, narrative: shouldIncludeNarrative ? itemNarrative : undefined, }; @@ -723,13 +1017,12 @@ export class OpportunityController { this.companyName || "Customer"; - const normalizedTaxRate = getExpectedSalesTaxRate( - site?.address ?? companyJson?.cw_Data?.address, - ); + const normalizedTaxRate = this.taxCodeRate ?? 0; + const taxDescription = this.taxCodeDescription ?? "Sales Tax"; const taxLabel = normalizedTaxRate > 0 - ? `Sales Tax (${(normalizedTaxRate * 100).toFixed(2)}%)` - : "Sales Tax"; + ? `Tax (${taxDescription} ${(normalizedTaxRate * 100).toFixed(2)}%)` + : `Tax (${taxDescription})`; await this._hydrateCustomFields(); @@ -769,43 +1062,55 @@ export class OpportunityController { .join(" "); const showCompany = normalise(companyLine) !== normalise(customerName); - return generateQuotePdf( - { - customer: { - name: customerName, - company: showCompany ? companyLine : undefined, - attention, - address: - addressLines.length > 0 ? addressLines : ["Address unavailable"], - }, - contact: { - email: companyJson?.cw_Data?.primaryContact?.email ?? undefined, - phone: companyJson?.cw_Data?.primaryContact?.phone ?? undefined, - }, - salesRep: await this._resolveSalesRep(), - quote: { - quoteNumber: this.cwOpportunityId.toString(), - date: new Date().toLocaleDateString("en-US", { - year: "numeric", - month: "long", - day: "numeric", - }), - description: quoteDescription, - }, - lineItems, - taxableSubtotal: taxableSubTotal, - quoteNarrative, - tax: { - rate: normalizedTaxRate, - label: taxLabel, - }, - isPreview: options.showPreview, - showLineItemPricing: options.lineItemPricing, - metadata: opts?.metadata, + const quotePayload = { + customer: { + name: customerName, + company: showCompany ? companyLine : undefined, + attention, + address: + addressLines.length > 0 ? addressLines : ["Address unavailable"], }, - {}, - options.logoPath, - ); + contact: { + email: companyJson?.cw_Data?.primaryContact?.email ?? undefined, + phone: companyJson?.cw_Data?.primaryContact?.phone ?? undefined, + }, + salesRep: await this._resolveSalesRep(), + quote: { + quoteNumber: this.cwOpportunityId.toString(), + date: new Date().toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + timeZone: "America/New_York", + }), + description: quoteDescription, + }, + lineItems, + taxableSubtotal: taxableSubTotal, + quoteNarrative, + tax: { + rate: normalizedTaxRate, + label: taxLabel, + }, + isPreview: options.showPreview, + showLineItemPricing: options.lineItemPricing, + metadata: opts?.metadata, + }; + + try { + return await generateQuotePdf(quotePayload, {}, options.logoPath); + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + if (message.includes("urlResolver.resolve")) { + console.warn( + "[generateQuote] Logo resolution failed, retrying without logo", + { error: message } + ); + return await generateQuotePdf(quotePayload, {}, ""); + } + + throw err; + } } /** @@ -822,7 +1127,7 @@ export class OpportunityController { includeItemNarratives?: boolean; logoPath?: string; } = {}, - user: UserController, + user: UserController ) { const quoteOptions = { lineItemPricing: opts?.lineItemPricing ?? true, @@ -844,8 +1149,7 @@ export class OpportunityController { await this._hydrateCustomFields(); const quoteNarrativeField = quoteOptions.includeQuoteNarrative - ? (this._customFields?.find((f) => f.id === 35)?.value?.toString() ?? - null) + ? this._customFields?.find((f) => f.id === 35)?.value?.toString() ?? null : null; // Fall back to the customerDescription of a QUO-Narrative product @@ -1012,7 +1316,7 @@ export class OpportunityController { */ private async _buildProductControllers( forecast: any, - procProducts: any[], + procProducts: any[] ): Promise { // Build a map of forecastDetailId → procurement product cancellation data const cancellationMap = new Map>(); @@ -1031,7 +1335,7 @@ export class OpportunityController { // Procurement-only view: only include forecast items that have a // matching procurement record (via forecastDetailId). const forecastItems = (forecast.forecastItems ?? []).filter((fi: any) => - cancellationMap.has(fi.id), + cancellationMap.has(fi.id) ); // Apply local ordering if productSequence is set, otherwise fall back @@ -1051,7 +1355,7 @@ export class OpportunityController { ordered = [...sequenced, ...unsequenced]; } else { ordered = [...forecastItems].sort( - (a: any, b: any) => a.sequenceNumber - b.sequenceNumber, + (a: any, b: any) => a.sequenceNumber - b.sequenceNumber ); } @@ -1064,7 +1368,7 @@ export class OpportunityController { ctrl.applyProcurementCustomFields(procData as any); } return ctrl; - }, + } ); // Enrich with internal inventory data from local CatalogItem DB @@ -1074,12 +1378,10 @@ export class OpportunityController { if (catalogCwIds.length > 0) { const catalogItems = await prisma.catalogItem.findMany({ - where: { cwCatalogId: { in: catalogCwIds } }, - select: { cwCatalogId: true, onHand: true }, + where: { id: { in: catalogCwIds } }, + select: { id: true, onHand: true }, }); - const inventoryMap = new Map( - catalogItems.map((ci) => [ci.cwCatalogId, ci]), - ); + const inventoryMap = new Map(catalogItems.map((ci) => [ci.id, ci])); for (const ctrl of controllers) { const inv = ctrl.catalogItemCwId ? inventoryMap.get(ctrl.catalogItemCwId) @@ -1095,82 +1397,6 @@ export class OpportunityController { // Opportunity Activity / Workflow Methods // --------------------------------------------------------------------------- - /** - * Set Internal Review - * - * The quote is ready to be reviewed before it is ready to be sent. - */ - public async setInternalReview(): Promise { - // TODO: implement - } - - /** - * Set Internal Approved - * - * The quote has been approved and is ready to be sent out. - */ - public async setInternalApproved(): Promise { - // TODO: implement - } - - /** - * Set Quote Sent - * - * The quote has been sent to the customer. - */ - public async setQuoteSent(): Promise { - // TODO: implement - } - - /** - * Set Quote Confirmed - * - * The quote has been received by the customer. - */ - public async setQuoteConfirmed(): Promise { - // TODO: implement - } - - /** - * Set Revision Needed - * - * The quote needs to be revised and is set to stage revision. - */ - public async setRevisionNeeded(): Promise { - // TODO: implement - } - - /** - * Set Finalized - * - * Locks any non-admins from modifying the quote, indicating - * this is the final iteration of the quote. - */ - public async setFinalized(): Promise { - // TODO: implement - } - - /** - * Convert - * - * Converts the quote to a ticket and updates all necessary fields. - */ - public async convert(): Promise { - // TODO: implement - } - - /** - * Add Time - * - * Adds time to an activity on this opportunity. - * - * @param activityId - The CW activity ID to add time to - * @param user - The user identifier adding time - */ - public async addTime(activityId: number, user: string): Promise { - // TODO: implement - } - /** * Update Product * @@ -1181,15 +1407,15 @@ export class OpportunityController { */ public async updateProduct( forecastItemId: number, - data: Record, + data: Record ): Promise { try { const updated = await opportunityCw.updateProduct( this.cwOpportunityId, forecastItemId, - data, + data ); - await invalidateProductsCache(this.cwOpportunityId); + // No cache invalidation needed return new ForecastProductController(updated); } catch (err: any) { console.error( @@ -1203,8 +1429,8 @@ export class OpportunityController { message: err?.message, }, null, - 2, - ), + 2 + ) ); throw err; } @@ -1220,13 +1446,14 @@ export class OpportunityController { * @param orderedIds - Forecast item IDs in the desired display order */ public async resequenceProducts( - orderedIds: number[], + orderedIds: number[] ): Promise { - // Validate all IDs exist in CW - const forecast = await opportunityCw.fetchProducts(this.cwOpportunityId); - const existingIds = new Set( - (forecast.forecastItems ?? []).map((fi) => fi.id), - ); + // Validate all IDs exist in the local ProductData table (the IDs the UI works with) + const existingRows = await prisma.productData.findMany({ + where: { opportunityId: this.cwOpportunityId }, + select: { id: true }, + }); + const existingIds = new Set(existingRows.map((r) => r.id)); for (const id of orderedIds) { if (!existingIds.has(id)) { throw new GenericError({ @@ -1239,13 +1466,13 @@ export class OpportunityController { // Persist the sequence locally await prisma.opportunity.update({ - where: { id: this.id }, + where: { uid: this.id }, data: { productSequence: orderedIds }, }); this.productSequence = orderedIds; // Invalidate cached products since ordering changed - await invalidateProductsCache(this.cwOpportunityId); + // No cache invalidation needed // Return items in the new order return this.fetchProducts(); @@ -1259,12 +1486,12 @@ export class OpportunityController { */ private async appendProductSequenceIds(ids: number[]): Promise { const normalizedIds = ids.filter( - (id): id is number => Number.isInteger(id) && id > 0, + (id): id is number => Number.isInteger(id) && id > 0 ); if (normalizedIds.length === 0) return; const current = await prisma.opportunity.findUnique({ - where: { id: this.id }, + where: { uid: this.id }, select: { productSequence: true }, }); @@ -1279,7 +1506,7 @@ export class OpportunityController { const updatedSequence = [...existing, ...idsToAppend]; await prisma.opportunity.update({ - where: { id: this.id }, + where: { uid: this.id }, data: { productSequence: updatedSequence }, }); @@ -1297,15 +1524,15 @@ export class OpportunityController { * Accepts a single item or an array of items. */ public async addProducts( - data: CWForecastItemCreate | CWForecastItemCreate[], + data: CWForecastItemCreate | CWForecastItemCreate[] ): Promise { try { const created = await opportunityCw.createProducts( this.cwOpportunityId, - data, + data ); await this.appendProductSequenceIds(created.map((item) => item.id)); - await invalidateProductsCache(this.cwOpportunityId); + // No cache invalidation needed return created.map((item) => new ForecastProductController(item)); } catch (err: any) { console.error( @@ -1319,8 +1546,8 @@ export class OpportunityController { message: err?.message, }, null, - 2, - ), + 2 + ) ); throw new GenericError({ status: err?.response?.status ?? 500, @@ -1340,7 +1567,7 @@ export class OpportunityController { * Use this when payloads include procurement-only fields such as customFields. */ public async addProcurementProducts( - data: CWProcurementProductCreate | CWProcurementProductCreate[], + data: CWProcurementProductCreate | CWProcurementProductCreate[] ): Promise { try { const items = Array.isArray(data) ? data : [data]; @@ -1353,9 +1580,9 @@ export class OpportunityController { await this.appendProductSequenceIds( created .map((item) => item.forecastDetailId) - .filter((id): id is number => typeof id === "number"), + .filter((id): id is number => typeof id === "number") ); - await invalidateProductsCache(this.cwOpportunityId); + // No cache invalidation needed return created; } catch (err: any) { console.error( @@ -1369,8 +1596,8 @@ export class OpportunityController { message: err?.message, }, null, - 2, - ), + 2 + ) ); throw new GenericError({ status: err?.response?.status ?? 500, @@ -1398,18 +1625,18 @@ export class OpportunityController { // Remove the deleted item from the local product sequence if (this.productSequence.includes(forecastItemId)) { const updatedSequence = this.productSequence.filter( - (id) => id !== forecastItemId, + (id) => id !== forecastItemId ); await prisma.opportunity.update({ - where: { id: this.id }, + where: { uid: this.id }, data: { productSequence: updatedSequence }, }); this.productSequence = updatedSequence; } - await invalidateProductsCache(this.cwOpportunityId); + // No cache invalidation needed } /** @@ -1419,11 +1646,11 @@ export class OpportunityController { * or null when no procurement record exists. */ public async fetchProcurementProductByForecastItem( - forecastItemId: number, + forecastItemId: number ): Promise { return opportunityCw.fetchProcurementProductByForecastDetail( this.cwOpportunityId, - forecastItemId, + forecastItemId ); } @@ -1435,17 +1662,18 @@ export class OpportunityController { */ public async updateProcurementProductByForecastItem( forecastItemId: number, - data: Record, + data: Record ): Promise { - const linked = - await this.fetchProcurementProductByForecastItem(forecastItemId); + const linked = await this.fetchProcurementProductByForecastItem( + forecastItemId + ); if (!linked?.id) return null; const updated = await opportunityCw.updateProcurementProduct( linked.id, - data, + data ); - await invalidateProductsCache(this.cwOpportunityId); + // No cache invalidation needed return updated; } @@ -1457,10 +1685,11 @@ export class OpportunityController { */ public async setProductCancellation( forecastItemId: number, - opts: { quantityCancelled: number; cancellationReason?: string | null }, + opts: { quantityCancelled: number; cancellationReason?: string | null } ): Promise { - const linked = - await this.fetchProcurementProductByForecastItem(forecastItemId); + const linked = await this.fetchProcurementProductByForecastItem( + forecastItemId + ); if (!linked?.id) { throw new GenericError({ @@ -1479,10 +1708,8 @@ export class OpportunityController { { quantityCancelled, cancelledFlag, - cancelledReason: cancelledFlag - ? (opts.cancellationReason ?? null) - : null, - }, + cancelledReason: cancelledFlag ? opts.cancellationReason ?? null : null, + } ); if (!updated) { @@ -1509,13 +1736,13 @@ export class OpportunityController { public async addNote( note: string, user: string, - opts?: { flagged?: boolean }, + opts?: { flagged?: boolean } ): Promise { const created = await opportunityCw.createNote(this.cwOpportunityId, { text: note, flagged: opts?.flagged ?? false, }); - await invalidateNotesCache(this.cwOpportunityId); + // No cache invalidation needed return created; } @@ -1529,14 +1756,14 @@ export class OpportunityController { */ public async updateNote( noteId: number, - data: { text?: string; flagged?: boolean }, + data: { text?: string; flagged?: boolean } ): Promise { const updated = await opportunityCw.updateNote( this.cwOpportunityId, noteId, - data, + data ); - await invalidateNotesCache(this.cwOpportunityId); + // No cache invalidation needed return updated; } @@ -1549,7 +1776,7 @@ export class OpportunityController { */ public async deleteNote(noteId: number): Promise { await opportunityCw.deleteNote(this.cwOpportunityId, noteId); - await invalidateNotesCache(this.cwOpportunityId); + // No cache invalidation needed } /** @@ -1559,19 +1786,17 @@ export class OpportunityController { */ public toJson(): Record { const siteAddress = this._siteData?.address; - const companyAddress = this._company?.cw_Data?.company + const defaultAddr = this._company?.getDefaultAddress(); + const companyAddress = defaultAddr ? { - line1: this._company.cw_Data.company.addressLine1, - line2: this._company.cw_Data.company.addressLine2, - city: this._company.cw_Data.company.city, - state: this._company.cw_Data.company.state, - zip: this._company.cw_Data.company.zip, - country: this._company.cw_Data.company.country?.name ?? null, + line1: defaultAddr.addressLine1, + line2: defaultAddr.addressLine2, + city: defaultAddr.city, + state: defaultAddr.state, + zip: defaultAddr.zipCode, + country: defaultAddr.country ?? null, } : null; - const expectedSalesTax = getExpectedSalesTaxRate( - siteAddress ?? companyAddress, - ); return { id: this.id, @@ -1588,27 +1813,37 @@ export class OpportunityController { priority: this.priorityCwId ? { id: this.priorityCwId, name: this.priorityName } : null, - rating: this.ratingCwId - ? { id: this.ratingCwId, name: this.ratingName } - : null, + interest: this.interest, + rating: this.interest ? { name: this.interest } : null, source: this.source, campaign: this.campaignCwId ? { id: this.campaignCwId, name: this.campaignName } : null, - primarySalesRep: this.primarySalesRepCwId - ? { - id: this.primarySalesRepCwId, - identifier: this.primarySalesRepIdentifier, - name: this.primarySalesRepName, - } - : null, - secondarySalesRep: this.secondarySalesRepCwId - ? { - id: this.secondarySalesRepCwId, - identifier: this.secondarySalesRepIdentifier, - name: this.secondarySalesRepName, - } - : null, + primarySalesRep: + this.primarySalesRepIdentifier || this._primarySalesRep + ? { + id: this.primarySalesRepCwId, + identifier: this.primarySalesRepIdentifier, + name: + this._primarySalesRep?.name ?? + this.primarySalesRepName ?? + this.primarySalesRepIdentifier, + user: this._primarySalesRep?.toJson({ safeReturn: true }) ?? null, + } + : null, + secondarySalesRep: + this.secondarySalesRepIdentifier || this._secondarySalesRep + ? { + id: this.secondarySalesRepCwId, + identifier: this.secondarySalesRepIdentifier, + name: + this._secondarySalesRep?.name ?? + this.secondarySalesRepName ?? + this.secondarySalesRepIdentifier, + user: + this._secondarySalesRep?.toJson({ safeReturn: true }) ?? null, + } + : null, company: this._company ? this._company.toJson({ includeAllContacts: true, @@ -1616,19 +1851,21 @@ export class OpportunityController { includePrimaryContact: false, }) : this.companyCwId - ? { id: this.companyCwId, name: this.companyName } - : null, + ? { id: this.companyCwId, name: this.companyName } + : null, contact: this.contactCwId ? { id: this.contactCwId, name: this.contactName } : null, site: this._siteData ? this._siteData : this.siteCwId - ? { id: this.siteCwId, name: this.siteName } - : null, + ? { id: this.siteCwId, name: this.siteName } + : null, customerPO: this.customerPO, totalSalesTax: this.totalSalesTax, - expectedSalesTax, + expectedSalesTaxRate: + this.taxCodeRate !== null ? this.taxCodeRate * 100 : null, + taxCodeDescription: this.taxCodeDescription, probability: this.probability, location: this.locationCwId ? { id: this.locationCwId, name: this.locationName } @@ -1641,7 +1878,7 @@ export class OpportunityController { dateBecameLead: this.dateBecameLead, closedDate: this.closedDate, closedFlag: this.closedFlag, - closedBy: this.closedByCwId + closedBy: this.closedByName ? { id: this.closedByCwId, name: this.closedByName } : null, companyId: this.companyId, diff --git a/api/src/controllers/RoleController.ts b/api/src/controllers/RoleController.ts index 01426f4..61a85ae 100644 --- a/api/src/controllers/RoleController.ts +++ b/api/src/controllers/RoleController.ts @@ -89,7 +89,7 @@ export class RoleController { }); throw new PermissionsVerificationError( `Unable to verify permissions for role '${this.title}, it is recommended that you override and rewrite these permissions immediately.`, - (err as Error).message, + (err as Error).message ); } @@ -261,14 +261,14 @@ export class RoleController { title: string; moniker: string; permissions: string[]; - }>, + }> ) { const schema = z .object({ title: z.string().min(1, "Title cannot be empty."), moniker: z.string().min(1, "Moniker cannot be empty."), permissions: z.array( - z.string().min(1, "Permission node cannot be empty"), + z.string().min(1, "Permission node cannot be empty") ), }) .partial() @@ -284,7 +284,7 @@ export class RoleController { if (checkMoniker && checkMoniker.moniker !== this.moniker) throw new RoleError( "Moniker is already taken.", - "Another role with this moniker already exists in the databse.", + "Another role with this moniker already exists in the databse." ); } @@ -337,7 +337,10 @@ export class RoleController { users: opts?.viewUsers ? this._users.map((v) => ({ id: v.id, - name: v.name, + name: + `${v.firstName ?? ""} ${v.lastName ?? ""}`.trim() || + v.login || + v.email, login: v.login, roles: v.roles.map((r: any) => r.id), })) diff --git a/api/src/controllers/ScheduleController.ts b/api/src/controllers/ScheduleController.ts new file mode 100644 index 0000000..b608977 --- /dev/null +++ b/api/src/controllers/ScheduleController.ts @@ -0,0 +1,84 @@ +import { + Schedule, + ScheduleStatus, + ScheduleType, + ScheduleSpan, +} from "../../generated/prisma/client"; + +type ScheduleWithRelations = Schedule & { + status?: ScheduleStatus | null; + type?: ScheduleType | null; + scheduleSpan?: ScheduleSpan | null; +}; + +export class ScheduleController { + public readonly id: number; + public readonly uid: string; + public name: string; + public description: string | null; + + private _data: ScheduleWithRelations; + + constructor(data: ScheduleWithRelations) { + this.id = data.id; + this.uid = data.uid; + this.name = data.name; + this.description = data.description; + this._data = data; + } + + public toJson() { + const d = this._data; + return { + id: d.uid, + cwId: d.id, + memberId: d.memberId, + name: d.name, + description: d.description, + closedFlag: d.closedFlag, + reminderFlag: d.reminderFlag, + allDayFlag: d.allDayFlag, + acknowledgementFlag: d.acknowledgementFlag, + meetingFlag: d.meetingFlag, + recurringFlag: d.recurringFlag, + billableFlag: d.billableFlag, + acknowledgedById: d.acknowledgedById, + acknowledgedAt: d.acknowledgedAt, + startDate: d.startDate, + endDate: d.endDate, + hoursScheduled: d.hoursScheduled, + duration: d.duration, + hoursPerDay: d.hoursPerDay, + reminderMinutes: d.reminderMinutes, + closedById: d.closedById, + closedAt: d.closedAt, + createdById: d.createdById, + updatedById: d.updatedById, + createdAt: d.createdAt, + updatedAt: d.updatedAt, + status: d.status + ? { + id: d.status.id, + uid: d.status.uid, + name: d.status.name, + color: d.status.color, + } + : null, + type: d.type + ? { + id: d.type.id, + uid: d.type.uid, + name: d.type.name, + displayColor: d.type.displayColor, + } + : null, + scheduleSpan: d.scheduleSpan + ? { + id: d.scheduleSpan.id, + scheduleSpanId: d.scheduleSpan.scheduleSpanId, + spanDesc: d.scheduleSpan.spanDesc, + } + : null, + }; + } +} diff --git a/api/src/controllers/UserController.ts b/api/src/controllers/UserController.ts index 045cb5a..bae7c1c 100644 --- a/api/src/controllers/UserController.ts +++ b/api/src/controllers/UserController.ts @@ -1,6 +1,5 @@ import { Collection } from "@discordjs/collection"; -import { Role } from "../../generated/prisma/client"; -import { User } from "../../generated/prisma/browser"; +import { Role, User } from "../../generated/prisma/client"; import { SessionTokensObject } from "./SessionController"; import { sessions } from "../managers/sessions"; import BodyError from "../Errors/BodyError"; @@ -15,11 +14,13 @@ import { permissionsPrivateKey } from "../constants"; export default class UserController { public id: string; - public name: string | null; + public firstName: string | null; + public lastName: string | null; public login: string; public email: string; public image: string | null; public cwIdentifier: string | null; + public cwMemberId: number | null; private _roles: Collection; private _permissions: string | null; @@ -33,13 +34,38 @@ export default class UserController { public createdAt: Date; public updatedAt: Date; + + public get name(): string | null { + const full = [this.firstName, this.lastName] + .filter(Boolean) + .join(" ") + .trim(); + return full.length > 0 ? full : null; + } + + private _splitName(name: string): { + firstName: string | null; + lastName: string | null; + } { + const trimmed = name.trim(); + if (!trimmed) return { firstName: null, lastName: null }; + + const parts = trimmed.split(/\s+/); + const firstName = parts.shift() ?? null; + const lastName = parts.length > 0 ? parts.join(" ") : null; + + return { firstName, lastName }; + } + constructor(userdata: User & { roles: Role[] }) { this.id = userdata.id; - this.name = userdata.name; + this.firstName = userdata.firstName ?? null; + this.lastName = userdata.lastName ?? null; this.login = userdata.login; this.email = userdata.email; this.image = userdata.image; this.cwIdentifier = userdata.cwIdentifier ?? null; + this.cwMemberId = userdata.cwMemberId ?? null; this.updatedAt = userdata.updatedAt; this.createdAt = userdata.createdAt; this._permissions = userdata.permissions ?? null; @@ -62,11 +88,13 @@ export default class UserController { */ private _updateInternalValues(userdata: User) { this.id = userdata.id; - this.name = userdata.name; + this.firstName = userdata.firstName ?? null; + this.lastName = userdata.lastName ?? null; this.login = userdata.login; this.email = userdata.email; this.image = userdata.image; this.cwIdentifier = userdata.cwIdentifier ?? null; + this.cwMemberId = userdata.cwMemberId ?? null; this.updatedAt = userdata.updatedAt; this.createdAt = userdata.createdAt; } @@ -92,17 +120,33 @@ export default class UserController { * @param data - A partial of the user data * @returns {Promise} - The updated user controller */ - public async update(data: Partial>) { - if (Object.keys(data).length == 0) + public async update( + data: Partial> & { + name?: string; + } + ) { + const updateData: Partial> = + {}; + + if (data.image !== undefined) updateData.image = data.image; + if (data.name !== undefined) { + const parsed = this._splitName(data.name); + updateData.firstName = parsed.firstName; + updateData.lastName = parsed.lastName; + } + if (data.firstName !== undefined) updateData.firstName = data.firstName; + if (data.lastName !== undefined) updateData.lastName = data.lastName; + + if (Object.keys(updateData).length == 0) throw new BodyError("Body cannot be empty."); const updatedUser = await prisma.user.update({ where: { id: this.id }, - data, + data: updateData, }); this._updateInternalValues(updatedUser); - events.emit("user:updated", { user: this, updatedValues: data }); + events.emit("user:updated", { user: this, updatedValues: updateData }); return this; } @@ -118,7 +162,7 @@ export default class UserController { */ public async setRoles(roleIdentifiers: string[]): Promise { const resolvedRoles = await Promise.all( - roleIdentifiers.map((identifier) => roles.fetch(identifier)), + roleIdentifiers.map((identifier) => roles.fetch(identifier)) ); const updatedUser = await prisma.user.update({ @@ -242,8 +286,8 @@ export default class UserController { await Promise.all( this._roles.map(async (v) => - collection.set(v.id, await roles.fetch(v.id)), - ), + collection.set(v.id, await roles.fetch(v.id)) + ) ); return collection; @@ -307,11 +351,13 @@ export default class UserController { return { id: this.id, name: this.name, + firstName: this.firstName, + lastName: this.lastName, roles: opts?.safeReturn ? undefined : this._roles.size > 0 - ? this._roles.map((v) => v.moniker) - : undefined, + ? this._roles.map((v) => v.moniker) + : undefined, permissions: opts?.safeReturn ? undefined : (() => { @@ -325,6 +371,7 @@ export default class UserController { login: opts?.safeReturn ? undefined : this.login, email: opts?.safeReturn ? undefined : this.email, cwIdentifier: opts?.safeReturn ? undefined : this.cwIdentifier, + cwMemberId: this.cwMemberId, image: this.image, createdAt: this.createdAt, updatedAt: this.updatedAt, diff --git a/api/src/index.ts b/api/src/index.ts index 418167c..07e2d67 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -1,61 +1,32 @@ -import { refresh } from "./api/auth"; import app from "./api/server"; import { setupSockets } from "./api/sockets"; -import { - COLLECTOR_WS_URL, - connectCollectorSocket, - collectorSocket, - engine, - PORT, - prisma, - unifi, - unifiPassword, - unifiUsername, -} from "./constants"; +import { engine, PORT, prisma } from "./constants"; import { unifiSites } from "./managers/unifiSites"; -import { refreshCompanies } from "./modules/cw-utils/refreshCompanies"; -import { refreshCatalog } from "./modules/cw-utils/procurement/refreshCatalog"; -import { refreshInventory } from "./modules/cw-utils/procurement/refreshInventory"; -import { listenInventoryAdjustments } from "./modules/cw-utils/procurement/listenInventoryAdjustments"; -import { refreshSalesOpportunityMetricsCache } from "./modules/cache/salesOpportunityMetricsCache"; -import { refreshCwIdentifiers } from "./modules/cw-utils/members/refreshCwIdentifiers"; -import { refreshCwMembers } from "./modules/cw-utils/members/refreshCwMembers"; -import { userDefinedFieldsCw } from "./modules/cw-utils/userDefinedFields"; import { events } from "./modules/globalEvents"; import { setupEventDebugger } from "./modules/logging/eventDebugger"; import { signPermissions } from "./modules/permission-utils/signPermissions"; import { RoleController } from "./controllers/RoleController"; -import cuid from "cuid"; +import { initializeWorkerSystem, getBoss } from "./workert"; +import { WorkerQueue } from "./modules/workers/queues"; +import { enqueueIncrementalSync } from "./modules/workers/incremental-sync"; import { startCommsServer } from "./modules/workers/coms"; -import { - enqueueActiveOpportunityRefreshJob, - startOpportunityCacheWorkers, -} from "./workert"; +import cuid from "cuid"; + +const startupArgs = new Set(Bun.argv.slice(2)); +const simpleTerminalMode = + startupArgs.has("-st") || startupArgs.has("--simple-terminal"); // Setup global event debugger in non-production environments -if (Bun.env.NODE_ENV == "development") { +if (Bun.env.NODE_ENV == "development" && !simpleTerminalMode) { setupEventDebugger({ processLabel: "API" }); } -/** Concise error message for interval logs — avoids dumping full Axios error objects. */ -const briefErr = (err: any): string => { - if (err?.isAxiosError) { - const method = (err.config?.method ?? "?").toUpperCase(); - const url = err.config?.url ?? "?"; - return `${method} ${url} → ${err.code ?? `HTTP ${err.response?.status}`}`; - } - return err?.message ?? String(err); -}; - -// Helper to run a startup sync safely — failures are logged but never crash the process. +// Helper to run a startup task safely — failures are logged but never crash the process. const safeStartup = async (label: string, fn: () => Promise) => { try { await fn(); } catch (err) { - console.error( - `[startup] ${label} failed — will retry on next interval`, - err, - ); + console.error(`[startup] ${label} failed`, err); } }; @@ -83,33 +54,30 @@ console.log(`[startup] Server listening on port ${PORT}`); setupSockets(); console.log("[startup] Socket namespaces initialized"); -collectorSocket.on("connect", () => { - console.log( - `[startup] Collector socket connected: ${COLLECTOR_WS_URL} (${collectorSocket.id})`, - ); -}); - -collectorSocket.on("connect_error", (err) => { - console.error(`[startup] Collector socket connect_error: ${err.message}`); -}); - -connectCollectorSocket(); -console.log("[startup] Collector socket initialization started"); +// Initialize worker system (PgBoss connection) +await safeStartup("initializeWorkerSystem", () => initializeWorkerSystem()); +// Start the inter-process comms server so the worker can connect on :8671 startCommsServer(); -console.log("[startup] Worker comms server initialized"); +console.log("[startup] Comms server listening on :8671"); -const embeddedWorkersEnabled = Bun.env.START_EMBEDDED_WORKERS === "true"; -if (embeddedWorkersEnabled) { - await safeStartup( - "startOpportunityCacheWorkers", - startOpportunityCacheWorkers, +// Enqueue a full dalpuri sync on startup +await safeStartup("enqueueDalpuriFullSync", async () => { + const jobId = await getBoss().send(WorkerQueue.DALPURI_FULL_SYNC, {}, { singletonKey: `startup-${Date.now()}` }); + if (jobId) { + console.log(`[startup] Dalpuri full sync enqueued: ${jobId}`); + } else { + console.warn("[startup] Dalpuri full sync send returned null — job may already be pending or PgBoss not ready"); + } +}); + +// Broadcast incremental sync jobs from the API process every 5s so the +// interval survives worker restarts. +setInterval(() => { + enqueueIncrementalSync().catch((err) => + console.error(`[interval] enqueueIncrementalSync failed: ${err?.message ?? err}`) ); -} else { - console.log( - "[startup] Embedded opportunity workers disabled on API node (set START_EMBEDDED_WORKERS=true to override)", - ); -} +}, 5_000); // --------------------------------------------------------------------------- // Background initialisation — none of this blocks the server. @@ -141,114 +109,30 @@ await safeStartup("ensureAdminRole", async () => { } }); -// Refresh the internal list of companies every minute -await safeStartup("refreshCompanies", refreshCompanies); -setInterval(() => { - return refreshCompanies().catch((err) => - console.error(`[interval] refreshCompanies failed: ${briefErr(err)}`), +// Enqueue an initial cold-load metrics refresh on startup +await safeStartup("enqueueSalesMetricsRefresh", async () => { + const jobId = await getBoss().send( + WorkerQueue.REFRESH_SALES_METRICS, + { forceColdLoad: true }, + { singletonKey: `startup-metrics-${Date.now()}` } ); -}, 60 * 1000); + if (jobId) { + console.log(`[startup] Sales metrics refresh enqueued: ${jobId}`); + } else { + console.warn("[startup] Sales metrics refresh send returned null — job may already be pending"); + } +}); -// Refresh the internal catalog every 30 minutes -await safeStartup("refreshCatalog", refreshCatalog); -setInterval( - () => { - return refreshCatalog().catch((err) => - console.error(`[interval] refreshCatalog failed: ${briefErr(err)}`), - ); - }, - 30 * 60 * 1000, -); - -// Fallback full inventory sweep every 6 hours (listener handles real-time deltas) -setInterval( - () => { - return refreshInventory().catch((err) => - console.error(`[interval] refreshInventory failed: ${briefErr(err)}`), - ); - }, - 6 * 60 * 60 * 1000, -); - -// Listen for procurement adjustment changes and sync changed products to DB + cache -await safeStartup("listenInventoryAdjustments", listenInventoryAdjustments); +// Enqueue a metrics refresh every 5 minutes setInterval(() => { - return listenInventoryAdjustments().catch((err) => - console.error( - `[interval] listenInventoryAdjustments failed: ${briefErr(err)}`, - ), - ); -}, 60 * 1000); - -// Refresh opportunity CW cache every 20 minutes (activities + company hydration) -// NOTE: Do NOT await — register the interval immediately so the cache refresh -// is never blocked by a slow/stuck startup task above. -safeStartup("enqueueActiveOpportunityRefreshJob", async () => { - await enqueueActiveOpportunityRefreshJob(); -}); -setInterval( - () => { - return enqueueActiveOpportunityRefreshJob().catch((err) => { - console.error( - `[interval] enqueueActiveOpportunityRefreshJob failed: ${briefErr(err)}`, - ); - }); - }, - 20 * 60 * 1000, -); - -// Refresh User Defined Fields every 5 minutes -await safeStartup("refreshUDFs", async () => { - await userDefinedFieldsCw.refresh(); -}); -setInterval( - () => { - return userDefinedFieldsCw - .refresh() - .catch((err) => - console.error(`[interval] refreshUDFs failed: ${briefErr(err)}`), - ); - }, - 5 * 60 * 1000, -); - -// Refresh sales opportunity metrics cache for active CW members every 5 minutes -await safeStartup("refreshSalesOpportunityMetricsCache", () => - refreshSalesOpportunityMetricsCache({ forceColdLoad: true }), -); -setInterval( - () => { - return refreshSalesOpportunityMetricsCache().catch((err) => - console.error( - `[interval] refreshSalesOpportunityMetricsCache failed: ${briefErr(err)}`, - ), + getBoss() + .send(WorkerQueue.REFRESH_SALES_METRICS, {}, { singletonKey: "metrics-interval" }) + .catch((err) => + console.error(`[interval] REFRESH_SALES_METRICS enqueue failed: ${err?.message ?? err}`) ); - }, - 5 * 60 * 1000, -); - -// Refresh CW identifiers for all users every 30 minutes -await safeStartup("refreshCwIdentifiers", refreshCwIdentifiers); -setInterval( - () => { - return refreshCwIdentifiers().catch((err) => - console.error(`[interval] refreshCwIdentifiers failed: ${briefErr(err)}`), - ); - }, - 30 * 60 * 1000, -); - -// Refresh CW members DB table every hour -await safeStartup("refreshCwMembers", refreshCwMembers); -setInterval( - () => { - return refreshCwMembers().catch((err) => - console.error(`[interval] refreshCwMembers failed: ${briefErr(err)}`), - ); - }, - 60 * 60 * 1000, -); +}, 5 * 60 * 1000); +// Sync UniFi sites await safeStartup("syncSites", async () => { await unifiSites.syncSites(); }); @@ -256,6 +140,6 @@ setInterval(() => { return unifiSites .syncSites() .catch((err) => - console.error(`[interval] syncSites failed: ${briefErr(err)}`), + console.error(`[interval] syncSites failed: ${err?.message ?? err}`) ); }, 60 * 1000); diff --git a/api/src/managers/activities.ts b/api/src/managers/activities.ts index 82b139c..bb4a355 100644 --- a/api/src/managers/activities.ts +++ b/api/src/managers/activities.ts @@ -1,5 +1,4 @@ import { ActivityController } from "../controllers/ActivityController"; -import { connectWiseApi } from "../constants"; import GenericError from "../Errors/GenericError"; import { activityCw } from "../modules/cw-utils/activities/activities"; import { @@ -18,28 +17,20 @@ export const activities = { * @returns {Promise} */ async fetchItem(cwActivityId: number): Promise { - try { - const cwData = await activityCw.fetch(cwActivityId); - return new ActivityController(cwData); - } catch (error) { - const errBody = (error as any).response?.data || error; - throw new GenericError({ - name: "FetchActivityError", - message: `Failed to fetch activity ${cwActivityId}`, - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: (error as any).status ?? 502, - }); - } + // TODO: Query local Activity table when synced by dalpuri + throw new GenericError({ + name: "NotAvailable", + message: "Activity fetch from local DB not yet implemented", + status: 501, + }); }, /** * Fetch All Activities (Paginated) * - * Fetches activities from ConnectWise with optional conditions and pagination. - * * @param page - Page number (1-based) * @param rpp - Records per page - * @param conditions - Optional CW conditions string for filtering + * @param conditions - Optional conditions string for filtering * @returns {Promise} */ async fetchPages( @@ -47,73 +38,32 @@ export const activities = { rpp: number, conditions?: string, ): Promise { - try { - const pageNum = Math.max(page, 1); - const conditionsParam = conditions - ? `&conditions=${encodeURIComponent(conditions)}` - : ""; - const response = await connectWiseApi.get( - `/sales/activities?page=${pageNum}&pageSize=${rpp}${conditionsParam}`, - ); - const items = response.data; - return items.map((item: any) => new ActivityController(item)); - } catch (error) { - const errBody = (error as any).response?.data || error; - throw new GenericError({ - name: "FetchActivitiesError", - message: "Failed to fetch activities from ConnectWise", - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: 502, - }); - } + // TODO: Query local Activity table when synced by dalpuri + return []; }, /** * Fetch Activities by Company * - * Fetches all activities for a company by its ConnectWise company ID. - * * @param cwCompanyId - The ConnectWise company ID * @returns {Promise} */ async fetchByCompany(cwCompanyId: number): Promise { - try { - const collection = await activityCw.fetchByCompany(cwCompanyId); - return collection.map((item) => new ActivityController(item)); - } catch (error) { - const errBody = (error as any).response?.data || error; - throw new GenericError({ - name: "FetchCompanyActivitiesError", - message: `Failed to fetch activities for company ${cwCompanyId}`, - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: 502, - }); - } + // TODO: Query local Activity table when synced by dalpuri + return []; }, /** * Fetch Activities by Opportunity * - * Fetches all activities for an opportunity by its ConnectWise opportunity ID. - * * @param cwOpportunityId - The ConnectWise opportunity ID * @returns {Promise} */ async fetchByOpportunity( cwOpportunityId: number, ): Promise { - try { - const collection = await activityCw.fetchByOpportunity(cwOpportunityId); - return collection.map((item) => new ActivityController(item)); - } catch (error) { - const errBody = (error as any).response?.data || error; - throw new GenericError({ - name: "FetchOpportunityActivitiesError", - message: `Failed to fetch activities for opportunity ${cwOpportunityId}`, - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: 502, - }); - } + // TODO: Query local Activity table when synced by dalpuri + return []; }, /** @@ -196,16 +146,7 @@ export const activities = { * @returns {Promise} */ async count(conditions?: string): Promise { - try { - return await activityCw.countItems(conditions); - } catch (error) { - const errBody = (error as any).response?.data || error; - throw new GenericError({ - name: "CountActivitiesError", - message: "Failed to count activities in ConnectWise", - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: 502, - }); - } + // TODO: Count from local Activity table when synced by dalpuri + return 0; }, }; diff --git a/api/src/managers/companies.ts b/api/src/managers/companies.ts index 3aad4e4..d6c802a 100644 --- a/api/src/managers/companies.ts +++ b/api/src/managers/companies.ts @@ -1,35 +1,23 @@ -import { connectWiseApi, prisma } from "../constants"; +import { prisma } from "../constants"; import { CompanyController } from "../controllers/CompanyController"; -import { Company } from "../types/ConnectWiseTypes"; export const companies = { async fetch(identifier: string | number): Promise { - const search = await prisma.company.findFirst({ - where: { - OR: [{ id: identifier as string }], - }, + const isNumeric = + typeof identifier === "number" || /^\d+$/.test(String(identifier)); + + const company = await prisma.company.findFirst({ + where: isNumeric + ? { id: Number(identifier) } + : { + OR: [{ uid: String(identifier) }, { name: String(identifier) }], + }, + include: { contacts: true, companyAddresses: true }, }); - if (!search) throw new Error("Unknown company."); + if (!company) throw new Error("Unknown company."); - const freshCwData: { data: Company } = await connectWiseApi.get( - `/company/companies/${search.cw_CompanyId}`, - ); - - const contactHref = freshCwData.data.defaultContact?._info?.contact_href; - const defaultContactData = contactHref - ? await connectWiseApi.get(contactHref) - : undefined; - - const allContactsData = await connectWiseApi.get( - `${freshCwData.data._info.contacts_href}&pageSize=1000`, - ); - - return new CompanyController(search, { - company: freshCwData.data, - defaultContact: defaultContactData?.data ?? null, - allContacts: allContactsData.data, - }); + return new CompanyController(company); }, async count() { @@ -75,12 +63,14 @@ export const companies = { const skip = (page > 1 ? page : 0) * rpp; const take = rpp ?? 30; + const numericQuery = parseInt(query, 10); + const data = prisma.company.findMany({ where: { OR: [ - { cw_Identifier: { contains: query, mode: "insensitive" } }, { name: { contains: query, mode: "insensitive" } }, - { id: { contains: query, mode: "insensitive" } }, + { uid: { contains: query, mode: "insensitive" } }, + ...(!isNaN(numericQuery) ? [{ id: numericQuery }] : []), ], }, skip, diff --git a/api/src/managers/credentialTypes.ts b/api/src/managers/credentialTypes.ts index 14f0ae0..321f247 100644 --- a/api/src/managers/credentialTypes.ts +++ b/api/src/managers/credentialTypes.ts @@ -2,6 +2,7 @@ import { prisma } from "../constants"; import { CredentialTypeController } from "../controllers/CredentialTypeController"; import { CredentialTypeField } from "../modules/credentials/credentialTypeDefs"; import GenericError from "../Errors/GenericError"; +import { Prisma } from "@prisma/client"; export const credentialTypes = { /** @@ -83,7 +84,7 @@ export const credentialTypes = { data: { name: data.name, permissionScope: data.permissionScope, - fields: data.fields as any, + fields: data.fields as Prisma.JsonArray, icon: data.icon, }, include: { @@ -91,8 +92,6 @@ export const credentialTypes = { }, }); - console.log(credentialType.fields); - return new CredentialTypeController(credentialType); }, diff --git a/api/src/managers/credentials.ts b/api/src/managers/credentials.ts index 05cbabd..77aaf5c 100644 --- a/api/src/managers/credentials.ts +++ b/api/src/managers/credentials.ts @@ -8,6 +8,7 @@ import { } from "../modules/credentials/credentialTypeDefs"; import { generateSecureValue } from "../modules/credentials/generateSecureValue"; import GenericError from "../Errors/GenericError"; +import { Prisma } from "@prisma/client"; /** * Standard include clause used by every credential query. @@ -115,7 +116,8 @@ export const credentials = { }); } - const typeFields = credentialType.fields! as any as CredentialTypeField[]; + const typeFields = + credentialType.fields! as Prisma.JsonArray as CredentialTypeField[]; // Validate the fields against acceptable fields (exclude multi-credential fields // from value validation since they don't carry a direct value). @@ -129,16 +131,16 @@ export const credentials = { })) as CredentialTypeField[]; const validatedFields = await fieldValidator( - data.fields as any as CredentialField[], - acceptableFields, + data.fields as Prisma.JsonArray as CredentialField[], + acceptableFields ); // Separate secure, non-secure, and multi-credential fields const secureFields = validatedFields.filter( - (f) => f.secure && !f.isMultiCredential, + (f) => f.secure && !f.isMultiCredential ); const nonSecureFields = validatedFields.filter( - (f) => !f.secure && !f.isMultiCredential, + (f) => !f.secure && !f.isMultiCredential ); // Build fields object for non-secure fields @@ -181,7 +183,7 @@ export const credentials = { // Create inline sub-credentials when provided if (data.subCredentials) { for (const [fieldId, subCredDataList] of Object.entries( - data.subCredentials, + data.subCredentials )) { const fieldDef = typeFields.find((f) => f.id === fieldId); @@ -200,8 +202,8 @@ export const credentials = { for (const subCredData of subCredDataList) { const validatedSubFields = await fieldValidator( - subCredData.fields as any as CredentialField[], - subFieldDefs, + subCredData.fields as Prisma.JsonArray as CredentialField[], + subFieldDefs ); const subSecure = validatedSubFields.filter((f) => f.secure); @@ -267,7 +269,7 @@ export const credentials = { data: { name: string; fields: { fieldId: string; value: string }[]; - }, + } ): Promise { const parent = await prisma.credential.findFirst({ where: { id: parentId }, @@ -297,8 +299,8 @@ export const credentials = { const subFieldDefs = (fieldDef.subFields ?? []) as CredentialTypeField[]; const validatedFields = await fieldValidator( - data.fields as any as CredentialField[], - subFieldDefs, + data.fields as Prisma.JsonArray as CredentialField[], + subFieldDefs ); const secureFields = validatedFields.filter((f) => f.secure); @@ -352,7 +354,7 @@ export const credentials = { */ async removeSubCredential( parentId: string, - subCredentialId: string, + subCredentialId: string ): Promise { const subCredential = await prisma.credential.findFirst({ where: { id: subCredentialId, subCredentialOfId: parentId }, @@ -382,7 +384,7 @@ export const credentials = { for (const key of Object.keys(parentFields)) { if (Array.isArray(parentFields[key])) { parentFields[key] = parentFields[key].filter( - (id: string) => id !== subCredentialId, + (id: string) => id !== subCredentialId ); } } diff --git a/api/src/managers/generatedQuotes.ts b/api/src/managers/generatedQuotes.ts index ade9603..f154db4 100644 --- a/api/src/managers/generatedQuotes.ts +++ b/api/src/managers/generatedQuotes.ts @@ -31,7 +31,7 @@ export const generatedQuotes = { }, async fetchByOpportunity( - opportunityId: string, + opportunityId: string ): Promise { const rows = await prisma.generatedQuotes.findMany({ where: { opportunityId }, @@ -43,7 +43,7 @@ export const generatedQuotes = { }, async fetchByCreator( - createdById: string, + createdById: string ): Promise { const rows = await prisma.generatedQuotes.findMany({ where: { createdById }, @@ -55,7 +55,7 @@ export const generatedQuotes = { }, async fetchByHash( - quoteRegenHash: string, + quoteRegenHash: string ): Promise { const quote = await prisma.generatedQuotes.findUnique({ where: { quoteRegenHash }, @@ -76,15 +76,15 @@ export const generatedQuotes = { createdById: string; }): Promise { const opportunity = await prisma.opportunity.findFirst({ - where: { id: data.opportunityId }, - select: { id: true }, + where: { uid: data.opportunityId }, + select: { uid: true }, }); if (!opportunity) { throw new GenericError({ message: "Opportunity not found", name: "OpportunityNotFound", - cause: `No opportunity exists with ID '${data.opportunityId}'`, + cause: `No opportunity exists with uid '${data.opportunityId}'`, status: 404, }); } @@ -147,7 +147,7 @@ export const generatedQuotes = { name?: string | null; email: string; fetchAction: string; - }, + } ): Promise { const existing = await prisma.generatedQuotes.findFirst({ where: { id }, diff --git a/api/src/managers/opportunities.ts b/api/src/managers/opportunities.ts index d858046..5f0d467 100644 --- a/api/src/managers/opportunities.ts +++ b/api/src/managers/opportunities.ts @@ -1,574 +1,33 @@ -import { Company } from "../../generated/prisma/client"; -import { prisma, redis } from "../constants"; -import { ActivityController } from "../controllers/ActivityController"; +import { prisma } from "../constants"; import { CompanyController } from "../controllers/CompanyController"; import { OpportunityController } from "../controllers/OpportunityController"; +import UserController from "../controllers/UserController"; import GenericError from "../Errors/GenericError"; -import { activityCw } from "../modules/cw-utils/activities/activities"; import { opportunityCw } from "../modules/cw-utils/opportunities/opportunities"; import { CWOpportunityCreate } from "../modules/cw-utils/opportunities/opportunity.types"; -import { computeCacheTTL } from "../modules/algorithms/computeCacheTTL"; -import { computeSubResourceCacheTTL } from "../modules/algorithms/computeSubResourceCacheTTL"; -import { computeProductsCacheTTL } from "../modules/algorithms/computeProductsCacheTTL"; -import { - TTL_ARCHIVED_MS, - activityCacheKey, - contactsCacheKey, - getCachedActivities, - getCachedCompanyCwData, - getCachedOppCwData, - fetchAndCacheActivities, - fetchAndCacheCompanyCwData, - fetchAndCacheOppCwData, - invalidateAllOpportunityCaches, - notesCacheKey, - oppCwDataCacheKey, - productsCacheKey, -} from "../modules/cache/opportunityCache"; -import { - normalizeCollectorOpportunities, - type NormalizedCollectorOpportunity, - type CollectorOpportunity, -} from "../modules/cw-utils/opportunities/opportunityCollectorTranslation"; - -const parseCollectorDate = (value?: string | null): Date | null => { - if (!value) return null; - const parsed = new Date(value); - return Number.isNaN(parsed.getTime()) ? null : parsed; -}; - -const asNumber = (value: unknown): number | null => { - if (typeof value === "number" && Number.isFinite(value)) return value; - if (typeof value === "string" && value.trim().length > 0) { - const parsed = Number(value); - return Number.isFinite(parsed) ? parsed : null; - } - return null; -}; - -const asString = (value: unknown): string | null => { - if (typeof value !== "string") return null; - const trimmed = value.trim(); - return trimmed.length > 0 ? trimmed : null; -}; - -const mapCollectorOpportunityToCw = ( - item: CollectorOpportunity, - cwOpportunityId: number, -) => ({ - id: cwOpportunityId, - name: item.soDescription ?? "", - notes: item.notes ?? null, - customerPO: item.customerPO ?? null, - source: item.source ?? null, - company: item.company?.recId - ? { - id: item.company.recId, - identifier: item.company.companyIdentifier ?? null, - name: item.company.companyName ?? null, - } - : null, - contact: item.contact?.recId - ? { id: item.contact.recId, name: item.contact.contactName ?? null } - : null, - stage: item.soPipeline?.recId - ? { id: item.soPipeline.recId, name: item.soPipeline.description ?? null } - : null, - status: item.soOppStatus?.recId - ? { id: item.soOppStatus.recId, name: item.soOppStatus.description ?? null } - : null, - type: item.soType?.recId - ? { id: item.soType.recId, name: item.soType.description ?? null } - : null, - owner: item.ownerLevel?.recId - ? { - id: item.ownerLevel.recId, - identifier: item.ownerLevel.memberIdentifier ?? null, - name: item.ownerLevel.memberName ?? null, - } - : null, - expectedCloseDate: item.expectedCloseDate ?? null, - closedDate: item.closedDate ?? null, - closedFlag: item.closedFlag ?? false, - customFields: [], - _info: { - lastUpdated: item._info?.lastUpdated, - dateEntered: item._info?.dateEntered, - }, -}); - -const mapCollectorNoteToCw = (note: any, idx: number) => { - const id = - asNumber(note?.id) ?? asNumber(note?.noteRecId) ?? asNumber(note?.recId); - const typeId = asNumber(note?.type?.id) ?? asNumber(note?.typeRecId); - const typeName = - asString(note?.type?.name) ?? - asString(note?.type?.description) ?? - asString(note?.typeDescription); - const enteredBy = - asString(note?.enteredBy) ?? - asString(note?.memberIdentifier) ?? - asString(note?.createdBy) ?? - asString(note?.updatedBy); - const lastUpdated = - asString(note?._info?.lastUpdated) ?? - asString(note?.lastUpdated) ?? - asString(note?.lastUpdatedUtc) ?? - asString(note?.dateEnteredUtc); - - return { - id: id ?? -(idx + 1), - text: asString(note?.text) ?? asString(note?.note) ?? asString(note?.notes) ?? "", - type: typeId ? { id: typeId, name: typeName ?? "" } : null, - flagged: Boolean(note?.flagged ?? note?.flaggedFlag ?? false), - enteredBy: enteredBy ?? "collector", - _info: { - lastUpdated: lastUpdated ?? new Date().toISOString(), - }, - }; -}; - -const mapCollectorContactToCw = ( - contact: any, - idx: number, - item: CollectorOpportunity, -) => { - const id = - asNumber(contact?.id) ?? - asNumber(contact?.opportunityContactRecId) ?? - asNumber(contact?.recId); - const contactId = - asNumber(contact?.contact?.id) ?? - asNumber(contact?.contact?.recId) ?? - asNumber(contact?.contactRecId) ?? - item.contact?.recId ?? - null; - const contactName = - asString(contact?.contact?.name) ?? - asString(contact?.contact?.contactName) ?? - asString(contact?.contactName) ?? - item.contact?.contactName ?? - null; - const roleId = - asNumber(contact?.role?.id) ?? - asNumber(contact?.role?.recId) ?? - asNumber(contact?.roleRecId); - const roleName = - asString(contact?.role?.name) ?? - asString(contact?.role?.description) ?? - asString(contact?.roleDescription); - - return { - id: id ?? -(idx + 1), - contact: contactId ? { id: contactId, name: contactName ?? "" } : null, - company: item.company?.recId - ? { - id: item.company.recId, - identifier: item.company.companyIdentifier ?? null, - name: item.company.companyName ?? null, - } - : null, - role: roleId ? { id: roleId, name: roleName ?? "" } : null, - notes: asString(contact?.notes), - referralFlag: Boolean(contact?.referralFlag ?? false), - }; -}; - -const mapCollectorActivityToCw = ( - activity: any, - idx: number, - item: CollectorOpportunity, - cwOpportunityId: number, -) => { - const id = - asNumber(activity?.id) ?? - asNumber(activity?.activityRecId) ?? - asNumber(activity?.recId); - const typeId = asNumber(activity?.type?.id) ?? asNumber(activity?.type?.recId); - const statusId = - asNumber(activity?.status?.id) ?? asNumber(activity?.status?.recId); - const scheduleStatusId = - asNumber(activity?.scheduleStatus?.id) ?? - asNumber(activity?.scheduleStatus?.recId); - - return { - id: id ?? -(idx + 1), - name: asString(activity?.name) ?? asString(activity?.activityName) ?? "Collector Activity", - notes: asString(activity?.notes), - type: typeId - ? { - id: typeId, - name: - asString(activity?.type?.name) ?? - asString(activity?.type?.description) ?? - "", - } - : null, - status: statusId - ? { - id: statusId, - name: - asString(activity?.status?.name) ?? - asString(activity?.status?.description) ?? - "", - } - : null, - company: item.company?.recId - ? { - id: item.company.recId, - identifier: item.company.companyIdentifier ?? null, - name: item.company.companyName ?? null, - } - : null, - contact: item.contact?.recId - ? { id: item.contact.recId, name: item.contact.contactName ?? null } - : null, - opportunity: { id: cwOpportunityId, name: item.soDescription ?? null }, - assignTo: item.ownerLevel?.recId - ? { - id: item.ownerLevel.recId, - identifier: item.ownerLevel.memberIdentifier ?? null, - name: item.ownerLevel.memberName ?? null, - } - : null, - scheduleStatus: scheduleStatusId - ? { - id: scheduleStatusId, - name: - asString(activity?.scheduleStatus?.name) ?? - asString(activity?.scheduleStatus?.description) ?? - "", - } - : null, - dateStart: asString(activity?.dateStart), - dateEnd: asString(activity?.dateEnd), - notifyFlag: Boolean(activity?.notifyFlag ?? false), - customFields: Array.isArray(activity?.customFields) ? activity.customFields : [], - _info: { - lastUpdated: - asString(activity?._info?.lastUpdated) ?? - asString(activity?.lastUpdated) ?? - new Date().toISOString(), - dateEntered: - asString(activity?._info?.dateEntered) ?? asString(activity?.dateEntered), - enteredBy: - asString(activity?._info?.enteredBy) ?? asString(activity?.enteredBy), - updatedBy: - asString(activity?._info?.updatedBy) ?? asString(activity?.updatedBy), - }, - }; -}; - -const mapCollectorForecastItemToCw = ( - forecastItem: any, - idx: number, - item: CollectorOpportunity, - cwOpportunityId: number, -) => { - const id = - asNumber(forecastItem?.id) ?? - asNumber(forecastItem?.forecastDetailId) ?? - asNumber(forecastItem?.recId); - const statusId = - asNumber(forecastItem?.status?.id) ?? - asNumber(forecastItem?.status?.recId) ?? - 1; - - return { - id: id ?? -(idx + 1), - forecastDescription: - asString(forecastItem?.forecastDescription) ?? - asString(forecastItem?.description) ?? - asString(forecastItem?.productDescription) ?? - "", - opportunity: { id: cwOpportunityId, name: item.soDescription ?? null }, - quantity: asNumber(forecastItem?.quantity) ?? 1, - status: { - id: statusId, - name: - asString(forecastItem?.status?.name) ?? - asString(forecastItem?.status?.description) ?? - "", - }, - catalogItem: - asNumber(forecastItem?.catalogItem?.id) ?? - asNumber(forecastItem?.catalogItemRecId) - ? { - id: - (asNumber(forecastItem?.catalogItem?.id) ?? - asNumber(forecastItem?.catalogItemRecId))!, - identifier: - asString(forecastItem?.catalogItem?.identifier) ?? - asString(forecastItem?.catalogItemIdentifier), - } - : null, - productDescription: - asString(forecastItem?.productDescription) ?? - asString(forecastItem?.description) ?? - "", - customerDescription: asString(forecastItem?.customerDescription), - productClass: asString(forecastItem?.productClass) ?? "Product", - forecastType: asString(forecastItem?.forecastType) ?? "Product", - revenue: asNumber(forecastItem?.revenue) ?? 0, - cost: asNumber(forecastItem?.cost) ?? 0, - margin: asNumber(forecastItem?.margin) ?? 0, - percentage: asNumber(forecastItem?.percentage) ?? 0, - includeFlag: - forecastItem?.includeFlag === undefined - ? true - : Boolean(forecastItem.includeFlag), - linkFlag: Boolean(forecastItem?.linkFlag ?? false), - recurringFlag: Boolean(forecastItem?.recurringFlag ?? false), - taxableFlag: Boolean(forecastItem?.taxableFlag ?? false), - recurringRevenue: asNumber(forecastItem?.recurringRevenue) ?? 0, - recurringCost: asNumber(forecastItem?.recurringCost) ?? 0, - cycles: asNumber(forecastItem?.cycles) ?? 0, - sequenceNumber: asNumber(forecastItem?.sequenceNumber) ?? idx, - subNumber: asNumber(forecastItem?.subNumber) ?? 0, - quoteWerksQuantity: asNumber(forecastItem?.quoteWerksQuantity) ?? 0, - customFields: Array.isArray(forecastItem?.customFields) - ? forecastItem.customFields - : [], - _info: { - lastUpdated: - asString(forecastItem?._info?.lastUpdated) ?? - asString(forecastItem?.lastUpdated) ?? - new Date().toISOString(), - updatedBy: - asString(forecastItem?._info?.updatedBy) ?? - asString(forecastItem?.updatedBy) ?? - "collector", - }, - }; -}; - -const selectCollectorContacts = (item: CollectorOpportunity): unknown[] | null => { - if (Array.isArray((item as any).opportunityContacts)) { - return (item as any).opportunityContacts as unknown[]; - } - if (Array.isArray((item as any).contacts)) { - return (item as any).contacts as unknown[]; - } - return null; -}; - -async function seedCollectorOpportunityCaches( - items: CollectorOpportunity[], -): Promise { - const pipeline = redis.pipeline(); - - for (const item of items) { - const cwOpportunityId = Number( - item.soRecId ?? - (item as any).recId ?? - (item as any).id ?? - (item as any).opportunityRecId ?? - 0, - ); - if (!Number.isFinite(cwOpportunityId) || cwOpportunityId <= 0) continue; - - const closedDate = parseCollectorDate(item.closedDate); - const expectedCloseDate = parseCollectorDate(item.expectedCloseDate); - const lastUpdated = parseCollectorDate(item._info?.lastUpdated); - - const primaryTtl = - computeCacheTTL({ - closedFlag: item.closedFlag ?? false, - closedDate, - expectedCloseDate, - lastUpdated, - }) ?? TTL_ARCHIVED_MS; - - const subResourceTtl = - computeSubResourceCacheTTL({ - closedFlag: item.closedFlag ?? false, - closedDate, - expectedCloseDate, - lastUpdated, - }) ?? TTL_ARCHIVED_MS; - - const productsTtl = - computeProductsCacheTTL({ - statusName: item.soOppStatus?.description ?? null, - closedFlag: item.closedFlag ?? false, - closedDate, - expectedCloseDate, - lastUpdated, - }) ?? TTL_ARCHIVED_MS; - - if (Array.isArray(item.activities)) { - const activityItems = item.activities.map((activity, idx) => - mapCollectorActivityToCw(activity, idx, item, cwOpportunityId), - ); - pipeline.set( - activityCacheKey(cwOpportunityId), - JSON.stringify(activityItems), - "PX", - primaryTtl, - ); - } - - if (Array.isArray(item.opportunityNotes)) { - const notes = item.opportunityNotes.map((note, idx) => - mapCollectorNoteToCw(note, idx), - ); - pipeline.set( - notesCacheKey(cwOpportunityId), - JSON.stringify(notes), - "PX", - subResourceTtl, - ); - } - - const collectorContacts = selectCollectorContacts(item); - if (collectorContacts) { - const contacts = collectorContacts.map((contact, idx) => - mapCollectorContactToCw(contact, idx, item), - ); - pipeline.set( - contactsCacheKey(cwOpportunityId), - JSON.stringify(contacts), - "PX", - subResourceTtl, - ); - } - - if (Array.isArray(item.forecastItems)) { - const forecastItems = item.forecastItems.map((forecastItem, idx) => - mapCollectorForecastItemToCw(forecastItem, idx, item, cwOpportunityId), - ); - const forecastBlob = { - forecast: { forecastItems }, - procProducts: [], - }; - pipeline.set( - productsCacheKey(cwOpportunityId), - JSON.stringify(forecastBlob), - "PX", - productsTtl, - ); - } - - const cwOpportunity = mapCollectorOpportunityToCw(item, cwOpportunityId); - pipeline.set( - oppCwDataCacheKey(cwOpportunityId), - JSON.stringify(cwOpportunity), - "PX", - primaryTtl, - ); - } - - await pipeline.exec(); -} // --------------------------------------------------------------------------- -// Data-source hierarchy helpers +// Data-source helpers (DB-only, no cache) // --------------------------------------------------------------------------- /** - * Build a CompanyController with hydrated CW data from a Prisma Company record. - * - * Data-source hierarchy (controlled by `strategy`): - * - * - `"cache-only"` — Redis cache → bare DB record (no CW call). - * Ideal for list views where latency matters and the background - * refresh job is responsible for keeping the cache warm. - * - * - `"cache-then-cw"` (default) — Redis cache → CW API → cache result. - * On a cold cache, calls CW to ensure the caller gets full data. - * - * - `"cw-first"` — CW API (always) → cache result. - * Forces a fresh fetch regardless of cache state. + * Build a CompanyController from a Prisma Company record. + * DB-only - no cache or CW API calls. */ async function buildCompanyController( - company: Company, - opts?: { - strategy?: "cache-only" | "cache-then-cw" | "cw-first"; - ttlMs?: number; - }, -): Promise { - const strategy = opts?.strategy ?? "cache-then-cw"; - const ctrl = new CompanyController(company); + companyId: number +): Promise { + const company = await prisma.company.findFirst({ + where: { id: companyId }, + include: { + contacts: true, + companyAddresses: true, + }, + }); - // ── cw-first: always fetch from CW (and cache the result) ────────── - if (strategy === "cw-first") { - const blob = opts?.ttlMs - ? await fetchAndCacheCompanyCwData( - company.cw_CompanyId, - opts.ttlMs, - ).catch(() => null) - : null; - if (blob) { - ctrl.cw_Data = blob; - } else { - await ctrl.hydrateCwData(); - } - return ctrl; - } + if (!company) return undefined; - // ── cache-only / cache-then-cw: try Redis first ───────────────────── - const cached = await getCachedCompanyCwData(company.cw_CompanyId); - if (cached) { - ctrl.cw_Data = cached; - return ctrl; - } - - // cache-only stops here — return the bare DB-backed controller - if (strategy === "cache-only") return ctrl; - - // cache-then-cw: cache miss — fetch from CW once and cache in one pass - if (opts?.ttlMs) { - const blob = await fetchAndCacheCompanyCwData( - company.cw_CompanyId, - opts.ttlMs, - ).catch(() => null); - if (blob) ctrl.cw_Data = blob; - } else { - await ctrl.hydrateCwData(); - } - return ctrl; -} - -/** - * Fetch ActivityController[] for an opportunity. - * - * Same three strategies as {@link buildCompanyController}: - * - * - `"cache-only"` — Redis → empty array (no CW call). - * - `"cache-then-cw"` (default) — Redis → CW API → cache result. - * - `"cw-first"` — CW API (always) → cache result. - */ -async function buildActivities( - cwOpportunityId: number, - opts?: { - strategy?: "cache-only" | "cache-then-cw" | "cw-first"; - ttlMs?: number; - }, -): Promise { - const strategy = opts?.strategy ?? "cache-then-cw"; - - // ── cw-first: always fetch from CW (and cache the result) ────────── - if (strategy === "cw-first") { - const arr = opts?.ttlMs - ? await fetchAndCacheActivities(cwOpportunityId, opts.ttlMs) - : await activityCw.fetchByOpportunityDirect(cwOpportunityId); - return arr.map((item) => new ActivityController(item)); - } - - // ── cache-only / cache-then-cw: try Redis first ───────────────────── - const cached = await getCachedActivities(cwOpportunityId); - if (cached) { - return cached.map((item) => new ActivityController(item)); - } - - // cache-only stops here — return empty (background job will fill it) - if (strategy === "cache-only") return []; - - // cache-then-cw: cache miss — fetch once and cache in one pass - const arr = opts?.ttlMs - ? await fetchAndCacheActivities(cwOpportunityId, opts.ttlMs) - : await activityCw.fetchByOpportunityDirect(cwOpportunityId); - return arr.map((item) => new ActivityController(item)); + return new CompanyController(company); } export const opportunities = { @@ -585,29 +44,55 @@ export const opportunities = { const cwData = await opportunityCw.create(data); const mapped = OpportunityController.mapCwToDb(cwData); - // Resolve optional local company relation - const companyId = cwData.company?.id - ? (( - await prisma.company.findFirst({ - where: { cw_CompanyId: cwData.company.id }, - select: { id: true }, - }) - )?.id ?? null) - : null; + // 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([ + cwData.company?.id + ? prisma.company.findFirst({ where: { id: cwData.company.id }, select: { id: true } }) + : null, + mapped.contactId != null + ? prisma.contact.findFirst({ where: { id: mapped.contactId }, select: { id: true } }) + : null, + mapped.siteId != null + ? prisma.companyAddress.findFirst({ where: { id: mapped.siteId }, select: { id: true } }) + : null, + ]); + + const companyId = companyExists?.id ?? null; + const contactId = contactExists?.id ?? null; + const siteId = siteExists?.id ?? null; const record = await prisma.opportunity.create({ data: { - cwOpportunityId: cwData.id, + id: cwData.id, ...mapped, companyId, + contactId, + siteId, + }, + include: { + company: { include: { contacts: true, companyAddresses: true } }, + contact: true, + type: true, + status: true, + site: true, + primarySalesRep: { include: { roles: true } }, + secondarySalesRep: { include: { roles: true } }, + stage: true, + taxCode: true, }, - include: { company: true }, }); return new OpportunityController(record, { company: record.company ? new CompanyController(record.company) : undefined, + primarySalesRep: record.primarySalesRep + ? new UserController(record.primarySalesRep) + : undefined, + secondarySalesRep: record.secondarySalesRep + ? new UserController(record.secondarySalesRep) + : undefined, }); }, @@ -615,26 +100,32 @@ export const opportunities = { * Fetch Record (lightweight) * * Returns an OpportunityController backed only by the **database record**. - * No ConnectWise API calls, no Redis lookups, no activity/company hydration. + * No ConnectWise API calls, no Redis lookups. * - * Use this when you only need the controller instance to call a sub-resource - * method (e.g. `fetchNotes()`, `fetchContacts()`, `fetchProducts()`, - * `fetchSite()`). - * - * @param identifier - Internal ID (string) or CW opportunity ID (number) + * @param identifier - The CW opportunity ID (number) or internal uid (string) * @returns {Promise} */ async fetchRecord( - identifier: string | number, + identifier: string | number ): Promise { const isNumeric = typeof identifier === "number" || /^\d+$/.test(String(identifier)); const record = await prisma.opportunity.findFirst({ where: isNumeric - ? { cwOpportunityId: Number(identifier) } - : { id: identifier as string }, - include: { company: true }, + ? { id: Number(identifier) } + : { uid: identifier as string }, + include: { + company: { include: { contacts: true, companyAddresses: true } }, + contact: true, + type: true, + status: true, + site: true, + primarySalesRep: { include: { roles: true } }, + secondarySalesRep: { include: { roles: true } }, + stage: true, + taxCode: true, + }, }); if (!record) { @@ -650,48 +141,53 @@ export const opportunities = { company: record.company ? new CompanyController(record.company) : undefined, + primarySalesRep: record.primarySalesRep + ? new UserController(record.primarySalesRep) + : undefined, + secondarySalesRep: record.secondarySalesRep + ? new UserController(record.secondarySalesRep) + : undefined, }); }, /** * Fetch Opportunity * - * Fetch an opportunity by its internal ID or ConnectWise opportunity ID + * Fetch an opportunity by its internal uid or CW opportunity ID * and return an OpportunityController instance. * - * **Data-source strategy:** - * - `fresh: true` → `"cw-first"` — always fetches from CW, updates DB, caches result. - * - `fresh: false` (default) → `"cache-then-cw"` — tries Redis cache for the - * CW opportunity response first, falls back to CW on miss. + * Data is loaded from the local database only. * - * The CW opportunity response is cached in Redis with the same TTL as - * activities/company. The background refresh keeps this warm so most - * detail-view loads skip the CW roundtrip entirely. - * - * @param identifier - The internal ID (string) or CW opportunity ID (number) + * @param identifier - The internal uid (string) or CW opportunity ID (number) * @param opts - Optional flags - * @param opts.fresh - When `true`, bypass the cache and pull directly from CW. + * @param opts.fresh - Ignored (kept for API compatibility) * @returns {Promise} */ async fetchItem( identifier: string | number, - opts?: { fresh?: boolean }, + opts?: { fresh?: boolean } ): Promise { - const strategy: "cache-only" | "cache-then-cw" | "cw-first" = opts?.fresh - ? "cw-first" - : "cache-then-cw"; const isNumeric = typeof identifier === "number" || /^\d+$/.test(String(identifier)); - // Look up the existing DB record (full, with company) - const existing = await prisma.opportunity.findFirst({ + const record = await prisma.opportunity.findFirst({ where: isNumeric - ? { cwOpportunityId: Number(identifier) } - : { id: identifier as string }, - include: { company: true }, + ? { id: Number(identifier) } + : { uid: identifier as string }, + include: { + company: { include: { contacts: true, companyAddresses: true } }, + contact: true, + type: true, + status: true, + site: true, + primarySalesRep: { include: { roles: true } }, + secondarySalesRep: { include: { roles: true } }, + stage: true, + taxCode: true, + }, }); - if (!existing) { + if (!record) { throw new GenericError({ message: "Opportunity not found", name: "OpportunityNotFound", @@ -700,87 +196,22 @@ export const opportunities = { }); } - // Compute TTL from the current DB state (used for cache and hydration) - const ttlMs = - computeCacheTTL({ - closedFlag: existing.closedFlag, - closedDate: existing.closedDate, - expectedCloseDate: existing.expectedCloseDate, - lastUpdated: existing.cwLastUpdated, - }) ?? undefined; - - // ── Resolve CW opportunity data (cache-aware) ────────────────────── - let cwData: any; - let record = existing; // default: use the existing DB record as-is - - if (!opts?.fresh) { - // Try the Redis cache first - cwData = await getCachedOppCwData(existing.cwOpportunityId); - } - - // ── Parallel block: CW opp fetch + activities + company ──────────── - // Activities and company hydration only need existing.cwOpportunityId - // and existing.company — both available from the initial DB lookup — - // so they can run concurrently with the CW opp fetch + DB update. - - const cwOppPromise = (async () => { - if (cwData) return; // cache hit — nothing to do - - cwData = ttlMs - ? await fetchAndCacheOppCwData(existing.cwOpportunityId, ttlMs) - : await opportunityCw.fetch(existing.cwOpportunityId); - - if (!cwData) { - throw new GenericError({ - message: "Opportunity not found in ConnectWise", - name: "OpportunityNotFound", - cause: `CW returned 404 for opportunity ${existing.cwOpportunityId}`, - status: 404, - }); - } - - // Map and update the DB record (only on cache miss/fresh) - const mapped = OpportunityController.mapCwToDb(cwData); - const companyId = cwData.company?.id - ? (( - await prisma.company.findFirst({ - where: { cw_CompanyId: cwData.company.id }, - select: { id: true }, - }) - )?.id ?? null) - : null; - - record = await prisma.opportunity.update({ - where: { id: existing.id }, - data: { ...mapped, companyId }, - include: { company: true }, - }); - })(); - - // Hydrate activities and company in parallel with CW opp fetch - const [, activities, company] = await Promise.all([ - cwOppPromise, - buildActivities(existing.cwOpportunityId, { strategy, ttlMs }), - existing.company - ? buildCompanyController(existing.company, { strategy, ttlMs }) - : Promise.resolve(undefined), - ]); - return new OpportunityController(record, { - company, - customFields: cwData?.customFields ?? [], - activities, + company: record.company + ? new CompanyController(record.company) + : undefined, + primarySalesRep: record.primarySalesRep + ? new UserController(record.primarySalesRep) + : undefined, + secondarySalesRep: record.secondarySalesRep + ? new UserController(record.secondarySalesRep) + : undefined, }); }, /** * Fetch All Opportunities (Paginated) * - * Uses the **cache-only** strategy: Redis → bare DB data. - * Activities and company hydration come from the Redis cache if - * available; otherwise the controller is returned with DB-only data. - * The background refresh job is responsible for keeping Redis warm. - * * @param page - Page number (1-based) * @param rpp - Records per page * @param opts - Optional filters @@ -789,41 +220,47 @@ export const opportunities = { async fetchPages( page: number, rpp: number, - opts?: { includeClosed?: boolean }, + opts?: { includeClosed?: boolean } ): Promise { const skip = (Math.max(page, 1) - 1) * rpp; const items = await prisma.opportunity.findMany({ - where: opts?.includeClosed ? undefined : { closedDate: null }, - include: { company: true }, + where: opts?.includeClosed ? undefined : { closedFlag: false }, + include: { + company: { include: { contacts: true, companyAddresses: true } }, + contact: true, + type: true, + status: true, + primarySalesRep: { include: { roles: true } }, + secondarySalesRep: { include: { roles: true } }, + stage: true, + taxCode: true, + }, skip, take: rpp, - orderBy: { createdAt: "desc" }, + orderBy: { id: "desc" }, }); - return Promise.all( - items.map(async (item) => { - return new OpportunityController(item, { + return items.map( + (item) => + new OpportunityController(item, { company: item.company - ? await buildCompanyController(item.company, { - strategy: "cache-only", - }) + ? new CompanyController(item.company) : undefined, - activities: await buildActivities(item.cwOpportunityId, { - strategy: "cache-only", - }), - }); - }), + primarySalesRep: item.primarySalesRep + ? new UserController(item.primarySalesRep) + : undefined, + secondarySalesRep: item.secondarySalesRep + ? new UserController(item.secondarySalesRep) + : undefined, + }) ); }, /** * Search Opportunities * - * Search opportunities by name, company name, contact name, notes, - * sales rep, or status with pagination support. - * - * Uses the **cache-only** strategy (same as `fetchPages`). + * Search opportunities by name with pagination support. * * @param query - Search query string * @param page - Page number (1-based) @@ -835,7 +272,7 @@ export const opportunities = { query: string, page: number, rpp: number, - opts?: { includeClosed?: boolean }, + opts?: { includeClosed?: boolean } ): Promise { const skip = (Math.max(page, 1) - 1) * rpp; const numericQuery = /^\d+$/.test(query.trim()) @@ -844,39 +281,41 @@ export const opportunities = { const items = await prisma.opportunity.findMany({ where: { - ...(opts?.includeClosed ? {} : { closedDate: null }), + ...(opts?.includeClosed ? {} : { closedFlag: false }), OR: [ { name: { contains: query, mode: "insensitive" } }, - { companyName: { contains: query, mode: "insensitive" } }, - { contactName: { contains: query, mode: "insensitive" } }, { notes: { contains: query, mode: "insensitive" } }, - { primarySalesRepName: { contains: query, mode: "insensitive" } }, - { statusName: { contains: query, mode: "insensitive" } }, - { stageName: { contains: query, mode: "insensitive" } }, - ...(numericQuery !== null - ? [{ cwOpportunityId: { equals: numericQuery } }] - : []), + ...(numericQuery !== null ? [{ id: { equals: numericQuery } }] : []), ], }, - include: { company: true }, + include: { + company: { include: { contacts: true, companyAddresses: true } }, + contact: true, + type: true, + status: true, + primarySalesRep: { include: { roles: true } }, + secondarySalesRep: { include: { roles: true } }, + stage: true, + taxCode: true, + }, skip, take: rpp, orderBy: { createdAt: "desc" }, }); - return Promise.all( - items.map(async (item) => { - return new OpportunityController(item, { + return items.map( + (item) => + new OpportunityController(item, { company: item.company - ? await buildCompanyController(item.company, { - strategy: "cache-only", - }) + ? new CompanyController(item.company) : undefined, - activities: await buildActivities(item.cwOpportunityId, { - strategy: "cache-only", - }), - }); - }), + primarySalesRep: item.primarySalesRep + ? new UserController(item.primarySalesRep) + : undefined, + secondarySalesRep: item.secondarySalesRep + ? new UserController(item.secondarySalesRep) + : undefined, + }) ); }, @@ -888,23 +327,20 @@ export const opportunities = { */ async count(opts?: { openOnly?: boolean }): Promise { return prisma.opportunity.count({ - where: opts?.openOnly ? { closedDate: null } : undefined, + where: opts?.openOnly ? { closedFlag: false } : undefined, }); }, /** * Count Search Results * - * Returns the total number of opportunities matching a search query, - * using the same filter logic as `search()`. - * * @param query - Search query string * @param opts - Optional filters * @returns {Promise} */ async searchCount( query: string, - opts?: { includeClosed?: boolean }, + opts?: { includeClosed?: boolean } ): Promise { const numericQuery = /^\d+$/.test(query.trim()) ? Number(query.trim()) @@ -912,18 +348,11 @@ export const opportunities = { return prisma.opportunity.count({ where: { - ...(opts?.includeClosed ? {} : { closedDate: null }), + ...(opts?.includeClosed ? {} : { closedFlag: false }), OR: [ { name: { contains: query, mode: "insensitive" } }, - { companyName: { contains: query, mode: "insensitive" } }, - { contactName: { contains: query, mode: "insensitive" } }, { notes: { contains: query, mode: "insensitive" } }, - { primarySalesRepName: { contains: query, mode: "insensitive" } }, - { statusName: { contains: query, mode: "insensitive" } }, - { stageName: { contains: query, mode: "insensitive" } }, - ...(numericQuery !== null - ? [{ cwOpportunityId: { equals: numericQuery } }] - : []), + ...(numericQuery !== null ? [{ id: { equals: numericQuery } }] : []), ], }, }); @@ -934,38 +363,45 @@ export const opportunities = { * * Fetch all opportunities for a company by its internal company ID. * - * Uses the **cache-only** strategy (same as `fetchPages`). - * * @param companyId - The internal company ID * @param opts - Optional filters * @returns {Promise} */ async fetchByCompany( - companyId: string, - opts?: { includeClosed?: boolean }, + companyId: number, + opts?: { includeClosed?: boolean } ): Promise { const items = await prisma.opportunity.findMany({ where: { companyId, - ...(opts?.includeClosed ? {} : { closedDate: null }), + ...(opts?.includeClosed ? {} : { closedFlag: false }), + }, + include: { + company: { include: { contacts: true, companyAddresses: true } }, + contact: true, + type: true, + status: true, + primarySalesRep: { include: { roles: true } }, + secondarySalesRep: { include: { roles: true } }, + stage: true, + taxCode: true, }, - include: { company: true }, orderBy: { expectedCloseDate: "asc" }, }); - return Promise.all( - items.map(async (item) => { - return new OpportunityController(item, { + return items.map( + (item) => + new OpportunityController(item, { company: item.company - ? await buildCompanyController(item.company, { - strategy: "cache-only", - }) + ? new CompanyController(item.company) : undefined, - activities: await buildActivities(item.cwOpportunityId, { - strategy: "cache-only", - }), - }); - }), + primarySalesRep: item.primarySalesRep + ? new UserController(item.primarySalesRep) + : undefined, + secondarySalesRep: item.secondarySalesRep + ? new UserController(item.secondarySalesRep) + : undefined, + }) ); }, @@ -973,11 +409,7 @@ export const opportunities = { * Fetch Opportunities by User * * Returns all opportunities where the given user (by internal User ID) is - * assigned as the primary or secondary sales rep. Resolves the user's - * ConnectWise member identifier from the DB, then queries opportunities by - * that identifier. - * - * Uses the **cache-only** strategy (same as `fetchPages`). + * assigned as the primary or secondary sales rep. * * @param userId - Internal User `id` (cuid) * @param opts - Optional filters @@ -985,7 +417,7 @@ export const opportunities = { */ async fetchByUser( userId: string, - opts?: { includeClosed?: boolean }, + opts?: { includeClosed?: boolean } ): Promise { const user = await prisma.user.findFirst({ where: { id: userId }, @@ -1001,311 +433,57 @@ export const opportunities = { }); } - if (!user.cwIdentifier) return []; + if (!user.cwIdentifier) { + return []; + } const items = await prisma.opportunity.findMany({ where: { OR: [ - { primarySalesRepIdentifier: user.cwIdentifier }, - { secondarySalesRepIdentifier: user.cwIdentifier }, + { primarySalesRepId: user.cwIdentifier }, + { secondarySalesRepId: user.cwIdentifier }, ], - ...(opts?.includeClosed ? {} : { closedDate: null }), + ...(opts?.includeClosed + ? {} + : { + closedFlag: false, + NOT: { + status: { + OR: [ + { wonFlag: true }, + { lostFlag: true }, + { closeFlag: true }, + ], + }, + }, + }), }, - include: { company: true }, - orderBy: { createdAt: "desc" }, + include: { + company: { include: { contacts: true, companyAddresses: true } }, + contact: true, + type: true, + status: true, + primarySalesRep: { include: { roles: true } }, + secondarySalesRep: { include: { roles: true } }, + stage: true, + taxCode: true, + }, + orderBy: { expectedCloseDate: "asc" }, }); - return Promise.all( - items.map( - async (item) => - new OpportunityController(item, { - company: item.company - ? await buildCompanyController(item.company, { - strategy: "cache-only", - }) - : undefined, - activities: await buildActivities(item.cwOpportunityId, { - strategy: "cache-only", - }), - }), - ), + return items.map( + (item) => + new OpportunityController(item, { + company: item.company + ? new CompanyController(item.company) + : undefined, + primarySalesRep: item.primarySalesRep + ? new UserController(item.primarySalesRep) + : undefined, + secondarySalesRep: item.secondarySalesRep + ? new UserController(item.secondarySalesRep) + : undefined, + }) ); }, - - /** - * Delete Opportunity - * - * Deletes an opportunity from ConnectWise, removes the local database - * record, and invalidates all related Redis caches. - * - * @param identifier - Internal ID (string) or CW opportunity ID (number) - * @returns {Promise} - */ - async deleteItem(identifier: string | number): Promise { - const isNumeric = - typeof identifier === "number" || /^\d+$/.test(String(identifier)); - - const record = await prisma.opportunity.findFirst({ - where: isNumeric - ? { cwOpportunityId: Number(identifier) } - : { id: identifier as string }, - }); - - if (!record) { - throw new GenericError({ - message: "Opportunity not found", - name: "OpportunityNotFound", - cause: `No opportunity exists with identifier '${identifier}'`, - status: 404, - }); - } - - // Delete from ConnectWise first - await opportunityCw.delete(record.cwOpportunityId); - - // Remove the local DB record - await prisma.opportunity.delete({ where: { id: record.id } }); - - // Invalidate all related caches - await invalidateAllOpportunityCaches(record.cwOpportunityId); - }, - - /** - * Upsert Normalized Opportunities - * - * Takes a map of normalized collector opportunities and upserts them - * into the database using the cwOpportunityId as the key. - * - * Internal helper used by `refreshOpportunitiesFromCollector`. - * - * @param normalized - Map of cwOpportunityId → NormalizedCollectorOpportunity - * @returns {Promise} — Number of rows upserted - */ - async upsertNormalizedOpportunities( - normalized: Map, - ): Promise { - let upserted = 0; - - for (const norm of normalized.values()) { - // Resolve the company ID if available - const companyId = norm.companyCwId - ? (( - await prisma.company.findFirst({ - where: { cw_CompanyId: norm.companyCwId }, - select: { id: true }, - }) - )?.id ?? null) - : null; - - // Helper to convert null to undefined for Prisma - const toUndef = (val: T | null | undefined): T | undefined => - val ?? undefined; - - await prisma.opportunity.upsert({ - where: { cwOpportunityId: norm.cwOpportunityId }, - create: { - cwOpportunityId: norm.cwOpportunityId, - name: norm.name, - notes: toUndef(norm.notes), - customerPO: toUndef(norm.customerPO), - source: toUndef(norm.source), - typeName: toUndef(norm.typeName), - typeCwId: toUndef(norm.typeCwId), - stageName: toUndef(norm.stageName), - stageCwId: toUndef(norm.stageCwId), - statusName: toUndef(norm.statusName), - statusCwId: toUndef(norm.statusCwId), - priorityName: toUndef(norm.priorityName), - priorityCwId: toUndef(norm.priorityCwId), - ratingName: toUndef(norm.ratingName), - ratingCwId: toUndef(norm.ratingCwId), - primarySalesRepName: toUndef(norm.primarySalesRepName), - primarySalesRepIdentifier: toUndef(norm.primarySalesRepIdentifier), - primarySalesRepCwId: toUndef(norm.primarySalesRepCwId), - secondarySalesRepName: toUndef(norm.secondarySalesRepName), - secondarySalesRepIdentifier: toUndef( - norm.secondarySalesRepIdentifier, - ), - secondarySalesRepCwId: toUndef(norm.secondarySalesRepCwId), - companyCwId: toUndef(norm.companyCwId), - companyName: toUndef(norm.companyName), - contactCwId: toUndef(norm.contactCwId), - contactName: toUndef(norm.contactName), - siteCwId: toUndef(norm.siteCwId), - siteName: toUndef(norm.siteName), - campaignName: toUndef(norm.campaignName), - campaignCwId: toUndef(norm.campaignCwId), - locationName: toUndef(norm.locationName), - locationCwId: toUndef(norm.locationCwId), - departmentName: toUndef(norm.departmentName), - departmentCwId: toUndef(norm.departmentCwId), - expectedCloseDate: toUndef(norm.expectedCloseDate), - pipelineChangeDate: toUndef(norm.pipelineChangeDate), - dateBecameLead: toUndef(norm.dateBecameLead), - closedDate: toUndef(norm.closedDate), - closedFlag: norm.closedFlag, - closedByName: toUndef(norm.closedByName), - closedByCwId: toUndef(norm.closedByCwId), - totalSalesTax: norm.totalSalesTax, - probability: toUndef(norm.probability), - cwLastUpdated: toUndef(norm.cwLastUpdated), - cwDateEntered: toUndef(norm.cwDateEntered), - companyId, - }, - update: { - name: norm.name, - notes: toUndef(norm.notes), - customerPO: toUndef(norm.customerPO), - source: toUndef(norm.source), - typeName: toUndef(norm.typeName), - typeCwId: toUndef(norm.typeCwId), - stageName: toUndef(norm.stageName), - stageCwId: toUndef(norm.stageCwId), - statusName: toUndef(norm.statusName), - statusCwId: toUndef(norm.statusCwId), - priorityName: toUndef(norm.priorityName), - priorityCwId: toUndef(norm.priorityCwId), - ratingName: toUndef(norm.ratingName), - ratingCwId: toUndef(norm.ratingCwId), - primarySalesRepName: toUndef(norm.primarySalesRepName), - primarySalesRepIdentifier: toUndef(norm.primarySalesRepIdentifier), - primarySalesRepCwId: toUndef(norm.primarySalesRepCwId), - secondarySalesRepName: toUndef(norm.secondarySalesRepName), - secondarySalesRepIdentifier: toUndef( - norm.secondarySalesRepIdentifier, - ), - secondarySalesRepCwId: toUndef(norm.secondarySalesRepCwId), - companyCwId: toUndef(norm.companyCwId), - companyName: toUndef(norm.companyName), - contactCwId: toUndef(norm.contactCwId), - contactName: toUndef(norm.contactName), - siteCwId: toUndef(norm.siteCwId), - siteName: toUndef(norm.siteName), - campaignName: toUndef(norm.campaignName), - campaignCwId: toUndef(norm.campaignCwId), - locationName: toUndef(norm.locationName), - locationCwId: toUndef(norm.locationCwId), - departmentName: toUndef(norm.departmentName), - departmentCwId: toUndef(norm.departmentCwId), - expectedCloseDate: toUndef(norm.expectedCloseDate), - pipelineChangeDate: toUndef(norm.pipelineChangeDate), - dateBecameLead: toUndef(norm.dateBecameLead), - closedDate: toUndef(norm.closedDate), - closedFlag: norm.closedFlag, - closedByName: toUndef(norm.closedByName), - closedByCwId: toUndef(norm.closedByCwId), - totalSalesTax: norm.totalSalesTax, - probability: toUndef(norm.probability), - cwLastUpdated: toUndef(norm.cwLastUpdated), - cwDateEntered: toUndef(norm.cwDateEntered), - companyId, - }, - }); - - upserted++; - } - - return upserted; - }, - - /** - * Refresh Opportunities from Collector - * - * Fetches a batch of opportunities from the collector with optional CW fallback, - * normalizes them to the internal schema, and upserts them into the database. - * - * Data-source strategy: - * - Collector first (dalpuri) if available - * - Falls back to ConnectWise API if collector is unavailable - * - Normalizes the result and upserts into the database - * - * @param opts - Optional configuration - * @param opts.collectorFetch - Function to fetch from collector (signature: (ids?: number[]) => Promise) - * @param opts.limit - Maximum number of opportunities to fetch (default: 500) - * @param opts.skipIds - CW opportunity IDs to skip (e.g., if already processed) - * @returns {Promise<{ upserted: number; fromCollector: boolean; errors?: string[] }>} - */ - async refreshOpportunitiesFromCollector(opts?: { - collectorFetch?: (ids?: number[]) => Promise; - limit?: number; - skipIds?: Set; - }): Promise<{ upserted: number; fromCollector: boolean; errors?: string[] }> { - const limit = opts?.limit ?? 500; - const errors: string[] = []; - - // ── Try collector first, then fall back to CW ────────────────────── - let rawItems: unknown[] = []; - let fromCollector = false; - - if (opts?.collectorFetch) { - try { - rawItems = await opts.collectorFetch(); - fromCollector = true; - console.log( - `[refreshOpportunitiesFromCollector] Fetched ${rawItems.length} opportunities from collector`, - ); - } catch (err) { - console.warn( - `[refreshOpportunitiesFromCollector] Collector fetch failed, falling back to CW: ${err instanceof Error ? err.message : String(err)}`, - ); - errors.push( - `Collector fetch failed: ${err instanceof Error ? err.message : String(err)}`, - ); - } - } - - // If collector didn't provide data, fall back to CW API - if (rawItems.length === 0) { - try { - const cwItemsMap = await opportunityCw.fetchAll(); - // Convert Map values to array - rawItems = Array.from(cwItemsMap.values()); - fromCollector = false; - console.log( - `[refreshOpportunitiesFromCollector] Fetched ${rawItems.length} opportunities from ConnectWise`, - ); - } catch (err) { - console.error( - `[refreshOpportunitiesFromCollector] CW fallback also failed: ${err instanceof Error ? err.message : String(err)}`, - ); - errors.push( - `ConnectWise fallback failed: ${err instanceof Error ? err.message : String(err)}`, - ); - return { upserted: 0, fromCollector: false, errors }; - } - } - - // ── Normalize and upsert ──────────────────────────────────────── - try { - if (fromCollector) { - await seedCollectorOpportunityCaches(rawItems as CollectorOpportunity[]); - } - - const normalized = normalizeCollectorOpportunities( - rawItems as any as CollectorOpportunity[], - ); - - // Filter out skipped IDs - if (opts?.skipIds && opts.skipIds.size > 0) { - for (const id of opts.skipIds) { - normalized.delete(id); - } - } - - const upserted = await this.upsertNormalizedOpportunities(normalized); - - return { - upserted, - fromCollector, - errors: errors.length > 0 ? errors : undefined, - }; - } catch (err) { - const msg = err instanceof Error ? err.message : String(err); - console.error( - `[refreshOpportunitiesFromCollector] Normalization/upsert failed: ${msg}`, - ); - errors.push(`Normalization/upsert failed: ${msg}`); - return { upserted: 0, fromCollector, errors }; - } - }, }; diff --git a/api/src/managers/procurement.ts b/api/src/managers/procurement.ts index ff2c6e2..873ab91 100644 --- a/api/src/managers/procurement.ts +++ b/api/src/managers/procurement.ts @@ -11,10 +11,14 @@ import { /** * Standard include clause used by catalog item queries. - * Includes one level of linked items. + * Includes one level of linked items plus the manufacturer and + * subcategory (with its parent category) relations so the + * CatalogItemController can populate all fields. */ const catalogItemInclude = { linkedItems: true, + manufacturer: true, + subcategory: { include: { category: true } }, } as const; const LABOR_STYLE_CANDIDATES = { @@ -22,8 +26,22 @@ const LABOR_STYLE_CANDIDATES = { tech: ["LABOR & INSTALLATION - TECH", "LABOR - TECH", "LABOR TECH"], } as const; +const DEFAULT_CATALOG_RPP = 30; + +function normalizePositiveInt(value: number, fallback: number): number { + const parsed = Number(value); + if (!Number.isFinite(parsed) || parsed < 1) return fallback; + return Math.floor(parsed); +} + +function normalizeFiniteNumber(value?: number): number | undefined { + if (value === undefined) return undefined; + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : undefined; +} + async function findCatalogByExactCandidates( - candidates: readonly string[], + candidates: readonly string[] ): Promise { for (const candidate of candidates) { const item = await prisma.catalogItem.findFirst({ @@ -44,7 +62,7 @@ async function findCatalogByExactCandidates( } async function findCatalogByLaborStyle( - style: "field" | "tech", + style: "field" | "tech" ): Promise { const fallback = await prisma.catalogItem.findFirst({ where: { @@ -92,6 +110,8 @@ export interface CatalogFilterOpts { */ function buildFilterWhere(opts: CatalogFilterOpts = {}) { const conditions: Record[] = []; + const minPrice = normalizeFiniteNumber(opts.minPrice); + const maxPrice = normalizeFiniteNumber(opts.maxPrice); const parseNumericId = (value?: string): number | null => { if (!value) return null; @@ -131,14 +151,14 @@ function buildFilterWhere(opts: CatalogFilterOpts = {}) { if (opts.category) { if (categoryId) { const categoryOr: Record[] = [ - { categoryCwId: categoryId }, + { subcategory: { is: { category: { is: { id: categoryId } } } } }, ]; if (resolvedCategoryName) { - categoryOr.push({ category: resolvedCategoryName }); + categoryOr.push({ subcategory: { is: { category: { is: { name: resolvedCategoryName } } } } }); } conditions.push({ OR: categoryOr }); } else { - conditions.push({ category: opts.category }); + conditions.push({ subcategory: { is: { category: { is: { name: opts.category } } } } }); } } @@ -146,40 +166,43 @@ function buildFilterWhere(opts: CatalogFilterOpts = {}) { if (subcategoryId) { const resolvedSubcategoryName = resolveSubcategoryNameById(subcategoryId); const subcategoryOr: Record[] = [ - { subcategoryCwId: subcategoryId }, + { subcategory: { is: { id: subcategoryId } } }, ]; if (resolvedSubcategoryName) { - subcategoryOr.push({ subcategory: resolvedSubcategoryName }); + subcategoryOr.push({ subcategory: { is: { name: resolvedSubcategoryName } } }); } conditions.push({ OR: subcategoryOr }); } else { - conditions.push({ subcategory: opts.subcategory }); + conditions.push({ subcategory: { is: { name: opts.subcategory } } }); } } if (opts.group && opts.category) { if (!resolvedCategoryName) { - conditions.push({ category: "__unknown_category__" }); + conditions.push({ subcategory: { is: { category: { is: { name: "__unknown_category__" } } } } }); } if (resolvedCategoryName) { const subcats = getSubcategoriesForGroup( resolvedCategoryName, - opts.group, + opts.group ); if (subcats.length > 0) { - conditions.push({ subcategory: { in: subcats } }); + conditions.push({ subcategory: { is: { name: { in: subcats } } } }); } } } else if (opts.group && !opts.category) { // Try to find the group in any category - const { - CATEGORY_TREE, - isCategoryGroup, - } = require("../modules/catalog-categories/catalogCategories"); for (const cat of CATEGORY_TREE) { const subcats = getSubcategoriesForGroup(cat.name, opts.group); if (subcats.length > 0) { - conditions.push({ category: cat.name, subcategory: { in: subcats } }); + conditions.push({ + subcategory: { + is: { + name: { in: subcats }, + category: { is: { name: cat.name } }, + }, + }, + }); break; } } @@ -187,20 +210,32 @@ function buildFilterWhere(opts: CatalogFilterOpts = {}) { if (opts.manufacturer) { conditions.push({ - manufacturer: { contains: opts.manufacturer, mode: "insensitive" }, + manufacturer: { + is: { + name: { contains: opts.manufacturer, mode: "insensitive" }, + }, + }, }); } if (opts.ecosystem) { const eco = ECOSYSTEM_TREE.find( - (e) => e.name.toLowerCase() === opts.ecosystem!.toLowerCase(), + (e) => e.name.toLowerCase() === opts.ecosystem!.toLowerCase() ); if (eco && eco.manufacturers.length > 0) { conditions.push({ OR: eco.manufacturers.map((m) => ({ - manufacturer: { contains: m.name, mode: "insensitive" as const }, - subcategory: { startsWith: m.subcategoryPrefix }, - category: m.category, + manufacturer: { + is: { + name: { contains: m.name, mode: "insensitive" as const }, + }, + }, + subcategory: { + is: { + name: { startsWith: m.subcategoryPrefix }, + category: { is: { name: m.category } }, + }, + }, })), }); } @@ -210,12 +245,12 @@ function buildFilterWhere(opts: CatalogFilterOpts = {}) { conditions.push({ onHand: { gt: 0 } }); } - if (opts.minPrice !== undefined) { - conditions.push({ price: { gte: opts.minPrice } }); + if (minPrice !== undefined) { + conditions.push({ price: { gte: minPrice } }); } - if (opts.maxPrice !== undefined) { - conditions.push({ price: { lte: opts.maxPrice } }); + if (maxPrice !== undefined) { + conditions.push({ price: { lte: maxPrice } }); } return conditions.length > 0 ? { AND: conditions } : undefined; @@ -237,10 +272,10 @@ export const procurement = { const item = await prisma.catalogItem.findFirst({ where: isNumeric - ? { cwCatalogId: Number(identifier) } + ? { id: Number(identifier) } : { OR: [ - { id: identifier as string }, + { uid: identifier as string }, { identifier: identifier as string }, ], }, @@ -302,10 +337,12 @@ export const procurement = { async fetchPages( page: number, rpp: number, - opts?: CatalogFilterOpts, + opts?: CatalogFilterOpts ): Promise { - const skip = (Math.max(page, 1) - 1) * rpp; - const take = rpp; + const safePage = normalizePositiveInt(page, 1); + const safeRpp = normalizePositiveInt(rpp, DEFAULT_CATALOG_RPP); + const skip = (safePage - 1) * safeRpp; + const take = safeRpp; const items = await prisma.catalogItem.findMany({ where: buildFilterWhere(opts), @@ -334,10 +371,12 @@ export const procurement = { query: string, page: number, rpp: number, - opts?: CatalogFilterOpts, + opts?: CatalogFilterOpts ): Promise { - const skip = (Math.max(page, 1) - 1) * rpp; - const take = rpp; + const safePage = normalizePositiveInt(page, 1); + const safeRpp = normalizePositiveInt(rpp, DEFAULT_CATALOG_RPP); + const skip = (safePage - 1) * safeRpp; + const take = safeRpp; const filterWhere = buildFilterWhere(opts) ?? {}; @@ -350,7 +389,11 @@ export const procurement = { { description: { contains: query, mode: "insensitive" } }, { partNumber: { contains: query, mode: "insensitive" } }, { vendorSku: { contains: query, mode: "insensitive" } }, - { manufacturer: { contains: query, mode: "insensitive" } }, + { + manufacturer: { + is: { name: { contains: query, mode: "insensitive" } }, + }, + }, ], }, skip, @@ -371,7 +414,7 @@ export const procurement = { * @returns {Promise} - Total count */ async count( - opts?: CatalogFilterOpts & { activeOnly?: boolean }, + opts?: CatalogFilterOpts & { activeOnly?: boolean } ): Promise { // Support legacy `activeOnly` flag by mapping it to `includeInactive` const filterOpts: CatalogFilterOpts = { @@ -407,7 +450,11 @@ export const procurement = { { description: { contains: query, mode: "insensitive" } }, { partNumber: { contains: query, mode: "insensitive" } }, { vendorSku: { contains: query, mode: "insensitive" } }, - { manufacturer: { contains: query, mode: "insensitive" } }, + { + manufacturer: { + is: { name: { contains: query, mode: "insensitive" } }, + }, + }, ], }, }); @@ -425,18 +472,39 @@ export const procurement = { */ async fetchDistinctValues( field: "category" | "subcategory" | "manufacturer", - opts?: CatalogFilterOpts, + opts?: CatalogFilterOpts ): Promise { + if (field === "manufacturer") { + const items = await prisma.catalogItem.findMany({ + where: buildFilterWhere(opts), + select: { manufacturer: { select: { name: true } } }, + }); + const names = items + .map((item) => item.manufacturer?.name ?? null) + .filter((v): v is string => v !== null); + return [...new Set(names)].sort(); + } + + if (field === "subcategory") { + const items = await prisma.catalogItem.findMany({ + where: buildFilterWhere(opts), + select: { subcategory: { select: { name: true } } }, + }); + const names = items + .map((item) => item.subcategory?.name ?? null) + .filter((v): v is string => v !== null); + return [...new Set(names)].sort(); + } + + // field === "category" const items = await prisma.catalogItem.findMany({ where: buildFilterWhere(opts), - select: { [field]: true }, - distinct: [field], - orderBy: { [field]: "asc" }, + select: { subcategory: { select: { category: { select: { name: true } } } } }, }); - - return items - .map((item: Record) => item[field] as string | null) + const names = items + .map((item) => item.subcategory?.category?.name ?? null) .filter((v): v is string => v !== null); + return [...new Set(names)].sort(); }, /** @@ -450,7 +518,7 @@ export const procurement = { */ async linkItems( sourceIdentifier: string | number, - targetIdentifier: string | number, + targetIdentifier: string | number ): Promise { const source = await procurement.fetchItem(sourceIdentifier); const target = await procurement.fetchItem(targetIdentifier); @@ -469,7 +537,7 @@ export const procurement = { */ async unlinkItems( sourceIdentifier: string | number, - targetIdentifier: string | number, + targetIdentifier: string | number ): Promise { const source = await procurement.fetchItem(sourceIdentifier); const target = await procurement.fetchItem(targetIdentifier); diff --git a/api/src/managers/schedules.ts b/api/src/managers/schedules.ts new file mode 100644 index 0000000..b24f7ca --- /dev/null +++ b/api/src/managers/schedules.ts @@ -0,0 +1,92 @@ +import { prisma } from "../constants"; +import { ScheduleController } from "../controllers/ScheduleController"; + +const scheduleIncludes = { + status: true, + type: true, + scheduleSpan: true, +} as const; + +export const schedules = { + async fetch(identifier: string | number): Promise { + const isNumeric = + typeof identifier === "number" || /^\d+$/.test(String(identifier)); + + const schedule = await prisma.schedule.findFirst({ + where: isNumeric + ? { id: Number(identifier) } + : { uid: String(identifier) }, + include: scheduleIncludes, + }); + + if (!schedule) throw new Error("Unknown schedule."); + + return new ScheduleController(schedule); + }, + + async count() { + return await prisma.schedule.count(); + }, + + async fetchPages(page: number, rpp: number) { + page = page.valueOf(); + rpp = rpp.valueOf(); + + const skip = (page > 1 ? page : 0) * rpp; + const take = rpp ?? 30; + + const data = await prisma.schedule.findMany({ + skip, + take, + include: scheduleIncludes, + orderBy: { startDate: "desc" }, + }); + + return data.map((s) => new ScheduleController(s)); + }, + + async search(query: string, page: number, rpp: number) { + page = page.valueOf(); + rpp = rpp.valueOf(); + + const skip = (page > 1 ? page : 0) * rpp; + const take = rpp ?? 30; + + const numericQuery = parseInt(query, 10); + + const data = await prisma.schedule.findMany({ + where: { + OR: [ + { name: { contains: query, mode: "insensitive" } }, + { description: { contains: query, mode: "insensitive" } }, + { uid: { contains: query, mode: "insensitive" } }, + ...(!isNaN(numericQuery) ? [{ id: numericQuery }] : []), + ], + }, + skip, + take, + include: scheduleIncludes, + orderBy: { startDate: "desc" }, + }); + + return data.map((s) => new ScheduleController(s)); + }, + + async fetchByMemberDateRange( + memberId: string, + startDate: Date, + endDate: Date + ) { + const data = await prisma.schedule.findMany({ + where: { + memberId, + startDate: { gte: startDate }, + endDate: { lte: endDate }, + }, + include: scheduleIncludes, + orderBy: { startDate: "asc" }, + }); + + return data.map((s) => new ScheduleController(s)); + }, +}; diff --git a/api/src/managers/users.ts b/api/src/managers/users.ts index 5ddb91e..570dcfc 100644 --- a/api/src/managers/users.ts +++ b/api/src/managers/users.ts @@ -1,10 +1,8 @@ -import { ms } from "zod/locales"; import { User } from "../../generated/prisma/client"; import { prisma } from "../constants"; import { SessionTokensObject } from "../controllers/SessionController"; import UserController from "../controllers/UserController"; import { fetchMicrosoftUser } from "../modules/fetchMicrosoftUser"; -import { findCwIdentifierByEmail } from "../modules/cw-utils/members/fetchAllMembers"; import { events } from "../modules/globalEvents"; import { sessions } from "./sessions"; import * as msal from "@azure/msal-node"; @@ -22,7 +20,7 @@ export const users = { * @param authRequest - The code supplied in the callback url of the Microsoft oAuth transaction */ async authenticate( - authRequest: msal.AuthenticationResult, + authRequest: msal.AuthenticationResult ): Promise { let id = authRequest.uniqueId as string; @@ -63,7 +61,7 @@ export const users = { email: string; login: string; userId: string; - }>, + }> ) { if (Object.keys(identifier).length == 0) return null; const userData = await prisma.user.findFirst({ @@ -90,18 +88,45 @@ export const users = { */ async createUser(token: string): Promise { const msData = await fetchMicrosoftUser(token); + const resolvedEmail = (msData.mail ?? msData.userPrincipalName ?? "") + .trim() + .toLowerCase(); + + if (!resolvedEmail) { + throw new Error("Microsoft account did not include an email address."); + } + + const resolvedLogin = (msData.userPrincipalName ?? resolvedEmail) + .trim() + .toLowerCase(); // Attempt to resolve the user's ConnectWise identifier by email - const cwIdentifier = await findCwIdentifierByEmail(msData.mail).catch( - () => null, - ); + const cwIdentifier = await prisma.cwMember + .findFirst({ where: { officeEmail: resolvedEmail } }) + .then((m) => m?.identifier ?? null) + .catch(() => null); - const newUser = await prisma.user.create({ - data: { + const existingUser = await prisma.user.findUnique({ + where: { email: resolvedEmail }, + select: { id: true }, + }); + + const newUser = await prisma.user.upsert({ + where: { email: resolvedEmail }, + create: { userId: msData.id, - email: msData.mail ?? msData.userPrincipalName, - name: `${msData.givenName} ${msData.surname}`, - login: msData.userPrincipalName, + email: resolvedEmail, + firstName: msData.givenName ?? null, + lastName: msData.surname ?? null, + login: resolvedLogin, + cwIdentifier, + token, + }, + update: { + userId: msData.id, + firstName: msData.givenName ?? null, + lastName: msData.surname ?? null, + login: resolvedLogin, cwIdentifier, token, }, @@ -109,7 +134,9 @@ export const users = { }); let controller = new UserController(newUser); - events.emit("user:created", controller); + if (!existingUser) { + events.emit("user:created", controller); + } return controller; }, diff --git a/api/src/modules/algorithms/computeCacheTTL.ts b/api/src/modules/algorithms/computeCacheTTL.ts deleted file mode 100644 index 63d06f5..0000000 --- a/api/src/modules/algorithms/computeCacheTTL.ts +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @module computeCacheTTL - * - * Adaptive Cache TTL Algorithm - * ============================ - * - * Determines how long a cached record should live before it must be - * re-fetched from the upstream source (e.g. ConnectWise API). - * - * The algorithm prioritises freshness for records that are actively - * being worked on, while avoiding unnecessary API calls for stale or - * inactive data. - * - * ## Spec - * - * | # | Condition | TTL (ms) | TTL (human) | Rationale | - * |---|------------------------------------------------------------------|----------|-------------|--------------------------------------------------------------------| - * | 1 | `closedFlag` is `true` | `null` | Do not cache| Closed records are rarely accessed; caching wastes memory. | - * | 2 | `expectedCloseDate` OR `lastUpdated` is within the last **5 days**| 60 000 | 60 seconds | High-activity window — data changes frequently and must stay fresh.| - * | 3 | `expectedCloseDate` OR `lastUpdated` is within the last **14 days**| 90 000 | 90 seconds | Moderate activity — still relevant, but changes less often. | - * | 4 | Everything else (older than 14 days) | 900 000 | 15 minutes | Low activity — safe to serve from cache for longer. | - * - * ## Evaluation order - * - * Rules are evaluated **top-to-bottom**; the first matching rule wins. - * Rule 2 (5-day window) is a subset of Rule 3 (14-day window), so it - * must be checked first. - * - * ## Inputs - * - * | Field | Type | Description | - * |--------------------|------------------|--------------------------------------------------------------------| - * | `closedFlag` | `boolean` | Whether the record is closed / inactive. | - * | `expectedCloseDate`| `Date \| null` | The projected close date (future-looking relevance signal). | - * | `lastUpdated` | `Date \| null` | The last time the upstream record was modified (backward-looking). | - * | `now` | `Date` (optional)| Override for the current timestamp; defaults to `new Date()`. | - * - * ## Output - * - * Returns `number | null`: - * - A positive integer representing the TTL in **milliseconds**, or - * - `null` when the record should **not** be cached at all. - * - * ## Usage - * - * ```ts - * import { computeCacheTTL } from "../modules/algorithms/computeCacheTTL"; - * - * const ttl = computeCacheTTL({ - * closedFlag: opportunity.closedFlag, - * expectedCloseDate: opportunity.expectedCloseDate, - * lastUpdated: opportunity.cwLastUpdated, - * }); - * - * if (ttl !== null) { - * await redis.set(key, serialised, "PX", ttl); - * } - * ``` - */ - -// --------------------------------------------------------------------------- -// Constants -// --------------------------------------------------------------------------- - -/** 60 seconds – TTL for high-activity records (within 5 days). - * Must exceed the 30-second background refresh interval so the cache - * stays warm between cycles. */ -export const TTL_HIGH_ACTIVITY = 60_000; - -/** 90 seconds – TTL for moderate-activity records (within 14 days). */ -export const TTL_MODERATE_ACTIVITY = 90_000; - -/** 15 minutes – TTL for low-activity / stale records. */ -export const TTL_LOW_ACTIVITY = 900_000; - -/** 30 days in milliseconds. */ -const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000; - -/** 5 days in milliseconds. */ -const FIVE_DAYS_MS = 5 * 24 * 60 * 60 * 1000; - -/** 14 days in milliseconds. */ -const FOURTEEN_DAYS_MS = 14 * 24 * 60 * 60 * 1000; - -// --------------------------------------------------------------------------- -// Input type -// --------------------------------------------------------------------------- - -export interface CacheTTLInput { - /** Whether the record is closed / inactive. */ - closedFlag: boolean; - /** When the record was closed — used for recently-closed caching (within 30 days). */ - closedDate: Date | null; - /** The projected close date — serves as a forward-looking relevance signal. */ - expectedCloseDate: Date | null; - /** The date the upstream record was last modified — backward-looking signal. */ - lastUpdated: Date | null; - /** - * Override for the current timestamp. - * Useful for deterministic testing. Defaults to `new Date()`. - */ - now?: Date; -} - -// --------------------------------------------------------------------------- -// Algorithm -// --------------------------------------------------------------------------- - -/** - * Compute the cache TTL for a record based on its activity signals. - * - * @param input - The record's activity signals. See {@link CacheTTLInput}. - * @returns The TTL in milliseconds, or `null` if the record should not be cached. - * - * @see Module-level JSDoc for the full spec table and evaluation rules. - */ -export function computeCacheTTL(input: CacheTTLInput): number | null { - const { - closedFlag, - closedDate, - expectedCloseDate, - lastUpdated, - now = new Date(), - } = input; - - const nowMs = now.getTime(); - - /** - * Check whether a date falls within a window around `now`. - * - * "Within" means the date is between `now - windowMs` and `now + windowMs`, - * allowing both past updates and future-scheduled dates to qualify. - */ - const isWithinWindow = (date: Date | null, windowMs: number): boolean => { - if (!date) return false; - const diff = Math.abs(nowMs - date.getTime()); - return diff <= windowMs; - }; - - // Rule 1 — Closed records - if (closedFlag) { - // Rule 1b — Recently closed (within 30 days) → cache at low-activity TTL - if (isWithinWindow(closedDate, THIRTY_DAYS_MS)) { - return TTL_LOW_ACTIVITY; - } - // Rule 1a — Closed longer than 30 days → do not cache - return null; - } - - // Rule 2 — High activity (5 days) - if ( - isWithinWindow(expectedCloseDate, FIVE_DAYS_MS) || - isWithinWindow(lastUpdated, FIVE_DAYS_MS) - ) { - return TTL_HIGH_ACTIVITY; - } - - // Rule 3 — Moderate activity (14 days) - if ( - isWithinWindow(expectedCloseDate, FOURTEEN_DAYS_MS) || - isWithinWindow(lastUpdated, FOURTEEN_DAYS_MS) - ) { - return TTL_MODERATE_ACTIVITY; - } - - // Rule 4 — Low activity / stale - return TTL_LOW_ACTIVITY; -} diff --git a/api/src/modules/algorithms/computeProductsCacheTTL.ts b/api/src/modules/algorithms/computeProductsCacheTTL.ts deleted file mode 100644 index b75b3cb..0000000 --- a/api/src/modules/algorithms/computeProductsCacheTTL.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * @module computeProductsCacheTTL - * - * Adaptive Cache TTL for Opportunity Products - * ============================================ - * - * Determines how long products (forecast items) should be cached in - * Redis before being re-fetched from ConnectWise. - * - * Products have unique caching rules compared to notes or contacts - * because they are typically finalised before a deal closes and do not - * change once the opportunity reaches a terminal status. - * - * ## Spec - * - * | # | Condition | TTL (ms) | TTL (human) | Rationale | - * |---|------------------------------------------------------------------------------|------------|-------------|---------------------------------------------------------------------------------------| - * | 1 | Status is **Won**, **Lost**, **Pending Won**, or **Pending Lost** | `null` | No cache | Products on terminal / near-terminal opps are static; no need to keep them warm. | - * | 2 | Opportunity is **not cacheable** (main cache TTL is `null`) | `null` | No cache | If the opp itself is evicted, sub-resources follow suit. | - * | 3 | `lastUpdated` is within the last **3 days** | 15 000 | 15 seconds | Actively-worked deals — products are being edited and need near-real-time freshness. | - * | 4 | Everything else | 1 200 000 | 20 minutes | Lazy on-demand cache: fetched when requested, expires after 20 min without refresh. | - * - * ## Evaluation order - * - * Rules are evaluated top-to-bottom; the first matching rule wins. - * - * ## Inputs - * - * Extends {@link CacheTTLInput} from `computeCacheTTL` with an - * additional `statusCwId` field used to identify terminal statuses. - * - * ## Output - * - * Returns `number | null`: - * - Positive integer = TTL in **milliseconds**. - * - `null` = do **not** cache. - */ - -import type { CacheTTLInput } from "./computeCacheTTL"; -import { computeCacheTTL } from "./computeCacheTTL"; -import { QUOTE_STATUSES } from "../../types/QuoteStatuses"; - -// --------------------------------------------------------------------------- -// Constants -// --------------------------------------------------------------------------- - -/** 45 seconds — TTL for hot products (opportunity updated within 3 days). - * Must exceed the 30-second background refresh interval so the cache - * stays warm between cycles. */ -export const PRODUCTS_TTL_HOT = 45_000; - -/** 20 minutes — TTL for on-demand product cache (lazy fallback). */ -export const PRODUCTS_TTL_LAZY = 1_200_000; - -/** 3 days in milliseconds. */ -const THREE_DAYS_MS = 3 * 24 * 60 * 60 * 1000; - -/** - * Set of all CW status IDs that map to a Won or Lost canonical status. - * - * Built at module load from {@link QUOTE_STATUSES} so it stays in sync - * with any future status additions. - */ -export const WON_LOST_STATUS_IDS: ReadonlySet = new Set( - QUOTE_STATUSES.filter((s) => s.wonFlag || s.lostFlag).flatMap((s) => [ - s.id, - ...s.optimaEquivalency, - ]), -); - -// --------------------------------------------------------------------------- -// Input type -// --------------------------------------------------------------------------- - -export interface ProductsCacheTTLInput extends CacheTTLInput { - /** The CW status ID of the opportunity. */ - statusCwId: number | null; -} - -// --------------------------------------------------------------------------- -// Algorithm -// --------------------------------------------------------------------------- - -/** - * Compute the cache TTL for an opportunity's products. - * - * @param input - The opportunity's activity signals plus status ID. - * @returns TTL in milliseconds, or `null` if products should not be cached. - */ -export function computeProductsCacheTTL( - input: ProductsCacheTTLInput, -): number | null { - const { statusCwId, lastUpdated, now = new Date() } = input; - - // Rule 1 — Terminal statuses: Won / Lost / Pending Won / Pending Lost - if (statusCwId !== null && WON_LOST_STATUS_IDS.has(statusCwId)) { - return null; - } - - // Rule 2 — If the opportunity itself is not cacheable, skip products too - const mainTTL = computeCacheTTL(input); - if (mainTTL === null) { - return null; - } - - // Rule 3 — Hot: updated within the last 3 days - if (lastUpdated) { - const diff = Math.abs(now.getTime() - lastUpdated.getTime()); - if (diff <= THREE_DAYS_MS) { - return PRODUCTS_TTL_HOT; - } - } - - // Rule 4 — Lazy fallback - return PRODUCTS_TTL_LAZY; -} diff --git a/api/src/modules/algorithms/computeSubResourceCacheTTL.ts b/api/src/modules/algorithms/computeSubResourceCacheTTL.ts deleted file mode 100644 index 1c5a4e5..0000000 --- a/api/src/modules/algorithms/computeSubResourceCacheTTL.ts +++ /dev/null @@ -1,118 +0,0 @@ -/** - * @module computeSubResourceCacheTTL - * - * Adaptive Cache TTL for Opportunity Sub-Resources - * ================================================= - * - * Determines how long cached sub-resource data (notes, contacts) should - * live before being re-fetched from ConnectWise. - * - * Sub-resources change less frequently than the opportunity record itself - * or its activity feed, so TTLs are longer than the primary cache. The - * same activity-signal heuristics are used (expected close date, last - * updated, closed status) but with relaxed durations. - * - * ## Spec - * - * | # | Condition | TTL (ms) | TTL (human) | Rationale | - * |---|-------------------------------------------------------------------|----------|-------------|--------------------------------------------------------------------| - * | 1 | `closedFlag` is `true` AND closed > 30 days ago | `null` | Do not cache| Old closed records are rarely accessed. | - * | 1b| `closedFlag` is `true` AND closed within 30 days | 300 000 | 5 minutes | Recently-closed records may still be viewed occasionally. | - * | 2 | `expectedCloseDate` OR `lastUpdated` within **5 days** | 60 000 | 60 seconds | Active deals — contacts/notes may still change. | - * | 3 | `expectedCloseDate` OR `lastUpdated` within **14 days** | 120 000 | 2 minutes | Moderate activity — less likely to change. | - * | 4 | Everything else (older than 14 days) | 300 000 | 5 minutes | Low activity — safe to cache longer. | - * - * ## Evaluation order - * - * Rules are evaluated top-to-bottom; the first matching rule wins. - * - * ## Inputs - * - * Uses the same {@link CacheTTLInput} interface as `computeCacheTTL`. - * - * ## Output - * - * Returns `number | null`: - * - Positive integer = TTL in **milliseconds**. - * - `null` = do **not** cache. - */ - -import type { CacheTTLInput } from "./computeCacheTTL"; - -// --------------------------------------------------------------------------- -// Constants -// --------------------------------------------------------------------------- - -/** 60 seconds — TTL for high-activity sub-resources (within 5 days). */ -export const SUB_TTL_HIGH_ACTIVITY = 60_000; - -/** 2 minutes — TTL for moderate-activity sub-resources (within 14 days). */ -export const SUB_TTL_MODERATE_ACTIVITY = 120_000; - -/** 5 minutes — TTL for low-activity / stale sub-resources. */ -export const SUB_TTL_LOW_ACTIVITY = 300_000; - -/** 30 days in milliseconds. */ -const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000; - -/** 5 days in milliseconds. */ -const FIVE_DAYS_MS = 5 * 24 * 60 * 60 * 1000; - -/** 14 days in milliseconds. */ -const FOURTEEN_DAYS_MS = 14 * 24 * 60 * 60 * 1000; - -// --------------------------------------------------------------------------- -// Algorithm -// --------------------------------------------------------------------------- - -/** - * Compute the cache TTL for an opportunity sub-resource (notes, contacts). - * - * @param input - The opportunity's activity signals. See {@link CacheTTLInput}. - * @returns The TTL in milliseconds, or `null` if the data should not be cached. - */ -export function computeSubResourceCacheTTL( - input: CacheTTLInput, -): number | null { - const { - closedFlag, - closedDate, - expectedCloseDate, - lastUpdated, - now = new Date(), - } = input; - - const nowMs = now.getTime(); - - const isWithinWindow = (date: Date | null, windowMs: number): boolean => { - if (!date) return false; - return Math.abs(nowMs - date.getTime()) <= windowMs; - }; - - // Rule 1 — Closed records - if (closedFlag) { - if (isWithinWindow(closedDate, THIRTY_DAYS_MS)) { - return SUB_TTL_LOW_ACTIVITY; - } - return null; - } - - // Rule 2 — High activity (5 days) - if ( - isWithinWindow(expectedCloseDate, FIVE_DAYS_MS) || - isWithinWindow(lastUpdated, FIVE_DAYS_MS) - ) { - return SUB_TTL_HIGH_ACTIVITY; - } - - // Rule 3 — Moderate activity (14 days) - if ( - isWithinWindow(expectedCloseDate, FOURTEEN_DAYS_MS) || - isWithinWindow(lastUpdated, FOURTEEN_DAYS_MS) - ) { - return SUB_TTL_MODERATE_ACTIVITY; - } - - // Rule 4 — Low activity / stale - return SUB_TTL_LOW_ACTIVITY; -} diff --git a/api/src/modules/cache/opportunityCache.ts b/api/src/modules/cache/opportunityCache.ts deleted file mode 100644 index 877599f..0000000 --- a/api/src/modules/cache/opportunityCache.ts +++ /dev/null @@ -1,659 +0,0 @@ -/** - * @module opportunityCache - * - * Redis-backed cache for expensive ConnectWise API data associated - * with opportunities. - * - * ## What is cached - * - * Each non-closed opportunity may have cached payloads keyed by its `cwOpportunityId`: - * - * - **Activities** (`opp:activities:{cwOpportunityId}`) — the raw `CWActivity[]` array - * - **Company CW data** (`opp:company-cw:{cw_CompanyId}`) — hydrated company / contacts blob - * - **Notes** (`opp:notes:{cwOpportunityId}`) — raw CW notes array - * - **Contacts** (`opp:contacts:{cwOpportunityId}`) — raw CW contacts array - * - **Products** (`opp:products:{cwOpportunityId}`) — raw CW forecast + procurement products blob - * - * TTLs are computed dynamically via {@link computeCacheTTL}. - * - * ## Background refresh (Worker-based) - * - * **⚠️ This module is now READ-ONLY.** Cache refresh logic has been moved to workers: - * - * - {@link refreshActiveOpportunitiesWorker} — Scheduled to run every 20 minutes - * to run a unified cache pass across all opportunities. Active/recent records - * use adaptive TTLs and archived records use {@link TTL_ARCHIVED_MS}. - * - * See `src/modules/workers/cache/` for worker implementations. - * - * ## This module now provides - * - * - `getCached*()` functions for reading cached data - * - `fetchAndCache*()` functions used internally by workers - * - `invalidate*()` functions for cache invalidation after mutations - * - Cache key helpers for Redis operations - */ - -import { prisma, redis } from "../../constants"; -import { activityCw } from "../cw-utils/activities/activities"; -import { computeCacheTTL } from "../algorithms/computeCacheTTL"; -import { computeSubResourceCacheTTL } from "../algorithms/computeSubResourceCacheTTL"; -import { - computeProductsCacheTTL, - PRODUCTS_TTL_HOT, -} from "../algorithms/computeProductsCacheTTL"; -import { connectWiseApi } from "../../constants"; -import { fetchCwCompanyById } from "../cw-utils/fetchCompany"; -import { fetchCompanySite } from "../cw-utils/sites/companySites"; -import { opportunityCw } from "../cw-utils/opportunities/opportunities"; -import { withCwRetry } from "../cw-utils/withCwRetry"; -import { events } from "../globalEvents"; - -// --------------------------------------------------------------------------- -// Key helpers -// --------------------------------------------------------------------------- - -const ACTIVITY_PREFIX = "opp:activities:"; -const COMPANY_CW_PREFIX = "opp:company-cw:"; -const NOTES_PREFIX = "opp:notes:"; -const CONTACTS_PREFIX = "opp:contacts:"; -const PRODUCTS_PREFIX = "opp:products:"; -const SITE_PREFIX = "opp:site:"; -const OPP_CW_PREFIX = "opp:cw-data:"; - -/** Redis key for cached activities by CW opportunity ID. */ -export const activityCacheKey = (cwOppId: number) => - `${ACTIVITY_PREFIX}${cwOppId}`; - -/** Redis key for cached company CW hydration data by CW company ID. */ -export const companyCwCacheKey = (cwCompanyId: number) => - `${COMPANY_CW_PREFIX}${cwCompanyId}`; - -/** Redis key for cached opportunity notes by CW opportunity ID. */ -export const notesCacheKey = (cwOppId: number) => `${NOTES_PREFIX}${cwOppId}`; - -/** Redis key for cached opportunity contacts by CW opportunity ID. */ -export const contactsCacheKey = (cwOppId: number) => - `${CONTACTS_PREFIX}${cwOppId}`; - -/** Redis key for cached opportunity products by CW opportunity ID. */ -export const productsCacheKey = (cwOppId: number) => - `${PRODUCTS_PREFIX}${cwOppId}`; - -/** Redis key for cached company site by CW company ID + site ID. */ -export const siteCacheKey = (cwCompanyId: number, cwSiteId: number) => - `${SITE_PREFIX}${cwCompanyId}:${cwSiteId}`; - -/** Redis key for cached CW opportunity response by CW opportunity ID. */ -export const oppCwDataCacheKey = (cwOppId: number) => - `${OPP_CW_PREFIX}${cwOppId}`; - -// --------------------------------------------------------------------------- -// Read helpers -// --------------------------------------------------------------------------- - -/** - * Retrieve cached CW activities for an opportunity. - * - * @returns The parsed `CWActivity[]` or `null` on cache miss. - */ -export async function getCachedActivities( - cwOpportunityId: number, -): Promise { - const raw = await redis.get(activityCacheKey(cwOpportunityId)); - if (!raw) return null; - try { - return JSON.parse(raw); - } catch { - return null; - } -} - -/** - * Retrieve cached company CW hydration data. - * - * @returns `{ company, defaultContact, allContacts }` or `null` on cache miss. - */ -export async function getCachedCompanyCwData( - cwCompanyId: number, -): Promise<{ company: any; defaultContact: any; allContacts: any[] } | null> { - const raw = await redis.get(companyCwCacheKey(cwCompanyId)); - if (!raw) return null; - try { - return JSON.parse(raw); - } catch { - return null; - } -} - -/** - * Retrieve cached opportunity notes (raw CW data). - * - * @returns The parsed raw CW notes array or `null` on cache miss. - */ -export async function getCachedNotes( - cwOpportunityId: number, -): Promise { - const raw = await redis.get(notesCacheKey(cwOpportunityId)); - if (!raw) return null; - try { - return JSON.parse(raw); - } catch { - return null; - } -} - -/** - * Retrieve cached opportunity contacts (raw CW data). - * - * @returns The parsed raw CW contacts array or `null` on cache miss. - */ -export async function getCachedContacts( - cwOpportunityId: number, -): Promise { - const raw = await redis.get(contactsCacheKey(cwOpportunityId)); - if (!raw) return null; - try { - return JSON.parse(raw); - } catch { - return null; - } -} - -/** - * Retrieve cached opportunity products (raw CW forecast + procurement blob). - * - * @returns `{ forecast, procProducts }` or `null` on cache miss. - */ -export async function getCachedProducts( - cwOpportunityId: number, -): Promise<{ forecast: any; procProducts: any[] } | null> { - const raw = await redis.get(productsCacheKey(cwOpportunityId)); - if (!raw) return null; - try { - return JSON.parse(raw); - } catch { - return null; - } -} - -/** - * Retrieve cached CW site data for a company/site pair. - * - * @returns Parsed site data or `null` on cache miss. - */ -export async function getCachedSite( - cwCompanyId: number, - cwSiteId: number, -): Promise { - const raw = await redis.get(siteCacheKey(cwCompanyId, cwSiteId)); - if (!raw) return null; - try { - return JSON.parse(raw); - } catch { - return null; - } -} - -/** - * Retrieve cached CW opportunity response data. - * - * @returns Parsed CW opportunity object or `null` on cache miss. - */ -export async function getCachedOppCwData( - cwOpportunityId: number, -): Promise { - const raw = await redis.get(oppCwDataCacheKey(cwOpportunityId)); - if (!raw) return null; - try { - return JSON.parse(raw); - } catch { - return null; - } -} - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -/** Check whether an error is an Axios 404 (resource not found in CW). */ -function isNotFoundError(err: unknown): boolean { - if (typeof err !== "object" || err === null) return false; - const e = err as Record; - return e.isAxiosError === true && e.response?.status === 404; -} - -/** - * Check whether an error is a transient network / timeout error. - * - * These are safe to swallow in background refresh tasks — CW will be - * retried on the next refresh cycle. Logs a concise one-line warning - * instead of dumping the full Axios error object. - */ -function isTransientError(err: unknown): boolean { - if (typeof err !== "object" || err === null) return false; - const e = err as Record; - if (!e.isAxiosError) return false; - const code = e.code as string | undefined; - return ( - code === "ECONNABORTED" || - code === "ECONNREFUSED" || - code === "ECONNRESET" || - code === "ETIMEDOUT" || - code === "ERR_NETWORK" || - code === "ENETUNREACH" || - code === "ERR_BAD_RESPONSE" - ); -} - -/** Build a concise error description for logging (avoids dumping entire Axios objects). */ -function describeError(err: unknown): string { - if (typeof err !== "object" || err === null) return String(err); - const e = err as Record; - if (e.isAxiosError) { - const method = (e.config?.method ?? "?").toUpperCase(); - const url = e.config?.url ?? "unknown"; - const code = e.code ?? ""; - const status = e.response?.status ?? ""; - return `${method} ${url} → ${code || `HTTP ${status}`} (${e.message})`; - } - return e.message ?? String(err); -} - -/** - * When true, transient-error warnings inside fetchAndCache* are suppressed. - * Used during background refresh to avoid flooding the terminal — the - * refresh function prints a single summary line instead. - */ -let _suppressTransientWarnings = false; - -// --------------------------------------------------------------------------- -// Write helpers -// --------------------------------------------------------------------------- - -/** - * Fetch activities from CW and cache them with the appropriate TTL. - * - * Returns an empty array if CW responds with 404 (opportunity doesn't - * exist or was deleted upstream). - * - * @returns The raw `CWActivity[]` collection (as plain array). - */ -export async function fetchAndCacheActivities( - cwOpportunityId: number, - ttlMs: number, -): Promise { - try { - // Use the direct (single-call) variant to avoid the extra count request - const arr = await activityCw.fetchByOpportunityDirect(cwOpportunityId); - await redis.set( - activityCacheKey(cwOpportunityId), - JSON.stringify(arr), - "PX", - ttlMs, - ); - return arr; - } catch (err) { - if (isNotFoundError(err)) return []; - if (isTransientError(err)) { - console.warn( - `[cache] activities opp#${cwOpportunityId}: ${describeError(err)}`, - ); - return []; - } - throw err; - } -} - -/** - * Fetch company CW data (company, contacts) and cache with the given TTL. - * - * @returns The hydration blob or `null` if the company doesn't exist in CW. - */ -export async function fetchAndCacheCompanyCwData( - cwCompanyId: number, - ttlMs: number, -): Promise<{ company: any; defaultContact: any; allContacts: any[] } | null> { - try { - // Fetch company and all-contacts in parallel — the allContacts URL - // can be constructed directly without the company response. - const [cwCompany, allContactsData] = await Promise.all([ - fetchCwCompanyById(cwCompanyId), - withCwRetry( - () => - connectWiseApi.get( - `/company/companies/${cwCompanyId}/contacts?pageSize=1000`, - ), - { label: `company#${cwCompanyId}/allContacts` }, - ), - ]); - - if (!cwCompany) return null; - - // Default contact: derive from allContacts instead of making an - // extra serial CW call. The company object carries the default - // contact's ID, so we can pull it from the list we already fetched. - const defaultContactId = cwCompany.defaultContact?.id; - const defaultContactData = defaultContactId - ? ((allContactsData.data as any[]).find( - (c: any) => c.id === defaultContactId, - ) ?? null) - : null; - - const blob = { - company: cwCompany, - defaultContact: defaultContactData, - allContacts: allContactsData.data, - }; - - await redis.set( - companyCwCacheKey(cwCompanyId), - JSON.stringify(blob), - "PX", - ttlMs, - ); - - return blob; - } catch (err) { - if (isNotFoundError(err)) return null; - if (isTransientError(err)) { - console.warn(`[cache] company#${cwCompanyId}: ${describeError(err)}`); - return null; - } - throw err; - } -} - -/** - * Fetch opportunity notes from CW and cache the raw response. - * - * Returns an empty array if CW responds with 404. - * - * @returns The raw CW notes array. - */ -export async function fetchAndCacheNotes( - cwOpportunityId: number, - ttlMs: number, -): Promise { - try { - const notes = await opportunityCw.fetchNotes(cwOpportunityId); - await redis.set( - notesCacheKey(cwOpportunityId), - JSON.stringify(notes), - "PX", - ttlMs, - ); - return notes; - } catch (err) { - if (isNotFoundError(err)) return []; - if (isTransientError(err)) { - console.warn( - `[cache] notes opp#${cwOpportunityId}: ${describeError(err)}`, - ); - return []; - } - throw err; - } -} - -/** - * Fetch opportunity contacts from CW and cache the raw response. - * - * Returns an empty array if CW responds with 404. - * - * @returns The raw CW contacts array. - */ -export async function fetchAndCacheContacts( - cwOpportunityId: number, - ttlMs: number, -): Promise { - try { - const contacts = await opportunityCw.fetchContacts(cwOpportunityId); - await redis.set( - contactsCacheKey(cwOpportunityId), - JSON.stringify(contacts), - "PX", - ttlMs, - ); - return contacts; - } catch (err) { - if (isNotFoundError(err)) return []; - if (isTransientError(err)) { - console.warn( - `[cache] contacts opp#${cwOpportunityId}: ${describeError(err)}`, - ); - return []; - } - throw err; - } -} - -/** - * Invalidate cached notes for an opportunity. - * - * Call this after any note mutation (create, update, delete) so the - * next read refreshes from ConnectWise. - */ -export async function invalidateNotesCache( - cwOpportunityId: number, -): Promise { - await redis.del(notesCacheKey(cwOpportunityId)); -} - -/** - * Invalidate cached contacts for an opportunity. - * - * Call this after any contact mutation so the next read refreshes - * from ConnectWise. - */ -export async function invalidateContactsCache( - cwOpportunityId: number, -): Promise { - await redis.del(contactsCacheKey(cwOpportunityId)); -} - -/** - * Fetch opportunity products (forecast + procurement) from CW and cache. - * - * Stores both the forecast response and procurement products together - * so that `fetchProducts()` can reconstruct ForecastProductControllers - * from a single cache hit. - * - * @returns `{ forecast, procProducts }` blob. - */ -export async function fetchAndCacheProducts( - cwOpportunityId: number, - ttlMs: number, -): Promise<{ forecast: any; procProducts: any[] }> { - try { - const [forecast, procProducts] = await Promise.all([ - opportunityCw.fetchProducts(cwOpportunityId), - opportunityCw.fetchProcurementProducts(cwOpportunityId), - ]); - - const blob = { forecast, procProducts }; - await redis.set( - productsCacheKey(cwOpportunityId), - JSON.stringify(blob), - "PX", - ttlMs, - ); - return blob; - } catch (err) { - if (isNotFoundError(err)) - return { forecast: { forecastItems: [] }, procProducts: [] }; - if (isTransientError(err)) { - console.warn( - `[cache] products opp#${cwOpportunityId}: ${describeError(err)}`, - ); - return { forecast: { forecastItems: [] }, procProducts: [] }; - } - throw err; - } -} - -/** - * Invalidate cached products for an opportunity. - * - * Call this after any product mutation (add, update, resequence) so the - * next read refreshes from ConnectWise. - */ -export async function invalidateProductsCache( - cwOpportunityId: number, -): Promise { - await redis.del(productsCacheKey(cwOpportunityId)); -} - -/** - * Invalidate all cached data for an opportunity. - * - * Removes activities, notes, contacts, products, and CW data cache keys. - * Call this when an opportunity is deleted. - */ -export async function invalidateAllOpportunityCaches( - cwOpportunityId: number, -): Promise { - await redis.del( - activityCacheKey(cwOpportunityId), - notesCacheKey(cwOpportunityId), - contactsCacheKey(cwOpportunityId), - productsCacheKey(cwOpportunityId), - oppCwDataCacheKey(cwOpportunityId), - ); -} - -/** - * Site TTL — 20 minutes. Site/address data rarely changes so we cache - * aggressively. The background refresh does NOT proactively warm site keys; - * they are populated lazily on the first detail-view request. - */ -const SITE_TTL_MS = 1_200_000; - -/** - * Fetch a CW company site from ConnectWise and cache the result. - * - * @returns The raw CW site object. - */ -export async function fetchAndCacheSite( - cwCompanyId: number, - cwSiteId: number, -): Promise { - try { - const site = await fetchCompanySite(cwCompanyId, cwSiteId); - await redis.set( - siteCacheKey(cwCompanyId, cwSiteId), - JSON.stringify(site), - "PX", - SITE_TTL_MS, - ); - return site; - } catch (err) { - if (isNotFoundError(err)) return null; - if (isTransientError(err)) { - console.warn( - `[cache] site company#${cwCompanyId}/site#${cwSiteId}: ${describeError(err)}`, - ); - return null; - } - throw err; - } -} - -/** - * Fetch the raw CW opportunity response from ConnectWise and cache it. - * - * Used by `fetchItem()` in the manager to avoid a CW roundtrip when - * the detail view is reloaded within the cache TTL window. - * - * @param cwOpportunityId - The CW opportunity ID - * @param ttlMs - Cache TTL in milliseconds - * @returns The raw CW opportunity response object. - */ -export async function fetchAndCacheOppCwData( - cwOpportunityId: number, - ttlMs: number, -): Promise { - try { - const cwData = await opportunityCw.fetch(cwOpportunityId); - await redis.set( - oppCwDataCacheKey(cwOpportunityId), - JSON.stringify(cwData), - "PX", - ttlMs, - ); - return cwData; - } catch (err) { - if (isNotFoundError(err)) return null; - if (isTransientError(err)) { - console.warn(`[cache] opp#${cwOpportunityId}: ${describeError(err)}`); - return null; - } - throw err; - } -} - -// --------------------------------------------------------------------------- -// Background refresh -// --------------------------------------------------------------------------- - -/** - * Fixed 24-hour TTL used for archived (closed > 30 days) opportunity cache entries. - * These opportunities are outside the adaptive-TTL window and are rebuilt once per - * day at midnight via {@link refreshArchivedOpportunityCache}. - */ -export const TTL_ARCHIVED_MS = 24 * 60 * 60 * 1000; // 24 hours - -/** - * Cache opportunities that fall outside the adaptive-TTL window — i.e. those - * closed **more than 30 days ago** — with a fixed 24-hour TTL. - * - * These opportunities are excluded by {@link computeCacheTTL} (returns `null`) - * and are therefore never warmed by {@link refreshOpportunityCache}. This - * function fills that gap so archived deals are still served from cache on - * the rare occasion they are accessed. - * - * ## Scheduling - * - * Designed to be triggered once per day at midnight from `src/index.ts`. At - * midnight `force` is `true` so every key is unconditionally overwritten, - * ensuring data is no more than 24 hours stale. - * - * On startup `force` defaults to `false` so only truly missing keys are - * populated; this avoids a large CW burst on every process restart. - * - * @param force - When `true`, overwrite every cache key without checking - * whether it already exists. Defaults to `false`. - */ -/** - * TODO: This function has been moved to a worker at - * `src/modules/workers/cache/refreshArchivedOpportunities.ts` - * - * Wire up the worker to run daily at midnight with force=true to ensure - * archived opportunities (closed > 30 days) have fresh cache entries. - * - * @deprecated - Use refreshArchivedOpportunitiesWorker from the worker module - */ -export async function refreshArchivedOpportunityCache( - force = false, -): Promise { - throw new Error( - "refreshArchivedOpportunityCache has been moved to a worker. " + - "Use refreshArchivedOpportunitiesWorker from src/modules/workers/cache/refreshArchivedOpportunities.ts", - ); -} - -/** - * TODO: This function has been moved to a worker at - * `src/modules/workers/cache/refreshActiveOpportunities.ts` - * - * Wire up the worker to run every 30 seconds to refresh cache for active - * and recently-closed (within 30 days) opportunities. - * - * @deprecated - Use refreshActiveOpportunitiesWorker from the worker module - */ -export async function refreshOpportunityCache(): Promise { - throw new Error( - "refreshOpportunityCache has been moved to a worker. " + - "Use refreshActiveOpportunitiesWorker from src/modules/workers/cache/refreshActiveOpportunities.ts", - ); -} diff --git a/api/src/modules/cache/salesOpportunityMetricsCache.ts b/api/src/modules/cache/salesOpportunityMetricsCache.ts index b425e55..eebb94b 100644 --- a/api/src/modules/cache/salesOpportunityMetricsCache.ts +++ b/api/src/modules/cache/salesOpportunityMetricsCache.ts @@ -1,6 +1,4 @@ import { prisma, redis } from "../../constants"; -import { getCachedOppCwData, getCachedProducts } from "./opportunityCache"; -import { OpportunityStatus } from "../../workflows/wf.opportunity"; import { events } from "../globalEvents"; import { opportunities } from "../../managers/opportunities"; import { normalizeProbabilityRatio } from "../sales-utils/normalizeProbability"; @@ -101,13 +99,16 @@ interface CachedOpportunityRevenue { } interface OpportunityRow { - id: string; - cwOpportunityId: number; + id: number; + uid: string; name: string; - primarySalesRepIdentifier: string | null; - secondarySalesRepIdentifier: string | null; - statusCwId: number | null; - statusName: string | null; + primarySalesRepId: string | null; + secondarySalesRepId: string | null; + status: { + wonFlag: boolean; + lostFlag: boolean; + closeFlag: boolean; + } | null; closedFlag: boolean; dateBecameLead: Date | null; closedDate: Date | null; @@ -137,107 +138,23 @@ const toFinite = (value: unknown): number => { return n; }; -const isWon = (opp: { - statusCwId: number | null; - statusName: string | null; - closedFlag: boolean; -}) => { - if (opp.statusCwId === OpportunityStatus.Won) return true; - if (opp.statusName?.toLowerCase().includes("won")) return true; - if (opp.closedFlag && opp.statusName?.toLowerCase().includes("won")) - return true; - return false; -}; +const isWon = (opp: { status: { wonFlag: boolean } | null }) => + Boolean(opp.status?.wonFlag); -const isLost = (opp: { - statusCwId: number | null; - statusName: string | null; - closedFlag: boolean; -}) => { - if (opp.statusCwId === OpportunityStatus.Lost) return true; - if (opp.statusName?.toLowerCase().includes("lost")) return true; - if (opp.closedFlag && opp.statusName?.toLowerCase().includes("lost")) - return true; - return false; -}; +const isLost = (opp: { status: { lostFlag: boolean } | null }) => + Boolean(opp.status?.lostFlag); const isClosedOpportunity = (opp: { - statusCwId: number | null; - statusName: string | null; + status: { wonFlag: boolean; lostFlag: boolean; closeFlag: boolean } | null; closedFlag: boolean; }) => { if (opp.closedFlag) return true; + if (opp.status?.closeFlag) return true; if (isWon(opp)) return true; if (isLost(opp)) return true; return false; }; -const buildCancellationMap = (procProducts: any[]) => { - const map = new Map(); - - for (const pp of procProducts) { - const rawForecastDetailId = pp?.forecastDetailId; - const forecastDetailId = - typeof rawForecastDetailId === "number" - ? rawForecastDetailId - : Number(rawForecastDetailId); - - if (Number.isFinite(forecastDetailId) && forecastDetailId > 0) { - map.set(forecastDetailId, pp); - } - } - - return map; -}; - -const computeRevenueFromProductsBlob = ( - blob: any, -): Omit => { - const forecastItems = Array.isArray(blob?.forecast?.forecastItems) - ? blob.forecast.forecastItems - : []; - const procProducts = Array.isArray(blob?.procProducts) - ? blob.procProducts - : []; - - const cancellationMap = buildCancellationMap(procProducts); - - let totalRevenue = 0; - let taxableRevenue = 0; - - for (const item of forecastItems) { - if (!cancellationMap.has(item?.id)) continue; - if (!item?.includeFlag) continue; - - const quantity = Math.max(0, toFinite(item?.quantity)); - const revenue = toFinite(item?.revenue); - - const cancellation = cancellationMap.get(item.id); - const cancelledFlag = Boolean(cancellation?.cancelledFlag); - const quantityCancelled = Math.max( - 0, - toFinite(cancellation?.quantityCancelled), - ); - - if (cancelledFlag && quantity > 0 && quantityCancelled >= quantity) - continue; - - const ratio = - quantity > 0 ? Math.max(0, (quantity - quantityCancelled) / quantity) : 1; - const effectiveRevenue = revenue * ratio; - - totalRevenue += effectiveRevenue; - if (item?.taxableFlag) taxableRevenue += effectiveRevenue; - } - - const nonTaxableRevenue = totalRevenue - taxableRevenue; - - return { - totalRevenue: roundCurrency(totalRevenue), - taxableRevenue: roundCurrency(taxableRevenue), - nonTaxableRevenue: roundCurrency(nonTaxableRevenue), - }; -}; const computeRevenueFromControllers = ( products: Array<{ @@ -298,20 +215,8 @@ const writeCachedOpportunityRevenue = async ( ); }; -const resolveProbabilityRatio = async (opp: { - cwOpportunityId: number; - probability: number; -}): Promise => { - const fromDb = normalizeProbabilityRatio(opp.probability); - if (fromDb > 0) return fromDb; - - const cachedCwOpp = await getCachedOppCwData(opp.cwOpportunityId); - if (!cachedCwOpp) return 0; - - const rawProbability = - cachedCwOpp?.probability?.name ?? cachedCwOpp?.probability ?? 0; - return normalizeProbabilityRatio(rawProbability); -}; +const resolveProbabilityRatio = (opp: { probability: number }): number => + normalizeProbabilityRatio(opp.probability); const getOpportunityRevenueCacheFirst = async ( cwOpportunityId: number, @@ -327,18 +232,6 @@ const getOpportunityRevenueCacheFirst = async ( } } - if (!opts?.forceColdLoad) { - const cachedProducts = await getCachedProducts(cwOpportunityId); - if (cachedProducts) { - const computed = computeRevenueFromProductsBlob(cachedProducts); - await writeCachedOpportunityRevenue(cwOpportunityId, computed); - return { - ...computed, - cacheHit: true, - }; - } - } - try { const opportunity = await opportunities.fetchRecord(cwOpportunityId); const products = await opportunity.fetchProducts({ @@ -489,8 +382,8 @@ export async function refreshSalesOpportunityMetricsCache( AND: [ { OR: [ - { primarySalesRepIdentifier: { in: memberIdentifiers } }, - { secondarySalesRepIdentifier: { in: memberIdentifiers } }, + { primarySalesRepId: { in: memberIdentifiers } }, + { secondarySalesRepId: { in: memberIdentifiers } }, ], }, { dateBecameLead: { gte: yearStart } }, @@ -501,12 +394,17 @@ export async function refreshSalesOpportunityMetricsCache( }, select: { id: true, - cwOpportunityId: true, + uid: true, name: true, - primarySalesRepIdentifier: true, - secondarySalesRepIdentifier: true, - statusCwId: true, - statusName: true, + primarySalesRepId: true, + secondarySalesRepId: true, + status: { + select: { + wonFlag: true, + lostFlag: true, + closeFlag: true, + }, + }, closedFlag: true, dateBecameLead: true, closedDate: true, @@ -565,7 +463,7 @@ export async function refreshSalesOpportunityMetricsCache( async (opp) => { const [revenue, probabilityRatio] = await Promise.all([ withTimeout( - getOpportunityRevenueCacheFirst(opp.cwOpportunityId, { + getOpportunityRevenueCacheFirst(opp.id, { forceColdLoad, }), PRODUCT_LOOKUP_TIMEOUT_MS, @@ -619,10 +517,10 @@ export async function refreshSalesOpportunityMetricsCache( for (const opp of opportunityRows) { const assigned = new Set(); - if (opp.primarySalesRepIdentifier) - assigned.add(opp.primarySalesRepIdentifier); - if (opp.secondarySalesRepIdentifier) - assigned.add(opp.secondarySalesRepIdentifier); + if (opp.primarySalesRepId) + assigned.add(opp.primarySalesRepId); + if (opp.secondarySalesRepId) + assigned.add(opp.secondarySalesRepId); for (const identifier of assigned) { const bucket = opportunitiesByMember.get(identifier); @@ -665,8 +563,8 @@ export async function refreshSalesOpportunityMetricsCache( ); const breakdownEntry: OpportunityBreakdownEntry = { - id: opp.id, - cwId: opp.cwOpportunityId, + id: opp.uid, + cwId: opp.id, name: opp.name, revenue: revenue.totalRevenue, taxableRevenue: revenue.taxableRevenue, diff --git a/api/src/modules/collector-client/runCollector.ts b/api/src/modules/collector-client/runCollector.ts deleted file mode 100644 index f030cae..0000000 --- a/api/src/modules/collector-client/runCollector.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { collectorSocket } from "../../constants"; - -export type CollectorQueryOptions = { - select?: string[]; - include?: string[]; - [key: string]: unknown; -}; - -type CollectorSuccessResponse = { - success: true; - data: T; -}; - -type CollectorErrorResponse = { - success: false; - error: string; -}; - -type CollectorResponse = - | CollectorSuccessResponse - | CollectorErrorResponse; - -const DEFAULT_ACK_TIMEOUT_MS = Number( - Bun.env.COLLECTOR_ACK_TIMEOUT_MS ?? "15000", -); -const DEFAULT_CONNECT_TIMEOUT_MS = Number( - Bun.env.COLLECTOR_CONNECT_TIMEOUT_MS ?? "5000", -); - -const ensureCollectorConnected = async ( - timeoutMs = DEFAULT_CONNECT_TIMEOUT_MS, -): Promise => { - if (collectorSocket.connected) { - return; - } - - collectorSocket.connect(); - - await new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - cleanup(); - reject(new Error("Collector socket connection timeout")); - }, timeoutMs); - - const onConnect = () => { - cleanup(); - resolve(); - }; - - const onConnectError = (err: Error) => { - cleanup(); - reject(err); - }; - - const cleanup = () => { - clearTimeout(timeout); - collectorSocket.off("connect", onConnect); - collectorSocket.off("connect_error", onConnectError); - }; - - collectorSocket.on("connect", onConnect); - collectorSocket.on("connect_error", onConnectError); - }); -}; - -export const runCollector = async ( - collector: string, - opts?: CollectorQueryOptions, -): Promise => { - await ensureCollectorConnected(); - - const response = await new Promise>( - (resolve, reject) => { - collectorSocket - .timeout(DEFAULT_ACK_TIMEOUT_MS) - .emit( - collector, - opts, - (err: Error | null, payload?: CollectorResponse) => { - if (err) { - reject(err); - return; - } - - if (!payload) { - reject( - new Error(`Collector '${collector}' returned an empty payload`), - ); - return; - } - - resolve(payload); - }, - ); - }, - ); - - if (!response.success) { - throw new Error(`Collector '${collector}' failed: ${response.error}`); - } - - return response.data; -}; diff --git a/api/src/modules/cw-utils/activities/fetchActivity.ts b/api/src/modules/cw-utils/activities/fetchActivity.ts deleted file mode 100644 index 8fd4c54..0000000 --- a/api/src/modules/cw-utils/activities/fetchActivity.ts +++ /dev/null @@ -1,27 +0,0 @@ -import GenericError from "../../../Errors/GenericError"; -import { activityCw } from "./activities"; -import { CWActivity } from "./activity.types"; - -/** - * Fetch a single activity by its ConnectWise ID. - * - * @param cwActivityId - The ConnectWise activity ID - * @returns The full CW activity object - * @throws GenericError if the fetch fails - */ -export const fetchActivity = async ( - cwActivityId: number, -): Promise => { - try { - return await activityCw.fetch(cwActivityId); - } catch (error) { - const errBody = (error as any).response?.data || error; - console.error(`Error fetching activity with ID ${cwActivityId}:`, errBody); - throw new GenericError({ - name: "FetchActivityError", - message: `Failed to fetch activity ${cwActivityId}`, - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: 502, - }); - } -}; diff --git a/api/src/modules/cw-utils/activities/fetchAllActivities.ts b/api/src/modules/cw-utils/activities/fetchAllActivities.ts deleted file mode 100644 index 6a1a219..0000000 --- a/api/src/modules/cw-utils/activities/fetchAllActivities.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Collection } from "@discordjs/collection"; -import GenericError from "../../../Errors/GenericError"; -import { activityCw } from "./activities"; -import { CWActivity } from "./activity.types"; - -/** - * Fetch all activities from ConnectWise with optional conditions. - * - * @param conditions - Optional CW conditions string for filtering - * @returns A Collection of CW activities keyed by their ID - * @throws GenericError if the fetch fails - */ -export const fetchAllActivities = async ( - conditions?: string, -): Promise> => { - try { - return await activityCw.fetchAll(conditions); - } catch (error) { - const errBody = (error as any).response?.data || error; - console.error("Error fetching all activities:", errBody); - throw new GenericError({ - name: "FetchAllActivitiesError", - message: "Failed to fetch activities from ConnectWise", - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: 502, - }); - } -}; diff --git a/api/src/modules/cw-utils/companyTranslation.ts b/api/src/modules/cw-utils/companyTranslation.ts index 6693e9b..510b73e 100644 --- a/api/src/modules/cw-utils/companyTranslation.ts +++ b/api/src/modules/cw-utils/companyTranslation.ts @@ -1,41 +1,10 @@ import { Company } from "../../types/ConnectWiseTypes"; import { - CollectorCompanyRecord, CompanySourceRecord, NormalizedCompanyRecord, } from "../../types/CompanySourceTypes"; -export const isCollectorCompanyRecord = ( - value: unknown, -): value is CollectorCompanyRecord => { - if (!value || typeof value !== "object") { - return false; - } - - const candidate = value as Partial; - - return ( - typeof candidate.companyRecId === "number" && - "companyId" in candidate && - "companyName" in candidate - ); -}; - -const normalizeFromCollector = ( - company: CollectorCompanyRecord, -): NormalizedCompanyRecord | null => { - if (!company.companyId || !company.companyName) { - return null; - } - - return { - id: company.companyRecId, - identifier: company.companyId, - name: company.companyName, - }; -}; - -const normalizeFromCwApi = ( +const normalizeCompany = ( company: Company, ): NormalizedCompanyRecord | null => { if (!company.identifier || !company.name) { @@ -52,11 +21,7 @@ const normalizeFromCwApi = ( export const normalizeCompanyRecord = ( source: CompanySourceRecord, ): NormalizedCompanyRecord | null => { - if (isCollectorCompanyRecord(source)) { - return normalizeFromCollector(source); - } - - return normalizeFromCwApi(source); + return normalizeCompany(source); }; export const normalizeCompanyRecords = ( diff --git a/api/src/modules/cw-utils/configurations/fetchCompanyConfigurations.ts b/api/src/modules/cw-utils/configurations/fetchCompanyConfigurations.ts deleted file mode 100644 index cde609a..0000000 --- a/api/src/modules/cw-utils/configurations/fetchCompanyConfigurations.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { connectWiseApi } from "../../../constants"; -import { ConfigurationResponse } from "../../../types/ConnectWiseTypes"; -import { - processConfigurationResponse, - ProcessedConfiguration, -} from "./processConfigurationResponse"; -import GenericError from "../../../Errors/GenericError"; - -export const fetchCompanyConfigurations = async ( - cwCompanyId: number, -): Promise => { - try { - const response = await connectWiseApi.get( - `/company/configurations?conditions=company/id=${cwCompanyId}`, - ); - return processConfigurationResponse(response.data); - } catch (error) { - const errBody = (error as any).response?.data || error; - console.error( - `Error fetching configurations for company ID ${cwCompanyId}:`, - errBody, - ); - throw new GenericError({ - name: "FetchCompanyConfigurationsError", - message: `Failed to fetch configurations for company ${cwCompanyId}`, - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: 502, - }); - } -}; diff --git a/api/src/modules/cw-utils/configurations/fetchSingleConfiguration.ts b/api/src/modules/cw-utils/configurations/fetchSingleConfiguration.ts deleted file mode 100644 index e69de29..0000000 diff --git a/api/src/modules/cw-utils/fetchAllCompanies.ts b/api/src/modules/cw-utils/fetchAllCompanies.ts deleted file mode 100644 index c188a61..0000000 --- a/api/src/modules/cw-utils/fetchAllCompanies.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Collection } from "@discordjs/collection"; -import { connectWiseApi } from "../../constants"; -import { runCollector } from "../collector-client/runCollector"; -import { - CollectorCompanyRecord, - NormalizedCompanyRecord, -} from "../../types/CompanySourceTypes"; -import { normalizeCompanyRecords } from "./companyTranslation"; - -const toCompanyCollection = ( - companies: NormalizedCompanyRecord[], -): Collection => { - const allCompanies = new Collection(); - - for (const company of companies) { - allCompanies.set(company.id, company); - } - - return allCompanies; -}; - -export const fetchAllCwCompanies = async (): Promise< - Collection -> => { - try { - console.log("[fetchAllCwCompanies] Attempting to fetch via collector..."); - const collectorCompanies = - await runCollector("fetchCompanies"); - if (!Array.isArray(collectorCompanies)) { - throw new Error("Collector payload was not an array"); - } - - console.log( - `[fetchAllCwCompanies] ✓ Successfully used collector data (${collectorCompanies.length} companies)`, - ); - - return toCompanyCollection(normalizeCompanyRecords(collectorCompanies)); - } catch (err) { - console.error( - `[fetchAllCwCompanies] ✗ Collector fetchCompanies failed, falling back to CW API:`, - err instanceof Error ? { message: err.message, stack: err.stack } : err, - ); - } - - let allCompanies = new Collection(); - const pageCount = 1000; - - const count = (await connectWiseApi.get("/company/companies/count")).data - .count; - const totalPages = Math.ceil(count / pageCount); - - for (let page = 0; page < totalPages; page++) { - const response = await connectWiseApi.get( - `/company/companies?page=${page + 1}&pageSize=${pageCount}`, - ); - const normalizedCompanies = normalizeCompanyRecords(response.data); - - for (const company of normalizedCompanies) { - allCompanies.set(company.id, company); - } - } - - return allCompanies; -}; diff --git a/api/src/modules/cw-utils/fetchCompany.ts b/api/src/modules/cw-utils/fetchCompany.ts deleted file mode 100644 index 9d8adcd..0000000 --- a/api/src/modules/cw-utils/fetchCompany.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { connectWiseApi } from "../../constants"; -import { Company } from "../../types/ConnectWiseTypes"; -import { withCwRetry } from "./withCwRetry"; - -export const fetchCwCompanyById = async ( - companyId: number, -): Promise => { - try { - const response = await withCwRetry( - () => connectWiseApi.get(`/company/companies/${companyId}`), - { - label: `fetchCompany#${companyId}`, - maxAttempts: 3, - baseDelayMs: 1_500, - }, - ); - return response.data; - } catch (error) { - console.error( - `Error fetching company with ID ${companyId}:`, - (error as any).response?.data || error, - ); - return null; - } -}; diff --git a/api/src/modules/cw-utils/members/fetchAllMembers.ts b/api/src/modules/cw-utils/members/fetchAllMembers.ts deleted file mode 100644 index 5f379ba..0000000 --- a/api/src/modules/cw-utils/members/fetchAllMembers.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { Collection } from "@discordjs/collection"; -import { connectWiseApi } from "../../../constants"; -import { runCollector } from "../../collector-client/runCollector"; - -export interface CWMember { - id: number; - identifier: string; - firstName: string; - lastName: string; - officeEmail: string; - inactiveFlag: boolean; - _info: Record; -} - -interface CollectorMemberRecord { - memberRecId: number; - memberId: string; - firstName: string | null; - lastName: string | null; - emailAddress: string | null; - deleteFlag: boolean; - lastUpdateUtc?: string | null; - lastUpdate?: string | null; - _info?: Record; -} - -const isCollectorMemberRecord = ( - value: unknown, -): value is CollectorMemberRecord => { - if (!value || typeof value !== "object") { - return false; - } - - const candidate = value as Partial; - return ( - typeof candidate.memberRecId === "number" && - typeof candidate.memberId === "string" - ); -}; - -const normalizeCollectorMember = ( - member: CollectorMemberRecord, -): CWMember => { - const updatedAt = member.lastUpdateUtc ?? member.lastUpdate ?? ""; - - return { - id: member.memberRecId, - identifier: member.memberId, - firstName: member.firstName ?? "", - lastName: member.lastName ?? "", - officeEmail: member.emailAddress ?? "", - inactiveFlag: Boolean(member.deleteFlag), - _info: member._info ?? { lastUpdated: updatedAt }, - }; -}; - -/** - * Fetch All CW Members - * - * Fetches every member from ConnectWise using pagination and returns them - * in a Collection keyed by their identifier (e.g. "jroberts"). - * - * @param opts.conditions - Optional CW conditions string to filter members - * @returns {Promise>} Collection of CW members keyed by identifier - */ -export const fetchAllCwMembers = async (opts?: { - conditions?: string; -}): Promise> => { - if (!opts?.conditions) { - try { - const collectorMembers = await runCollector("fetchMembers"); - if (!Array.isArray(collectorMembers)) { - throw new Error("Collector payload was not an array"); - } - - const members = new Collection(); - for (const member of collectorMembers) { - if (!isCollectorMemberRecord(member)) { - continue; - } - - const normalized = normalizeCollectorMember(member); - members.set(normalized.identifier, normalized); - } - - if (members.size > 0) { - console.log( - `[fetchAllCwMembers] Using collector data from fetchMembers (${members.size} members)`, - ); - return members; - } - - throw new Error("Collector payload did not contain valid member records"); - } catch (err) { - console.warn( - `[fetchAllCwMembers] Collector fetchMembers failed, falling back to CW API: ${err instanceof Error ? err.message : String(err)}`, - ); - } - } - - const members = new Collection(); - const pageSize = 1000; - const conditionsParam = opts?.conditions - ? `&conditions=${encodeURIComponent(opts.conditions)}` - : ""; - - const { data: countData } = await connectWiseApi.get( - `/system/members/count${conditionsParam ? `?${conditionsParam.slice(1)}` : ""}`, - ); - const totalPages = Math.ceil(countData.count / pageSize); - - for (let page = 0; page < totalPages; page++) { - const { data } = await connectWiseApi.get( - `/system/members?page=${page + 1}&pageSize=${pageSize}${conditionsParam}`, - ); - - for (const member of data) { - members.set(member.identifier, member); - } - } - - return members; -}; - -/** - * Find CW Member Identifier by Email - * - * Looks up a ConnectWise member whose `officeEmail` matches the provided - * email address (case-insensitive) and returns their `identifier` string - * (e.g. "jroberts"). Returns `null` if no match is found. - * - * @param email - The email address to search for - * @param members - Optional pre-fetched member collection to search against (avoids extra API call) - * @returns {Promise} The CW identifier or null - */ -export const findCwIdentifierByEmail = async ( - email: string, - members?: Collection, -): Promise => { - const allMembers = members ?? (await fetchAllCwMembers()); - const normalised = email.toLowerCase(); - - const match = allMembers.find( - (m) => m.officeEmail?.toLowerCase() === normalised, - ); - - return match?.identifier ?? null; -}; diff --git a/api/src/modules/cw-utils/members/memberCache.ts b/api/src/modules/cw-utils/members/memberCache.ts deleted file mode 100644 index 0784fb2..0000000 --- a/api/src/modules/cw-utils/members/memberCache.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Collection } from "@discordjs/collection"; -import { prisma } from "../../../constants"; -import { redis } from "../../../constants"; -import { CWMember } from "./fetchAllMembers"; - -const REDIS_KEY = "cw:members"; - -export interface ResolvedMember { - /** Local database user ID (null if no matching local user) */ - id: string | null; - /** CW member identifier (e.g. "jroberts") */ - identifier: string; - /** Full name resolved from CW member cache, or raw identifier as fallback */ - name: string; - /** ConnectWise member ID */ - cwMemberId: number | null; -} - -/** - * CW Member Cache - * - * Dual-layer cache (in-memory + Redis) of ConnectWise members keyed by - * their identifier (e.g. "jroberts"). Populated by `refreshCwIdentifiers` - * on startup and every 30 minutes thereafter. - */ -let memberCache = new Collection(); - -/** - * Set the member cache contents. - * - * Replaces both the in-memory Collection and the Redis snapshot. - * - * @param members - Collection of CW members keyed by identifier - */ -export const setMemberCache = async (members: Collection) => { - memberCache = members; - await redis.set(REDIS_KEY, JSON.stringify([...members.values()])); -}; - -/** - * Get the current member cache. - * - * Returns the in-memory Collection. If empty, attempts to hydrate from Redis - * first. Returns whatever is available (may be empty if Redis is also cold). - */ -export const getMemberCache = async (): Promise< - Collection -> => { - if (memberCache.size > 0) return memberCache; - - const stored = await redis.get(REDIS_KEY); - if (stored) { - const parsed: CWMember[] = JSON.parse(stored); - memberCache = new Collection(parsed.map((m) => [m.identifier, m])); - } - - return memberCache; -}; - -/** - * Resolve CW Identifier to Full Name - * - * Looks up a ConnectWise member by their identifier in the in-memory cache - * and returns their full name. Falls back to the raw identifier if not found. - * - * @param identifier - The CW member identifier (e.g. "jroberts") - * @returns The member's full name (e.g. "John Roberts") or the raw identifier - */ -export const resolveMemberName = (identifier: string): string => { - const member = memberCache.get(identifier); - if (!member) return identifier; - return `${member.firstName} ${member.lastName}`.trim() || identifier; -}; - -/** - * Resolve CW Identifier to Full Member Info - * - * Looks up a ConnectWise member by their identifier in the in-memory cache - * and cross-references with the local database to return a complete member - * reference including local user ID, CW identifier, full name, and CW member ID. - * - * @param identifier - The CW member identifier (e.g. "jroberts") - * @returns {Promise} Resolved member info - */ -export const resolveMember = async ( - identifier: string, -): Promise => { - const cwMember = memberCache.get(identifier); - const name = cwMember - ? `${cwMember.firstName} ${cwMember.lastName}`.trim() || identifier - : identifier; - - const localUser = await prisma.user.findFirst({ - where: { cwIdentifier: identifier }, - select: { id: true }, - }); - - return { - id: localUser?.id ?? null, - identifier, - name, - cwMemberId: cwMember?.id ?? null, - }; -}; - -/** - * Resolve Multiple CW Identifiers in a Single Batch - * - * Same as `resolveMember` but batches the DB query so that N identifiers - * require only **one** `findMany` instead of N `findFirst` calls. - * - * @param identifiers - Array of CW member identifiers - * @returns Map of identifier → ResolvedMember - */ -export const resolveMembers = async ( - identifiers: string[], -): Promise> => { - const unique = [...new Set(identifiers)]; - - // Single batched DB query for all identifiers - const localUsers = await prisma.user.findMany({ - where: { cwIdentifier: { in: unique } }, - select: { id: true, cwIdentifier: true }, - }); - const userMap = new Map(localUsers.map((u) => [u.cwIdentifier, u.id])); - - const result = new Map(); - for (const identifier of unique) { - const cwMember = memberCache.get(identifier); - const name = cwMember - ? `${cwMember.firstName} ${cwMember.lastName}`.trim() || identifier - : identifier; - result.set(identifier, { - id: userMap.get(identifier) ?? null, - identifier, - name, - cwMemberId: cwMember?.id ?? null, - }); - } - return result; -}; diff --git a/api/src/modules/cw-utils/members/refreshCwIdentifiers.ts b/api/src/modules/cw-utils/members/refreshCwIdentifiers.ts deleted file mode 100644 index 208040e..0000000 --- a/api/src/modules/cw-utils/members/refreshCwIdentifiers.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { connectWiseApi, prisma } from "../../../constants"; -import { events } from "../../globalEvents"; -import { fetchAllCwMembers, findCwIdentifierByEmail } from "./fetchAllMembers"; -import { setMemberCache } from "./memberCache"; - -/** - * Refresh CW Identifiers - * - * Fetches all CW members and all users from the database, then updates - * each user's `cwIdentifier` field by matching their email to a CW member's - * `officeEmail`. Only users whose identifier has changed (or was previously - * null) are updated to avoid unnecessary writes. - * - * Also refreshes the in-memory member cache used for name resolution. - */ -export const refreshCwIdentifiers = async () => { - events.emit("cw:members:refresh:started"); - - const allMembers = await fetchAllCwMembers(); - await setMemberCache(allMembers); - const allUsers = await prisma.user.findMany({ - select: { id: true, email: true, cwIdentifier: true }, - }); - - let updatedCount = 0; - - await Promise.all( - allUsers.map(async (user) => { - const identifier = await findCwIdentifierByEmail(user.email, allMembers); - - if (identifier !== user.cwIdentifier) { - await prisma.user.update({ - where: { id: user.id }, - data: { cwIdentifier: identifier }, - }); - updatedCount++; - } - }), - ); - - events.emit("cw:members:refresh:completed", { - totalMembers: allMembers.size, - totalUsers: allUsers.length, - usersUpdated: updatedCount, - }); -}; diff --git a/api/src/modules/cw-utils/members/refreshCwMembers.ts b/api/src/modules/cw-utils/members/refreshCwMembers.ts deleted file mode 100644 index 4eb0fc2..0000000 --- a/api/src/modules/cw-utils/members/refreshCwMembers.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { prisma } from "../../../constants"; -import { events } from "../../globalEvents"; -import { fetchAllCwMembers, type CWMember } from "./fetchAllMembers"; -import { setMemberCache } from "./memberCache"; -import { CwMemberController } from "../../../controllers/CwMemberController"; - -/** - * Is Regular User - * - * Returns true if the CW member looks like a real person rather than - * a service account (e.g. "labtech", "Admin"). A regular user must - * have a last name and an email address. - */ -const isRegularUser = (member: CWMember): boolean => - !member.inactiveFlag && - Boolean(member.lastName?.trim()) && - Boolean(member.officeEmail?.trim()); - -/** - * Refresh CW Members - * - * Syncs local CwMember records with ConnectWise using a stale-check - * pattern: - * 1. Fetch all members from CW - * 2. Filter to regular users (active, non-service accounts) - * 3. Compare against local cwLastUpdated timestamps - * 4. Upsert stale/new records - * 5. Also refreshes the in-memory member cache - */ -export const refreshCwMembers = async () => { - events.emit("cw:members:db:refresh:check"); - - // 1. Fetch all members from CW - const allCwMembers = await fetchAllCwMembers(); - - // Also refresh the in-memory cache with ALL members (used for name resolution) - await setMemberCache(allCwMembers); - - // 2. Filter to regular users only (active, has last name + email) - const cwMembers = allCwMembers.filter(isRegularUser); - - // 2. Fetch all DB records with their identifier and cwLastUpdated - const dbItems = await prisma.cwMember.findMany({ - select: { cwMemberId: true, cwLastUpdated: true }, - }); - const dbMap = new Map( - dbItems.map((item) => [item.cwMemberId, item.cwLastUpdated]), - ); - - // 3. Determine stale / new IDs - const staleIds: number[] = []; - - for (const [, member] of cwMembers) { - const cwLastUpdated = member._info?.lastUpdated - ? new Date(member._info.lastUpdated) - : null; - const dbLastUpdated = dbMap.get(member.id) ?? null; - - if (!dbLastUpdated || (cwLastUpdated && cwLastUpdated > dbLastUpdated)) { - staleIds.push(member.id); - } - } - - if (staleIds.length === 0) { - events.emit("cw:members:db:refresh:skipped", { - totalCw: cwMembers.size, - totalDb: dbItems.length, - staleCount: 0, - }); - return; - } - - events.emit("cw:members:db:refresh:started", { - totalCw: cwMembers.size, - totalDb: dbItems.length, - staleCount: staleIds.length, - }); - - // 4. Upsert stale/new items - const staleIdSet = new Set(staleIds); - const updatedCount = ( - await Promise.all( - [...cwMembers.values()] - .filter((m) => staleIdSet.has(m.id)) - .map(async (member) => { - const mapped = CwMemberController.mapCwToDb(member); - - return prisma.cwMember.upsert({ - where: { cwMemberId: member.id }, - create: { - cwMemberId: member.id, - ...mapped, - }, - update: mapped, - }); - }), - ) - ).filter(Boolean).length; - - events.emit("cw:members:db:refresh:completed", { - totalCw: cwMembers.size, - totalDb: dbItems.length, - staleCount: staleIds.length, - itemsUpdated: updatedCount, - }); -}; diff --git a/api/src/modules/cw-utils/opportunities/fetchAllOpportunities.ts b/api/src/modules/cw-utils/opportunities/fetchAllOpportunities.ts deleted file mode 100644 index d6a4ca5..0000000 --- a/api/src/modules/cw-utils/opportunities/fetchAllOpportunities.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Collection } from "@discordjs/collection"; -import GenericError from "../../../Errors/GenericError"; -import { opportunityCw } from "./opportunities"; -import { CWOpportunity } from "./opportunity.types"; - -/** - * Fetch all opportunities from ConnectWise with optional conditions. - * - * @param conditions - Optional CW conditions string for filtering - * @returns A Collection of CW opportunities keyed by their ID - * @throws GenericError if the fetch fails - */ -export const fetchAllOpportunities = async ( - conditions?: string, -): Promise> => { - try { - return await opportunityCw.fetchAll(conditions); - } catch (error) { - const errBody = (error as any).response?.data || error; - console.error("Error fetching all opportunities:", errBody); - throw new GenericError({ - name: "FetchAllOpportunitiesError", - message: "Failed to fetch opportunities from ConnectWise", - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: 502, - }); - } -}; diff --git a/api/src/modules/cw-utils/opportunities/fetchCompanyOpportunities.ts b/api/src/modules/cw-utils/opportunities/fetchCompanyOpportunities.ts deleted file mode 100644 index 5fd73fb..0000000 --- a/api/src/modules/cw-utils/opportunities/fetchCompanyOpportunities.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Collection } from "@discordjs/collection"; -import GenericError from "../../../Errors/GenericError"; -import { opportunityCw } from "./opportunities"; -import { CWOpportunity } from "./opportunity.types"; - -/** - * Fetch all opportunities for a specific company from ConnectWise. - * - * @param cwCompanyId - The ConnectWise company ID - * @returns A Collection of CW opportunities for the company keyed by their ID - * @throws GenericError if the fetch fails - */ -export const fetchCompanyOpportunities = async ( - cwCompanyId: number, -): Promise> => { - try { - return await opportunityCw.fetchByCompany(cwCompanyId); - } catch (error) { - const errBody = (error as any).response?.data || error; - console.error( - `Error fetching opportunities for company ${cwCompanyId}:`, - errBody, - ); - throw new GenericError({ - name: "FetchCompanyOpportunitiesError", - message: `Failed to fetch opportunities for company ${cwCompanyId}`, - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: 502, - }); - } -}; diff --git a/api/src/modules/cw-utils/opportunities/fetchOpportunity.ts b/api/src/modules/cw-utils/opportunities/fetchOpportunity.ts deleted file mode 100644 index 3f19e5b..0000000 --- a/api/src/modules/cw-utils/opportunities/fetchOpportunity.ts +++ /dev/null @@ -1,30 +0,0 @@ -import GenericError from "../../../Errors/GenericError"; -import { opportunityCw } from "./opportunities"; -import { CWOpportunity } from "./opportunity.types"; - -/** - * Fetch a single opportunity by its ConnectWise ID. - * - * @param cwOpportunityId - The ConnectWise opportunity ID - * @returns The full CW opportunity object - * @throws GenericError if the fetch fails - */ -export const fetchOpportunity = async ( - cwOpportunityId: number, -): Promise => { - try { - return await opportunityCw.fetch(cwOpportunityId); - } catch (error) { - const errBody = (error as any).response?.data || error; - console.error( - `Error fetching opportunity with ID ${cwOpportunityId}:`, - errBody, - ); - throw new GenericError({ - name: "FetchOpportunityError", - message: `Failed to fetch opportunity ${cwOpportunityId}`, - cause: typeof errBody === "string" ? errBody : JSON.stringify(errBody), - status: 502, - }); - } -}; diff --git a/api/src/modules/cw-utils/opportunities/opportunities.ts b/api/src/modules/cw-utils/opportunities/opportunities.ts index f363329..f755b91 100644 --- a/api/src/modules/cw-utils/opportunities/opportunities.ts +++ b/api/src/modules/cw-utils/opportunities/opportunities.ts @@ -1,6 +1,5 @@ import { Collection } from "@discordjs/collection"; import { connectWiseApi } from "../../../constants"; -import { runCollector } from "../../collector-client/runCollector"; import { CWOpportunity, CWOpportunityCreate, @@ -500,59 +499,7 @@ export const opportunityCw = { }, /** - * Fetch All Opportunities from Collector - * - * Fetches opportunities from the dalpuri collector service with full - * opportunity data (relationships, metadata, etc.). - * - * Includes: pipeline, status, type, urgency, interest, owner, - * company, contact, addresses, marketing campaign, and sale dates. - * - * @returns {Promise} — Raw collector payload of opportunities + * Fetch Opportunities from ConnectWise API + * Depends on pagination from the CW API. */ - fetchAllOpportunitiesFromCollector: async (): Promise => { - const startedAt = Date.now(); - console.info("[opportunities] Collector fetchOpportunities started"); - - const payload = await runCollector("fetchOpportunities", { - include: [ - "soPipeline", - "soOppStatus", - "soType", - "soUrgency", - "soInterest", - "ownerLevel", - "company", - "contact", - "companyAddress", - "billingTerms", - "taxCode", - "currency", - "billingUnit", - "contractType", - "pmProject", - "marketingCampaign", - "agrType", - "srService", - "approvedByMember", - "rejectedByMember", - "activities", - "opportunityNotes", - "forecastItems", - "contacts", - ], - }); - - console.info( - `[opportunities] Collector fetchOpportunities received payload in ${Date.now() - startedAt}ms`, - ); - - if (!Array.isArray(payload)) { - throw new Error("Collector fetchOpportunities payload was not an array"); - } - - console.info(`[opportunities] Collector payload rows: ${payload.length}`); - - return payload; - }, }; diff --git a/api/src/modules/cw-utils/opportunities/opportunity.types.ts b/api/src/modules/cw-utils/opportunities/opportunity.types.ts index 69414b8..fb1975b 100644 --- a/api/src/modules/cw-utils/opportunities/opportunity.types.ts +++ b/api/src/modules/cw-utils/opportunities/opportunity.types.ts @@ -131,6 +131,8 @@ export interface CWForecastItem { sequenceNumber: number; subNumber: number; taxableFlag: boolean; + procurementNotes?: string | null; + productNarrative?: string | null; customFields?: CWCustomField[]; _info?: Record; } @@ -281,6 +283,7 @@ export interface CWOpportunityUpdate { source?: string | null; locationId?: number; businessUnitId?: number; + customFields?: CWCustomField[]; } export interface CWOpportunityCreate { diff --git a/api/src/modules/cw-utils/opportunities/refreshOpportunities.ts b/api/src/modules/cw-utils/opportunities/refreshOpportunities.ts deleted file mode 100644 index 15ac6f0..0000000 --- a/api/src/modules/cw-utils/opportunities/refreshOpportunities.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { prisma } from "../../../constants"; -import { events } from "../../globalEvents"; -import { opportunities } from "../../../managers/opportunities"; -import { opportunityCw } from "./opportunities"; -import { OpportunityController } from "../../../controllers/OpportunityController"; -import { invalidateAllOpportunityCaches } from "../../cache/opportunityCache"; - -/** - * Refresh Opportunities - * - * **Data-source strategy:** - * 1. Try to fetch from the collector (dalpuri) first - * 2. Fall back to ConnectWise API if collector fails or is unavailable - * 3. Normalize the result and upsert into the database - * 4. Reconcile orphaned items (records in DB but not in CW) - * - * Uses the same stale-check pattern as refreshCatalog: - * 1. Fetch lightweight summaries (id + _info.lastUpdated) - * 2. Compare against local cwLastUpdated timestamps - * 3. Full-fetch only stale/new records - * 4. Upsert stale items, optionally linking to internal Company - */ -export const refreshOpportunities = async (opts?: { - collectorFetch?: () => Promise; -}) => { - events.emit("cw:opportunities:refresh:check"); - - // ── Step 1: Try collector first, then fall back to CW ────────────── - let cwSummaries: Map = new Map(); - let allCwItems: Map = new Map(); - let useCollector = false; - - if (opts?.collectorFetch) { - try { - console.log("[refreshOpportunities] Attempting collector fetch"); - const result = await opportunities.refreshOpportunitiesFromCollector({ - collectorFetch: opts.collectorFetch, - }); - - if (result.fromCollector && result.upserted > 0) { - useCollector = true; - console.log( - `[refreshOpportunities] Collector provided ${result.upserted} opportunities`, - ); - events.emit("cw:opportunities:refresh:completed", { - totalCw: result.upserted, - totalDb: result.upserted, - staleCount: result.upserted, - itemsUpdated: result.upserted, - orphanedCount: 0, - }); - } else if (result.errors?.length) { - console.warn( - `[refreshOpportunities] Collector errors: ${result.errors.join("; ")}`, - ); - console.log("[refreshOpportunities] Falling back to ConnectWise API"); - } - } catch (err) { - console.warn( - `[refreshOpportunities] Collector fetch exception, falling back to CW: ${err instanceof Error ? err.message : String(err)}`, - ); - } - } - - // If collector didn't work, use traditional CW fetch - if (!useCollector) { - console.log( - "[refreshOpportunities] Fetching opportunities from ConnectWise", - ); - - // 1. Fetch lightweight summaries from CW - cwSummaries = await opportunityCw.fetchAllSummaries(); - - // 4. Full-fetch all opportunities for upserting - allCwItems = await opportunityCw.fetchAll(); - } - - // ── Step 2: Reconcile orphaned items ───────────────────────────────── - // 2. Fetch all DB items with their cwOpportunityId and cwLastUpdated - const dbItems = await prisma.opportunity.findMany({ - select: { - id: true, - cwOpportunityId: true, - cwLastUpdated: true, - cwDateEntered: true, - }, - }); - const dbMap = new Map(dbItems.map((item) => [item.cwOpportunityId, item])); - - if (!useCollector) { - // 3. Determine stale / new IDs (only if we fetched from CW) - const staleIds: number[] = []; - - for (const [cwId, summary] of cwSummaries) { - const cwLastUpdated = summary._info?.lastUpdated - ? new Date(summary._info.lastUpdated) - : null; - const dbItem = dbMap.get(cwId) ?? null; - const dbLastUpdated = dbItem?.cwLastUpdated ?? null; - - // Treat as stale if never synced, CW has newer data, or cwDateEntered is missing (backfill) - if ( - !dbLastUpdated || - (cwLastUpdated && cwLastUpdated > dbLastUpdated) || - !dbItem?.cwDateEntered - ) { - staleIds.push(cwId); - } - } - - // 3b. Reconcile — find local records that no longer exist in CW - const orphanedItems = dbItems.filter( - (item) => !cwSummaries.has(item.cwOpportunityId), - ); - - if (orphanedItems.length > 0) { - console.log( - `[refreshOpportunities] Reconciling ${orphanedItems.length} orphaned local record(s) not found in CW`, - ); - - await Promise.all( - orphanedItems.map(async (item) => { - await prisma.opportunity.delete({ where: { id: item.id } }); - await invalidateAllOpportunityCaches(item.cwOpportunityId); - }), - ); - - events.emit("cw:opportunities:refresh:reconciled", { - orphanedCount: orphanedItems.length, - removedCwIds: orphanedItems.map((i) => i.cwOpportunityId), - }); - } - - if (staleIds.length === 0) { - events.emit("cw:opportunities:refresh:skipped", { - totalCw: cwSummaries.size, - totalDb: dbItems.length, - staleCount: 0, - orphanedCount: orphanedItems.length, - }); - return; - } - - events.emit("cw:opportunities:refresh:started", { - totalCw: cwSummaries.size, - totalDb: dbItems.length, - staleCount: staleIds.length, - }); - - // 5. Build a company CW ID → internal ID lookup for linking - const companies = await prisma.company.findMany({ - select: { id: true, cw_CompanyId: true }, - }); - const companyMap = new Map(companies.map((c) => [c.cw_CompanyId, c.id])); - - // 6. Upsert stale/new items (only if we fetched from CW) - const updatedCount = ( - await Promise.all( - staleIds.map(async (cwId) => { - const item = allCwItems.get(cwId); - if (!item) return null; - - const mapped = OpportunityController.mapCwToDb(item); - const companyId = item.company?.id - ? (companyMap.get(item.company.id) ?? null) - : null; - - return prisma.opportunity.upsert({ - where: { cwOpportunityId: cwId }, - create: { - cwOpportunityId: cwId, - ...mapped, - companyId, - }, - update: { - ...mapped, - companyId, - }, - }); - }), - ) - ).filter(Boolean).length; - - events.emit("cw:opportunities:refresh:completed", { - totalCw: cwSummaries.size, - totalDb: dbItems.length, - staleCount: staleIds.length, - itemsUpdated: updatedCount, - orphanedCount: orphanedItems.length, - }); - } else { - // Collector-based refresh: still reconcile orphaned items but skip stale-check - console.log( - "[refreshOpportunities] Collector-based refresh: skipping stale-check, performing orphan reconciliation", - ); - - // Fetch list of CW opp IDs from cache or a quick count - // For now, reconcile only items older than a threshold - const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000); - const orphanedItems = dbItems.filter((item) => { - const lastUpdated = item.cwLastUpdated ?? item.cwDateEntered; - // Only reconcile items that were last updated more than a day ago - return lastUpdated && lastUpdated < twentyFourHoursAgo; - }); - - if (orphanedItems.length > 0) { - console.log( - `[refreshOpportunities] Collector reconciling ${orphanedItems.length} stale orphan record(s)`, - ); - - await Promise.all( - orphanedItems.map(async (item) => { - await prisma.opportunity.delete({ where: { id: item.id } }); - await invalidateAllOpportunityCaches(item.cwOpportunityId); - }), - ); - - events.emit("cw:opportunities:refresh:reconciled", { - orphanedCount: orphanedItems.length, - removedCwIds: orphanedItems.map((i) => i.cwOpportunityId), - }); - } - - events.emit("cw:opportunities:refresh:completed", { - totalCw: dbItems.length, - totalDb: dbItems.length, - staleCount: 0, - itemsUpdated: 0, - orphanedCount: orphanedItems.length, - }); - } -}; diff --git a/api/src/modules/cw-utils/procurement/catalog.ts b/api/src/modules/cw-utils/procurement/catalog.ts index c1eeba8..bc4a22d 100644 --- a/api/src/modules/cw-utils/procurement/catalog.ts +++ b/api/src/modules/cw-utils/procurement/catalog.ts @@ -1,11 +1,6 @@ import { Collection } from "@discordjs/collection"; import { connectWiseApi } from "../../../constants"; -import { runCollector } from "../../collector-client/runCollector"; import { CatalogItem } from "./catalog.types.ts"; -import { - normalizeCollectorProducts, - NormalizedCatalogCollectorItem, -} from "./catalogCollectorTranslation"; export interface CatalogSummary { id: number; @@ -18,40 +13,6 @@ export interface InventoryEntry { } export const catalogCw = { - fetchAllProductsFromCollector: async (): Promise< - Collection - > => { - const startedAt = Date.now(); - console.info("[catalog-refresh] Collector fetchProducts started"); - - const payload = await runCollector("fetchProducts", { - include: ["subcategory", "manufacturer", "inventory", "itemVendors"], - }); - - console.info( - `[catalog-refresh] Collector fetchProducts received payload in ${Date.now() - startedAt}ms`, - ); - - if (!Array.isArray(payload)) { - throw new Error("Collector payload was not an array"); - } - - console.info(`[catalog-refresh] Collector payload rows: ${payload.length}`); - - const normalizeStartedAt = Date.now(); - const normalized = normalizeCollectorProducts(payload); - console.info( - `[catalog-refresh] Collector normalization completed in ${Date.now() - normalizeStartedAt}ms (${normalized.size} valid rows)`, - ); - - if (normalized.size === 0) { - throw new Error( - "Collector payload did not contain valid product records", - ); - } - - return new Collection(normalized); - }, countItems: async (): Promise => { const response = await connectWiseApi.get("/procurement/catalog/count"); return response.data.count; diff --git a/api/src/modules/cw-utils/procurement/catalogCollectorTranslation.ts b/api/src/modules/cw-utils/procurement/catalogCollectorTranslation.ts deleted file mode 100644 index 0dcf2c3..0000000 --- a/api/src/modules/cw-utils/procurement/catalogCollectorTranslation.ts +++ /dev/null @@ -1,281 +0,0 @@ -/** - * Catalog Collector Translation - * - * Maps products from the collector (dalpuri) fetchProducts schema - * to the internal database schema for normalization and storage. - */ - -type NumberLike = number | string | null | undefined; - -interface CollectorReference { - recId?: number; - description?: string; - name?: string; -} - -interface CollectorVendorReference { - recId?: number; - identifier?: string; - description?: string; - name?: string; -} - -interface CollectorInventory { - onHand?: NumberLike; -} - -interface CollectorItemVendor { - vendor?: CollectorVendorReference | null; -} - -/** - * Raw collector product shape from fetchProducts. - * This matches the MSSQL source structure with appropriate field names. - */ -export interface CollectorProduct { - // Current collector fields - catalogRecId?: number; - itemId?: string; - description?: string; - longDescription?: string | null; - notes?: string | null; - - categoryRecId?: NumberLike; - category?: CollectorReference | null; - - subcategoryRecId?: NumberLike; - subcategory?: CollectorReference | null; - - manufacturerRecId?: NumberLike; - manufacturerPartNum?: string | null; - manufacturer?: CollectorReference | null; - - vendorRecId?: NumberLike; - vendorSku?: string | null; - itemVendors?: CollectorItemVendor[] | null; - - listPrice?: NumberLike; - currentCost?: NumberLike; - inventory?: CollectorInventory[] | null; - - inactiveFlag?: boolean; - taxableFlag?: boolean; - - lastUpdatedUtc?: string | null; - lastUpdate?: string | null; - dateEnteredUtc?: string | null; - - // Legacy collector fields retained for backward compatibility - pId?: number; - pIdentifier?: string; - pNumber?: string; - pName?: string; - pDescription?: string; - pCustomerDescription?: string; - pInternalNotes?: string; - pCategory?: CollectorReference | null; - pSubcategory?: CollectorReference | null; - pManufacturer?: CollectorReference | null; - pManufacturerPartNumber?: string; - pVendor?: CollectorVendorReference | null; - pVendorSku?: string; - pPrice?: NumberLike; - pCost?: NumberLike; - pOnHand?: NumberLike; - pInactive?: boolean; - pSalesTaxable?: boolean; - _info?: { lastUpdated?: string; dateEntered?: string }; -} - -/** - * Normalized product shape that maps to the DB CatalogItem table. - * Output from normalizeCollectorProduct(). - */ -export interface NormalizedCatalogCollectorItem { - // Core identifiers (required for upsert) - cwCatalogId: number; - - // Basic info - identifier: string; - name: string; - description: string | null; - customerDescription: string | null; - internalNotes: string | null; - - // Categorization - category: string | null; - categoryCwId: number | null; - subcategory: string | null; - subcategoryCwId: number | null; - - // Vendor / Manufacturer - manufacturer: string | null; - manufactureCwId: number | null; - partNumber: string | null; - vendorName: string | null; - vendorSku: string | null; - vendorCwId: number | null; - - // Pricing & Inventory - price: number; - cost: number; - onHand: number; - inactive: boolean; - salesTaxable: boolean; - - // Metadata - cwLastUpdated: Date | null; - cwDateEntered: Date | null; -} - -/** - * Helper: parse a date string to Date or return null. - */ -const parseDate = (dateString: string | null | undefined): Date | null => { - if (!dateString) return null; - try { - const d = new Date(dateString); - return isNaN(d.getTime()) ? null : d; - } catch { - return null; - } -}; - -const parseNumber = (value: NumberLike): number | null => { - if (typeof value === "number" && Number.isFinite(value)) return value; - if (typeof value === "string" && value.trim().length > 0) { - const parsed = Number(value); - if (Number.isFinite(parsed)) return parsed; - } - return null; -}; - -/** - * Helper: extract ID from nested object. - */ -const getId = (obj: unknown): number | null => { - if (!obj || typeof obj !== "object") return null; - const id = (obj as Record).recId; - const parsed = parseNumber(id as NumberLike); - if (!parsed) return null; - return parsed > 0 ? parsed : null; -}; - -/** - * Helper: extract description/name from nested object. - */ -const getName = (obj: unknown, fallback = ""): string => { - if (!obj || typeof obj !== "object") return fallback; - const source = obj as Record; - const desc = source.description; - const name = source.name; - const candidate = typeof desc === "string" ? desc : name; - const result = typeof candidate === "string" ? candidate : fallback; - return result || fallback; -}; - -const getVendorName = (item: CollectorProduct): string | null => { - const directVendor = getName(item.pVendor ?? null); - if (directVendor) return directVendor; - - const firstVendor = item.itemVendors?.[0]?.vendor ?? null; - const nestedVendor = getName(firstVendor); - if (nestedVendor) return nestedVendor; - - return null; -}; - -const getOnHand = (item: CollectorProduct): number => { - const inventory = item.inventory; - if (Array.isArray(inventory) && inventory.length > 0) { - return inventory.reduce((sum, entry) => { - const onHand = parseNumber(entry?.onHand); - return sum + (onHand ?? 0); - }, 0); - } - - return parseNumber(item.pOnHand) ?? 0; -}; - -/** - * Normalize a collector product into the internal DB schema. - * - * Handles field mapping, type conversions, and null coercion. - */ -export const normalizeCollectorProduct = ( - item: CollectorProduct, -): NormalizedCatalogCollectorItem => { - const cwCatalogId = - parseNumber(item.catalogRecId) ?? parseNumber(item.pId) ?? 0; - if (cwCatalogId <= 0) { - throw new Error("Collector product missing catalogRecId"); - } - - // Build normalized object mapping collector fields to DB schema - return { - cwCatalogId, - - // Basic info - identifier: item.itemId ?? item.pIdentifier ?? item.pNumber ?? `product_${cwCatalogId}`, - name: item.description ?? item.pName ?? "", - description: item.longDescription ?? item.pDescription ?? null, - customerDescription: item.pCustomerDescription ?? null, - internalNotes: item.notes ?? item.pInternalNotes ?? null, - - // Categorization - category: getName(item.category ?? item.pCategory), - categoryCwId: - parseNumber(item.categoryRecId) ?? getId(item.category ?? item.pCategory), - subcategory: getName(item.subcategory ?? item.pSubcategory), - subcategoryCwId: - parseNumber(item.subcategoryRecId) ?? - getId(item.subcategory ?? item.pSubcategory), - - // Vendor / Manufacturer - manufacturer: getName(item.manufacturer ?? item.pManufacturer), - manufactureCwId: - parseNumber(item.manufacturerRecId) ?? - getId(item.manufacturer ?? item.pManufacturer), - partNumber: item.manufacturerPartNum ?? item.pManufacturerPartNumber ?? null, - vendorName: getVendorName(item), - vendorSku: item.vendorSku ?? item.pVendorSku ?? null, - vendorCwId: - parseNumber(item.vendorRecId) ?? - getId(item.itemVendors?.[0]?.vendor ?? item.pVendor), - - // Pricing & Inventory - price: parseNumber(item.listPrice) ?? parseNumber(item.pPrice) ?? 0, - cost: parseNumber(item.currentCost) ?? parseNumber(item.pCost) ?? 0, - onHand: getOnHand(item), - inactive: item.inactiveFlag ?? item.pInactive ?? false, - salesTaxable: item.taxableFlag ?? item.pSalesTaxable ?? false, - - // Metadata - cwLastUpdated: parseDate( - item.lastUpdatedUtc ?? item.lastUpdate ?? item._info?.lastUpdated, - ), - cwDateEntered: parseDate(item.dateEnteredUtc ?? item._info?.dateEntered), - }; -}; - -/** - * Normalize a collection of products from the collector. - */ -export const normalizeCollectorProducts = ( - items: unknown[], -): Map => { - const normalized = new Map(); - - for (const item of items) { - try { - const norm = normalizeCollectorProduct(item as CollectorProduct); - normalized.set(norm.cwCatalogId, norm); - } catch (err) { - console.warn( - `[catalogCollectorTranslation] Failed to normalize item: ${err instanceof Error ? err.message : String(err)}`, - ); - } - } - - return normalized; -}; diff --git a/api/src/modules/cw-utils/procurement/listenInventoryAdjustments.ts b/api/src/modules/cw-utils/procurement/listenInventoryAdjustments.ts deleted file mode 100644 index 59f97a4..0000000 --- a/api/src/modules/cw-utils/procurement/listenInventoryAdjustments.ts +++ /dev/null @@ -1,469 +0,0 @@ -import { prisma, redis, connectWiseApi } from "../../../constants"; -import { withCwRetry } from "../withCwRetry"; -import { catalogCw } from "./catalog"; -import { CatalogItem } from "./catalog.types"; - -type JsonObject = Record; - -type TrackedProduct = { - cwCatalogId: number; - product: string; - onHand: string; - inventory: string; - key: string; -}; - -type AdjustmentSnapshot = { - key: string; - trackedRows: TrackedProduct[]; - signature: string; -}; - -const ADJUSTMENTS_ENDPOINT = "/procurement/adjustments?pageSize=1000"; -const CATALOG_ITEM_CACHE_PREFIX = "catalog:item:cw:"; -const CATALOG_ITEM_CACHE_TTL_SECONDS = 20 * 60; -const MAX_SYNC_PER_CYCLE = Number( - process.env.CW_ADJUSTMENT_SYNC_MAX_PER_CYCLE ?? "50", -); -const SYNC_COOLDOWN_MS = Number( - process.env.CW_ADJUSTMENT_SYNC_COOLDOWN_MS ?? `${10 * 60 * 1000}`, -); - -let previous = new Map(); -let previousProductState = new Map(); -const lastSyncedAt = new Map(); -let inFlight = false; - -const isObject = (value: unknown): value is JsonObject => - typeof value === "object" && value !== null && !Array.isArray(value); - -const toObject = (value: unknown): JsonObject => { - if (!isObject(value)) return {}; - - return value; -}; - -const stableStringify = (value: unknown): string => { - if (Array.isArray(value)) { - const entries = value.map((entry) => stableStringify(entry)).sort(); - - return `[${entries.join(",")}]`; - } - - if (isObject(value)) { - const keys = Object.keys(value).sort(); - const pairs = keys.map( - (key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`, - ); - - return `{${pairs.join(",")}}`; - } - - return JSON.stringify(value); -}; - -const readPathValue = (obj: JsonObject, path: string): unknown => { - const parts = path.split("."); - let current: unknown = obj; - - for (const part of parts) { - if (!isObject(current)) return null; - current = current[part]; - } - - return current; -}; - -const firstValue = (obj: JsonObject, paths: string[]): unknown => { - for (const path of paths) { - const value = readPathValue(obj, path); - if (value === null || value === undefined || value === "") continue; - - return value; - } - - return null; -}; - -const asNumber = (value: unknown): number | null => { - if (typeof value === "number" && Number.isFinite(value)) return value; - if (typeof value === "string" && value.length > 0) { - const parsed = Number(value); - if (Number.isFinite(parsed)) return parsed; - } - - return null; -}; - -const asText = (value: unknown): string => { - if (value === null || value === undefined || value === "") return "-"; - if ( - typeof value === "string" || - typeof value === "number" || - typeof value === "boolean" - ) { - return String(value); - } - - if (Array.isArray(value)) { - return `[${value.map((entry) => asText(entry)).join(",")}]`; - } - - if (!isObject(value)) return String(value); - - const preferredFields = ["name", "identifier", "id", "code", "value"]; - for (const field of preferredFields) { - const fieldValue = readPathValue(value, field); - if (fieldValue === null || fieldValue === undefined || fieldValue === "") - continue; - if (typeof fieldValue === "object") continue; - - return String(fieldValue); - } - - return stableStringify(value); -}; - -const adjustmentKey = (adjustment: JsonObject): string => { - const keyPaths = [ - "id", - "adjustmentId", - "procurementAdjustmentId", - "recordId", - "recId", - "_info.id", - "_info.href", - ]; - - for (const path of keyPaths) { - const key = firstValue(adjustment, [path]); - const keyText = asText(key); - if (keyText !== "-") return keyText; - } - - return `anon:${stableStringify(adjustment)}`; -}; - -const trackedRow = (detail: JsonObject): TrackedProduct | null => { - const cwCatalogId = asNumber( - firstValue(detail, [ - "catalogItem.id", - "catalogItemId", - "catalog.id", - "catalogId", - "item.id", - "itemId", - "product.id", - "productId", - "id", - ]), - ); - if (!cwCatalogId) return null; - - const onHand = asText( - firstValue(detail, [ - "onHand", - "onHandQty", - "onHandQuantity", - "qtyOnHand", - "quantityOnHand", - "quantity.onHand", - ]), - ); - const inventory = asText( - firstValue(detail, [ - "inventory", - "inventoryQty", - "inventoryLevel", - "quantity", - "qty", - ]), - ); - if (onHand === "-" && inventory === "-") return null; - - const product = asText( - firstValue(detail, [ - "product.name", - "product.identifier", - "item.name", - "item.identifier", - "catalogItem.name", - "catalogItem.identifier", - "productName", - "productIdentifier", - "sku", - "identifier", - ]), - ); - - return { - cwCatalogId, - product, - onHand, - inventory, - key: `${cwCatalogId}|${product}|${onHand}|${inventory}`, - }; -}; - -const trackedRows = (adjustment: JsonObject): TrackedProduct[] => { - const detailCandidates = [ - readPathValue(adjustment, "adjustmentDetails"), - readPathValue(adjustment, "details"), - readPathValue(adjustment, "lineItems"), - ]; - - for (const candidate of detailCandidates) { - if (!Array.isArray(candidate)) continue; - - const rows = candidate - .map((entry) => trackedRow(toObject(entry))) - .filter((entry): entry is TrackedProduct => entry !== null) - .sort((a, b) => a.key.localeCompare(b.key)); - - if (rows.length > 0) return rows; - } - - const root = trackedRow(adjustment); - if (!root) return []; - - return [root]; -}; - -const snapshot = (rows: unknown[]): Map => { - const out = new Map(); - - for (const entry of rows) { - const adjustment = toObject(entry); - const key = adjustmentKey(adjustment); - const rowsTracked = trackedRows(adjustment); - const signature = stableStringify(rowsTracked); - out.set(key, { - key, - trackedRows: rowsTracked, - signature, - }); - } - - return out; -}; - -const changedCatalogIds = ( - before: Map, - after: Map, -): Set => { - const changed = new Set(); - - for (const [cwCatalogId, nextSignature] of after) { - const prevSignature = before.get(cwCatalogId); - if (!prevSignature) { - changed.add(cwCatalogId); - continue; - } - - if (prevSignature === nextSignature) continue; - changed.add(cwCatalogId); - } - - return changed; -}; - -const productState = ( - adjustments: Map, -): Map => { - const grouped = new Map>(); - - for (const snapshot of adjustments.values()) { - for (const row of snapshot.trackedRows) { - const rows = grouped.get(row.cwCatalogId) ?? new Set(); - rows.add(row.key); - grouped.set(row.cwCatalogId, rows); - } - } - - const state = new Map(); - for (const [cwCatalogId, rows] of grouped) { - state.set(cwCatalogId, stableStringify([...rows].sort())); - } - - return state; -}; - -const applySyncGuards = (ids: number[]): number[] => { - const now = Date.now(); - const cooledIds = ids.filter((cwCatalogId) => { - const last = lastSyncedAt.get(cwCatalogId); - if (!last) return true; - - return now - last >= SYNC_COOLDOWN_MS; - }); - - if (cooledIds.length <= MAX_SYNC_PER_CYCLE) return cooledIds; - return cooledIds.slice(0, MAX_SYNC_PER_CYCLE); -}; - -const fetchAdjustments = async (): Promise => { - const response = await withCwRetry( - () => connectWiseApi.get(ADJUSTMENTS_ENDPOINT), - { - label: "inventory-adjustments", - maxAttempts: 3, - }, - ); - const payload = response.data; - - if (Array.isArray(payload)) return payload; - if (isObject(payload) && Array.isArray(payload.data)) return payload.data; - - return []; -}; - -const cacheKey = (cwCatalogId: number) => - `${CATALOG_ITEM_CACHE_PREFIX}${cwCatalogId}`; - -const cwLastUpdated = (item: CatalogItem): Date => { - const value = item._info?.lastUpdated; - if (!value) return new Date(); - - const parsed = new Date(value); - const invalidDate = Number.isNaN(parsed.getTime()); - if (invalidDate) return new Date(); - - return parsed; -}; - -const syncCatalogItem = async (cwCatalogId: number): Promise => { - try { - const item = await withCwRetry( - () => catalogCw.fetchByCatalogId(cwCatalogId), - { - label: `catalog-item:${cwCatalogId}`, - maxAttempts: 3, - }, - ); - const onHand = await withCwRetry( - () => catalogCw.fetchInventoryOnHand(cwCatalogId), - { - label: `catalog-onhand:${cwCatalogId}`, - maxAttempts: 3, - }, - ); - - const persisted = await prisma.catalogItem.upsert({ - where: { cwCatalogId }, - create: { - cwCatalogId, - identifier: item.identifier, - name: item.description, - description: item.description, - customerDescription: item.customerDescription, - internalNotes: item.notes, - category: item.category?.name, - categoryCwId: item.category?.id, - subcategory: item.subcategory?.name, - subcategoryCwId: item.subcategory?.id, - manufacturer: item.manufacturer?.name, - manufactureCwId: item.manufacturer?.id, - partNumber: item.manufacturerPartNumber, - vendorName: item.vendor?.name, - vendorSku: item.vendorSku, - vendorCwId: item.vendor?.id, - price: item.price, - cost: item.cost, - inactive: item.inactiveFlag, - salesTaxable: item.taxableFlag, - onHand, - cwLastUpdated: cwLastUpdated(item), - }, - update: { - identifier: item.identifier, - name: item.description, - description: item.description, - customerDescription: item.customerDescription, - internalNotes: item.notes, - category: item.category?.name, - categoryCwId: item.category?.id, - subcategory: item.subcategory?.name, - subcategoryCwId: item.subcategory?.id, - manufacturer: item.manufacturer?.name, - manufactureCwId: item.manufacturer?.id, - partNumber: item.manufacturerPartNumber, - vendorName: item.vendor?.name, - vendorSku: item.vendorSku, - vendorCwId: item.vendor?.id, - price: item.price, - cost: item.cost, - inactive: item.inactiveFlag, - salesTaxable: item.taxableFlag, - onHand, - cwLastUpdated: cwLastUpdated(item), - }, - }); - - await redis.set( - cacheKey(cwCatalogId), - JSON.stringify({ - cwCatalogId, - onHand, - cwItem: item, - dbItem: persisted, - syncedAt: new Date().toISOString(), - }), - "EX", - CATALOG_ITEM_CACHE_TTL_SECONDS, - ); - - return true; - } catch (err) { - console.error( - `[inventory-adjustments] failed to sync catalog item ${cwCatalogId}`, - err, - ); - return false; - } -}; - -export const listenInventoryAdjustments = async (): Promise => { - if (inFlight) return; - inFlight = true; - - try { - const rows = await fetchAdjustments(); - const current = snapshot(rows); - const currentProductState = productState(current); - - if (previous.size === 0) { - previous = current; - previousProductState = currentProductState; - console.log( - `[inventory-adjustments] baseline captured (${current.size} adjustments, ${currentProductState.size} products)`, - ); - return; - } - - const changedIds = [ - ...changedCatalogIds(previousProductState, currentProductState), - ].sort((a, b) => a - b); - const guardedIds = applySyncGuards(changedIds); - previous = current; - previousProductState = currentProductState; - if (guardedIds.length === 0) return; - - let successCount = 0; - for (const cwCatalogId of guardedIds) { - const ok = await syncCatalogItem(cwCatalogId); - if (!ok) continue; - lastSyncedAt.set(cwCatalogId, Date.now()); - successCount += 1; - } - - const skippedByCooldown = changedIds.length - guardedIds.length; - - console.log( - `[inventory-adjustments] inventory changed for ${changedIds.length} products, queued ${guardedIds.length}, synced ${successCount}, cooldown/cap skipped ${skippedByCooldown}`, - ); - } catch (err) { - console.error("[inventory-adjustments] listener failed", err); - } finally { - inFlight = false; - } -}; diff --git a/api/src/modules/cw-utils/procurement/refreshCatalog.ts b/api/src/modules/cw-utils/procurement/refreshCatalog.ts deleted file mode 100644 index 898964c..0000000 --- a/api/src/modules/cw-utils/procurement/refreshCatalog.ts +++ /dev/null @@ -1,429 +0,0 @@ -import { prisma } from "../../../constants"; -import { events } from "../../globalEvents"; -import { catalogCw } from "./catalog"; -import { NormalizedCatalogCollectorItem } from "./catalogCollectorTranslation"; - -const CONCURRENCY = 6; -const BATCH_DELAY_MS = 250; -const UPSERT_BATCH_SIZE = 50; -const MAX_ROW_ERROR_LOGS = 10; - -const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - -const truncate = (value: string, max = 400): string => - value.length <= max ? value : `${value.slice(0, max - 3)}...`; - -const summarizePrismaError = (err: unknown): string => { - const e = err as Record; - const code = typeof e?.code === "string" && e.code.length > 0 ? e.code : null; - const message = - typeof e?.message === "string" && e.message.length > 0 - ? e.message - : String(err); - const clientVersion = - typeof e?.clientVersion === "string" ? e.clientVersion : null; - - const meta = - e?.meta && typeof e.meta === "object" - ? (e.meta as Record) - : null; - const modelName = typeof meta?.modelName === "string" ? meta.modelName : null; - const targetRaw = meta?.target; - const target = Array.isArray(targetRaw) - ? targetRaw.join(",") - : typeof targetRaw === "string" - ? targetRaw - : null; - const cause = typeof meta?.cause === "string" ? meta.cause : null; - - const lines: string[] = [ - code ? `Prisma ${code}: ${truncate(message, 600)}` : truncate(message, 600), - ]; - - if (modelName) lines.push(`model=${modelName}`); - if (target) lines.push(`target=${target}`); - if (cause) lines.push(`cause=${truncate(cause, 400)}`); - if (clientVersion) lines.push(`clientVersion=${clientVersion}`); - - return lines.join(" | "); -}; - -const createCatalogErrorLogger = () => { - let loggedRowErrors = 0; - - return (context: string, err: unknown, detail?: Record) => { - if (loggedRowErrors >= MAX_ROW_ERROR_LOGS) return; - - const detailStr = detail ? JSON.stringify(detail) : ""; - const payload = detailStr ? ` | detail=${truncate(detailStr, 120)}` : ""; - console.error( - `[catalog-refresh] ${context}: ${summarizePrismaError(err)}${payload}`, - ); - - loggedRowErrors += 1; - if (loggedRowErrors === MAX_ROW_ERROR_LOGS) { - console.error( - `[catalog-refresh] Reached ${MAX_ROW_ERROR_LOGS} row-level error logs; suppressing additional row errors for this refresh cycle`, - ); - } - }; -}; - -const runSlowParallel = async ( - tasks: Array<() => Promise>, -): Promise => { - let failureCount = 0; - - for (let i = 0; i < tasks.length; i += CONCURRENCY) { - const batch = tasks.slice(i, i + CONCURRENCY); - const results = await Promise.allSettled(batch.map((task) => task())); - - for (const result of results) { - if (result.status === "rejected") failureCount += 1; - } - - if (i + CONCURRENCY >= tasks.length) continue; - await sleep(BATCH_DELAY_MS); - } - - return failureCount; -}; - -const runBatchUpserts = async ( - tasks: Array<() => Promise>, -): Promise => { - let failureCount = 0; - - for (let i = 0; i < tasks.length; i += UPSERT_BATCH_SIZE) { - const batch = tasks.slice(i, i + UPSERT_BATCH_SIZE); - const results = await Promise.allSettled(batch.map((task) => task())); - - for (const result of results) { - if (result.status === "rejected") { - failureCount += 1; - } - } - } - - return failureCount; -}; - -export const refreshCatalog = async () => { - const refreshStartedAt = Date.now(); - const logCatalogError = createCatalogErrorLogger(); - events.emit("cw:catalog:refresh:check"); - console.info("[catalog-refresh] Refresh cycle started"); - - try { - console.info( - "[catalog-refresh] Attempting collector-first catalog refresh", - ); - const collectorItems = await catalogCw.fetchAllProductsFromCollector(); - console.info( - `[catalog-refresh] Collector returned ${collectorItems.size} products`, - ); - events.emit("cw:catalog:refresh:started", { - totalCw: collectorItems.size, - totalDb: null, - staleCount: collectorItems.size, - }); - - const upsertStartedAt = Date.now(); - const updatedCount = await upsertCollectorItems( - collectorItems, - logCatalogError, - ); - console.info( - `[catalog-refresh] Collector upserts completed in ${Date.now() - upsertStartedAt}ms (${updatedCount} rows updated)`, - ); - - events.emit("cw:catalog:refresh:completed", { - totalCw: collectorItems.size, - totalDb: collectorItems.size, - staleCount: collectorItems.size, - itemsUpdated: updatedCount, - }); - console.info( - `[catalog-refresh] Refresh cycle completed via collector in ${Date.now() - refreshStartedAt}ms`, - ); - return; - } catch (err) { - console.warn( - `[catalog-refresh] Collector fetchProducts failed, falling back to CW API: ${err instanceof Error ? err.message : String(err)}`, - ); - } - - const fallbackStartedAt = Date.now(); - console.info("[catalog-refresh] Starting CW fallback catalog refresh"); - try { - await refreshCatalogFromCw(logCatalogError); - } catch (err) { - console.error( - `[catalog-refresh] CW fallback failed: ${summarizePrismaError(err)}`, - ); - throw err; - } - console.info( - `[catalog-refresh] CW fallback refresh completed in ${Date.now() - fallbackStartedAt}ms (${Date.now() - refreshStartedAt}ms total cycle)`, - ); -}; - -const upsertCollectorItems = async ( - collectorItems: Map, - logCatalogError: ( - context: string, - err: unknown, - detail?: Record, - ) => void, -): Promise => { - let updatedCount = 0; - const totalItems = collectorItems.size; - let processedCount = 0; - - const upsertTasks: Array<() => Promise> = []; - for (const item of collectorItems.values()) { - upsertTasks.push(async () => { - try { - await prisma.catalogItem.upsert({ - where: { cwCatalogId: item.cwCatalogId }, - create: { - cwCatalogId: item.cwCatalogId, - identifier: item.identifier, - name: item.name, - description: item.description, - customerDescription: item.customerDescription, - internalNotes: item.internalNotes, - category: item.category, - categoryCwId: item.categoryCwId, - subcategory: item.subcategory, - subcategoryCwId: item.subcategoryCwId, - manufacturer: item.manufacturer, - manufactureCwId: item.manufactureCwId, - partNumber: item.partNumber, - vendorName: item.vendorName, - vendorSku: item.vendorSku, - vendorCwId: item.vendorCwId, - price: item.price, - cost: item.cost, - inactive: item.inactive, - salesTaxable: item.salesTaxable, - onHand: item.onHand, - cwLastUpdated: item.cwLastUpdated, - }, - update: { - identifier: item.identifier, - name: item.name, - description: item.description, - customerDescription: item.customerDescription, - internalNotes: item.internalNotes, - category: item.category, - categoryCwId: item.categoryCwId, - subcategory: item.subcategory, - subcategoryCwId: item.subcategoryCwId, - manufacturer: item.manufacturer, - manufactureCwId: item.manufactureCwId, - partNumber: item.partNumber, - vendorName: item.vendorName, - vendorSku: item.vendorSku, - vendorCwId: item.vendorCwId, - price: item.price, - cost: item.cost, - inactive: item.inactive, - salesTaxable: item.salesTaxable, - onHand: item.onHand, - cwLastUpdated: item.cwLastUpdated, - }, - }); - } catch (err) { - logCatalogError("collector upsert failed", err, { - id: item.cwCatalogId, - }); - throw err; - } - - updatedCount += 1; - processedCount += 1; - - const shouldLogProgress = - processedCount <= 5 || - processedCount % 250 === 0 || - processedCount === totalItems; - if (shouldLogProgress) { - console.info( - `[catalog-refresh] Collector upsert progress: ${processedCount}/${totalItems}`, - ); - } - }); - } - - const upsertFailures = await runBatchUpserts(upsertTasks); - if (upsertFailures > 0) { - console.warn( - `[catalog-refresh] ${upsertFailures} collector upsert task(s) failed; remaining items will retry next cycle`, - ); - } - - return updatedCount; -}; - -const refreshCatalogFromCw = async ( - logCatalogError: ( - context: string, - err: unknown, - detail?: Record, - ) => void, -) => { - // 1. Fetch lightweight summaries from CW (id + _info with lastUpdated) - const cwSummaries = await catalogCw.fetchAllCatalogSummary(); - - // 2. Fetch all DB items with their cwCatalogId and cwLastUpdated - const dbItems = await prisma.catalogItem.findMany({ - select: { cwCatalogId: true, cwLastUpdated: true }, - }); - const dbMap = new Map( - dbItems.map((item) => [item.cwCatalogId, item.cwLastUpdated]), - ); - - // 3. Compare CW lastUpdated vs DB cwLastUpdated — collect IDs that are stale or new - const staleIds: number[] = []; - - for (const [cwId, summary] of cwSummaries) { - const cwLastUpdated = summary._info?.lastUpdated - ? new Date(summary._info.lastUpdated) - : null; - const dbLastUpdated = dbMap.get(cwId) ?? null; - - // New item (not in DB) or CW has a newer timestamp - if (!dbLastUpdated || (cwLastUpdated && cwLastUpdated > dbLastUpdated)) { - staleIds.push(cwId); - } - } - - if (staleIds.length === 0) { - events.emit("cw:catalog:refresh:skipped", { - totalCw: cwSummaries.size, - totalDb: dbItems.length, - staleCount: 0, - }); - return; - } - - events.emit("cw:catalog:refresh:started", { - totalCw: cwSummaries.size, - totalDb: dbItems.length, - staleCount: staleIds.length, - }); - - // 4. Fetch full CW item data for stale IDs using slow, bounded concurrency - const cwItemMap = new Map(); - const itemFetchTasks: Array<() => Promise> = staleIds.map( - (cwId) => async () => { - const item = await catalogCw.fetchByCatalogId(cwId); - cwItemMap.set(cwId, item); - }, - ); - const itemFetchFailures = await runSlowParallel(itemFetchTasks); - - // 5. Fetch inventory onHand for stale IDs using the same slow parallel strategy - const onHandMap = new Map(); - const inventoryTasks: Array<() => Promise> = staleIds.map( - (cwId) => async () => { - try { - const onHand = await catalogCw.fetchInventoryOnHand(cwId); - onHandMap.set(cwId, onHand); - } catch { - onHandMap.set(cwId, 0); - } - }, - ); - const inventoryFailures = await runSlowParallel(inventoryTasks); - - // 6. Upsert stale/new items with bounded slow parallel execution - let updatedCount = 0; - const upsertTasks: Array<() => Promise> = staleIds.map( - (cwId) => async () => { - const item = cwItemMap.get(cwId); - if (!item) return; - - const cwLastUpdated = item._info?.lastUpdated - ? new Date(item._info.lastUpdated) - : new Date(); - const onHand = onHandMap.get(cwId) ?? 0; - - try { - await prisma.catalogItem.upsert({ - where: { cwCatalogId: cwId }, - create: { - cwCatalogId: cwId, - identifier: item.identifier, - name: item.description, - description: item.description, - customerDescription: item.customerDescription, - internalNotes: item.notes, - category: item.category?.name, - categoryCwId: item.category?.id, - subcategory: item.subcategory?.name, - subcategoryCwId: item.subcategory?.id, - manufacturer: item.manufacturer?.name, - manufactureCwId: item.manufacturer?.id, - partNumber: item.manufacturerPartNumber, - vendorName: item.vendor?.name, - vendorSku: item.vendorSku, - vendorCwId: item.vendor?.id, - price: item.price, - cost: item.cost, - inactive: item.inactiveFlag, - salesTaxable: item.taxableFlag, - onHand, - cwLastUpdated, - }, - update: { - name: item.description, - identifier: item.identifier, - description: item.description, - customerDescription: item.customerDescription, - internalNotes: item.notes, - category: item.category?.name, - categoryCwId: item.category?.id, - subcategory: item.subcategory?.name, - subcategoryCwId: item.subcategory?.id, - manufacturer: item.manufacturer?.name, - manufactureCwId: item.manufacturer?.id, - partNumber: item.manufacturerPartNumber, - vendorName: item.vendor?.name, - vendorSku: item.vendorSku, - vendorCwId: item.vendor?.id, - price: item.price, - cost: item.cost, - inactive: item.inactiveFlag, - salesTaxable: item.taxableFlag, - onHand, - cwLastUpdated, - }, - }); - } catch (err) { - logCatalogError("CW fallback upsert failed", err, { - id: cwId, - }); - throw err; - } - - updatedCount += 1; - }, - ); - const upsertFailures = await runBatchUpserts(upsertTasks); - - const failedTasks = itemFetchFailures + inventoryFailures + upsertFailures; - if (failedTasks > 0) { - console.warn( - `[catalog-refresh] ${failedTasks} slow-parallel task(s) failed; remaining items will retry next cycle`, - ); - } - - events.emit("cw:catalog:refresh:completed", { - totalCw: cwSummaries.size, - totalDb: dbItems.length, - staleCount: staleIds.length, - itemsUpdated: updatedCount, - }); -}; diff --git a/api/src/modules/cw-utils/procurement/refreshInventory.ts b/api/src/modules/cw-utils/procurement/refreshInventory.ts deleted file mode 100644 index dc603de..0000000 --- a/api/src/modules/cw-utils/procurement/refreshInventory.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { prisma } from "../../../constants"; -import { events } from "../../globalEvents"; -import { catalogCw } from "./catalog"; - -const CONCURRENCY = 6; -const BATCH_DELAY_MS = 250; - -const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - -export const refreshInventory = async () => { - events.emit("cw:inventory:refresh:check"); - - // 1. Get all active catalog items from DB - const dbItems = await prisma.catalogItem.findMany({ - where: { inactive: false }, - select: { cwCatalogId: true, onHand: true }, - }); - - if (dbItems.length === 0) { - events.emit("cw:inventory:refresh:skipped", { - totalItems: 0, - updatedCount: 0, - }); - return; - } - - events.emit("cw:inventory:refresh:started", { - totalItems: dbItems.length, - }); - - // 2. Slow-parallel fetch inventory onHand for all items - const onHandMap = new Map(); - let failedCount = 0; - - for (let i = 0; i < dbItems.length; i += CONCURRENCY) { - const batch = dbItems.slice(i, i + CONCURRENCY); - const results = await Promise.allSettled( - batch.map(async (item) => { - try { - const onHand = await catalogCw.fetchInventoryOnHand(item.cwCatalogId); - onHandMap.set(item.cwCatalogId, onHand); - } catch { - onHandMap.set(item.cwCatalogId, item.onHand); - } - }), - ); - - for (const result of results) { - if (result.status === "rejected") failedCount += 1; - } - - if (i + CONCURRENCY >= dbItems.length) continue; - await sleep(BATCH_DELAY_MS); - } - - // 3. Only update items where onHand has changed - const updates = dbItems.filter((item) => { - const newOnHand = onHandMap.get(item.cwCatalogId) ?? item.onHand; - return newOnHand !== item.onHand; - }); - - if (updates.length === 0) { - events.emit("cw:inventory:refresh:skipped", { - totalItems: dbItems.length, - updatedCount: 0, - }); - return; - } - - const updatedCount = ( - await Promise.all( - updates.map(async (item) => { - const newOnHand = onHandMap.get(item.cwCatalogId) ?? item.onHand; - return await prisma.catalogItem.update({ - where: { cwCatalogId: item.cwCatalogId }, - data: { onHand: newOnHand }, - }); - }), - ) - ).length; - - events.emit("cw:inventory:refresh:completed", { - totalItems: dbItems.length, - updatedCount, - }); - - if (failedCount > 0) { - console.warn( - `[inventory-refresh] ${failedCount} task(s) failed; fallback values were used and will retry next sweep`, - ); - } -}; diff --git a/api/src/modules/cw-utils/refreshCompanies.ts b/api/src/modules/cw-utils/refreshCompanies.ts deleted file mode 100644 index 61dbe2e..0000000 --- a/api/src/modules/cw-utils/refreshCompanies.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { connectWiseApi, prisma } from "../../constants"; -import { events } from "../globalEvents"; -import { fetchAllCwCompanies } from "./fetchAllCompanies"; - -export const refreshCompanies = async () => { - events.emit("cw:companies:refresh:check"); - - const internalCompanyCount = await prisma.company.count(); - events.emit("cw:companies:refresh:started"); - - const allCompanies = await fetchAllCwCompanies(); - const externalCompanyCount = allCompanies.size; - - // Batch upserts to avoid exhausting the database connection pool - const batchSize = 50; - let updatedCount = 0; - const companiesArray = Array.from(allCompanies.values()); - - for (let i = 0; i < companiesArray.length; i += batchSize) { - const batch = companiesArray.slice(i, i + batchSize); - - const results = await Promise.all( - batch.map((company) => - prisma.company.upsert({ - where: { cw_CompanyId: company.id }, - create: { - cw_CompanyId: company.id, - cw_Identifier: company.identifier, - name: company.name, - }, - update: { - name: company.name, - }, - }), - ), - ); - - updatedCount += results.length; - } - - events.emit("cw:companies:refresh:completed", { - internalCompaniesCount: internalCompanyCount, - externalCompaniesCount: externalCompanyCount, - companiesUpdated: updatedCount, - }); -}; diff --git a/api/src/modules/cw-utils/sites/companySites.ts b/api/src/modules/cw-utils/sites/companySites.ts deleted file mode 100644 index 3007028..0000000 --- a/api/src/modules/cw-utils/sites/companySites.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { connectWiseApi } from "../../../constants"; - -export interface CWCompanySite { - id: number; - name: string; - addressLine1: string; - addressLine2?: string; - city: string; - stateReference: { id: number; identifier: string; name: string } | null; - zip: string; - country: { id: number; name: string } | null; - phoneNumber: string; - faxNumber: string; - taxCodeId: number | null; - expenseReimbursement: number; - primaryAddressFlag: boolean; - defaultShippingFlag: boolean; - defaultBillingFlag: boolean; - defaultMailingFlag: boolean; - mobileGuid: string; - calendar: { id: number; name: string } | null; - timeZone: { id: number; name: string } | null; - company: { id: number; identifier: string; name: string }; - _info: Record; -} - -/** - * Fetch all sites for a ConnectWise company. - * - * @param cwCompanyId - The ConnectWise company ID - * @returns Array of CW company sites - */ -export const fetchCompanySites = async ( - cwCompanyId: number, -): Promise => { - const response = await connectWiseApi.get( - `/company/companies/${cwCompanyId}/sites?pageSize=1000`, - ); - return response.data; -}; - -/** - * Fetch a single site by CW site ID for a given company. - * - * @param cwCompanyId - The ConnectWise company ID - * @param cwSiteId - The ConnectWise site ID - * @returns The CW company site - */ -export const fetchCompanySite = async ( - cwCompanyId: number, - cwSiteId: number, -): Promise => { - const response = await connectWiseApi.get( - `/company/companies/${cwCompanyId}/sites/${cwSiteId}`, - ); - return response.data; -}; - -/** - * Serialize a CW site into a clean API-friendly object. - */ -export const serializeCwSite = (site: CWCompanySite) => ({ - id: site.id, - name: site.name, - address: { - line1: site.addressLine1, - line2: site.addressLine2 ?? null, - city: site.city, - state: site.stateReference?.name ?? null, - zip: site.zip, - country: site.country?.name ?? "United States", - }, - phoneNumber: site.phoneNumber || null, - faxNumber: site.faxNumber || null, - primaryAddressFlag: site.primaryAddressFlag, - defaultShippingFlag: site.defaultShippingFlag, - defaultBillingFlag: site.defaultBillingFlag, - defaultMailingFlag: site.defaultMailingFlag, -}); diff --git a/api/src/modules/cw-utils/updateCompany.ts b/api/src/modules/cw-utils/updateCompany.ts index 17fd699..75e135b 100644 --- a/api/src/modules/cw-utils/updateCompany.ts +++ b/api/src/modules/cw-utils/updateCompany.ts @@ -1,24 +1,7 @@ import { Company } from "../../../generated/prisma/client"; import { prisma } from "../../constants"; -import { fetchCwCompanyById } from "./fetchCompany"; - export const updateCwInternalCompany = async ( companyId: number, ): Promise => { - const cwCompany = await fetchCwCompanyById(companyId); - if (!cwCompany) return null; - - const updatedCompany = await prisma.company.upsert({ - where: { cw_CompanyId: cwCompany.id }, - create: { - cw_CompanyId: cwCompany.id, - cw_Identifier: cwCompany.identifier, - name: cwCompany.name, - }, - update: { - name: cwCompany.name, - }, - }); - - return updatedCompany; + return prisma.company.findFirst({ where: { cw_CompanyId: companyId } }); }; diff --git a/api/src/modules/cw-utils/userDefinedFields/index.ts b/api/src/modules/cw-utils/userDefinedFields/index.ts deleted file mode 100644 index b9faf74..0000000 --- a/api/src/modules/cw-utils/userDefinedFields/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { userDefinedFieldsCw } from "./userDefinedFields"; -export type { - CWUserDefinedField, - CWUserDefinedFieldOption, - CWUserDefinedFieldInfo, -} from "./udf.types"; diff --git a/api/src/modules/cw-utils/userDefinedFields/userDefinedFields.ts b/api/src/modules/cw-utils/userDefinedFields/userDefinedFields.ts deleted file mode 100644 index e31c97b..0000000 --- a/api/src/modules/cw-utils/userDefinedFields/userDefinedFields.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Collection } from "@discordjs/collection"; -import { connectWiseApi, redis } from "../../../constants"; -import { events } from "../../globalEvents"; -import { CWUserDefinedField } from "./udf.types"; - -const REDIS_KEY = "cw:userDefinedFields"; - -/** In-memory cache of all CW User Defined Fields, keyed by UDF id */ -let cache: Collection = new Collection(); - -export const userDefinedFieldsCw = { - /** - * Get Cache - * - * Returns the current in-memory Collection of all User Defined Fields. - * If the cache is empty, it will attempt to hydrate from Redis first, - * then fall back to a live API fetch. - */ - get: async (): Promise> => { - if (cache.size > 0) return cache; - - // Try hydrating from Redis - const stored = await redis.get(REDIS_KEY); - if (stored) { - const parsed: CWUserDefinedField[] = JSON.parse(stored); - cache = new Collection(parsed.map((udf) => [udf.id, udf])); - return cache; - } - - // Nothing cached anywhere — do a live fetch - return userDefinedFieldsCw.refresh(); - }, - - /** - * Fetch All User Defined Fields - * - * Fetches all UDFs from the ConnectWise API. - * Does NOT update the cache — use `refresh()` for that. - */ - fetchAll: async (): Promise> => { - const allItems = new Collection(); - const pageSize = 1000; - - const response = await connectWiseApi.get( - `/system/userDefinedFields?pageSize=${pageSize}`, - ); - const items: CWUserDefinedField[] = response.data; - - for (const item of items) { - allItems.set(item.id, item); - } - - return allItems; - }, - - /** - * Refresh - * - * Fetches all UDFs from ConnectWise, replaces the in-memory cache - * and persists the snapshot to Redis. - */ - refresh: async (): Promise> => { - events.emit("cw:udf:refresh:started"); - - const allItems = await userDefinedFieldsCw.fetchAll(); - cache = allItems; - - // Persist to Redis - await redis.set(REDIS_KEY, JSON.stringify([...allItems.values()])); - - events.emit("cw:udf:refresh:completed", { count: allItems.size }); - return cache; - }, - - /** - * Find by ID - * - * Returns a single UDF by its ConnectWise ID from the cache. - */ - findById: async (id: number): Promise => { - const items = await userDefinedFieldsCw.get(); - return items.get(id); - }, - - /** - * Find by Caption - * - * Returns the first UDF matching the given caption (case-insensitive). - */ - findByCaption: async ( - caption: string, - ): Promise => { - const items = await userDefinedFieldsCw.get(); - const lowerCaption = caption.toLowerCase(); - return items.find((udf) => udf.caption.toLowerCase() === lowerCaption); - }, - - /** - * Find by Screen ID - * - * Returns all UDFs associated with a given screenId. - */ - findByScreenId: async ( - screenId: string, - ): Promise> => { - const items = await userDefinedFieldsCw.get(); - return items.filter((udf) => udf.screenId === screenId); - }, - - /** - * Invalidate - * - * Clears the in-memory cache and removes the Redis key. - */ - invalidate: async (): Promise => { - cache = new Collection(); - await redis.del(REDIS_KEY); - }, -}; diff --git a/api/src/modules/pdf-utils/generateQuote.ts b/api/src/modules/pdf-utils/generateQuote.ts index 6d0b0fe..81556b5 100644 --- a/api/src/modules/pdf-utils/generateQuote.ts +++ b/api/src/modules/pdf-utils/generateQuote.ts @@ -120,11 +120,22 @@ const fonts = { }, }; -const printer = new PdfPrinter(fonts as never); +const noOpUrlResolver = { + resolve: () => Promise.resolve(), + resolved: () => Promise.resolve([]), +}; + +const printer = new PdfPrinter(fonts as never, undefined, noOpUrlResolver); const fmt = (n: number) => "$" + n.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); +const fmtMoney = (n: number) => { + const abs = Math.abs(n); + const formatted = "$" + abs.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); + return n < 0 ? "-" + formatted : formatted; +}; + const hr = (color = DIVIDER, weight = 0.75) => ({ canvas: [ { @@ -152,12 +163,12 @@ function loadLogoDataUrl(logoPath: string): string | null { export async function generateQuote( data: QuoteData, theme: Partial = {}, - logoPath = DEFAULT_LOGO_PATH, + logoPath = DEFAULT_LOGO_PATH ): Promise { const t: QuoteTheme = { ...DEFAULT_THEME, ...theme }; const subTotal = data.lineItems.reduce( (sum, item) => sum + item.qty * item.unitPrice, - 0, + 0 ); const taxableSubTotal = Math.max(0, data.taxableSubtotal ?? subTotal); const taxAmount = taxableSubTotal * data.tax.rate; @@ -166,6 +177,13 @@ export async function generateQuote( const showPricing = data.showLineItemPricing ?? false; + const discountTotal = data.lineItems.reduce((sum, item) => { + const lineTotal = item.qty * item.unitPrice; + return lineTotal < 0 ? sum + lineTotal : sum; + }, 0); + const hasDiscounts = discountTotal < 0; + const showDiscount = !showPricing && hasDiscounts; + const tableHeader = [ { text: "Qty", style: "thCell", alignment: "center" }, { text: "Description", style: "thCell" }, @@ -174,10 +192,12 @@ export async function generateQuote( { text: "Unit Price", style: "thCell", alignment: "right" }, { text: "Total", style: "thCell", alignment: "right" }, ] + : showDiscount + ? [{ text: "", style: "thCell", alignment: "right" }] : []), ]; - const colCount = showPricing ? 4 : 2; + const colCount = showPricing ? 4 : showDiscount ? 3 : 2; const tableRows: Record[][] = []; for (const item of data.lineItems) { @@ -202,13 +222,25 @@ export async function generateQuote( ...(showPricing ? [ { - text: fmt(item.unitPrice), + text: fmtMoney(item.unitPrice), style: "tdCell", alignment: "right", noWrap: true, }, { - text: fmt(item.qty * item.unitPrice), + text: fmtMoney(item.qty * item.unitPrice), + style: "tdCell", + alignment: "right", + noWrap: true, + }, + ] + : showDiscount + ? [ + { + text: + item.qty * item.unitPrice < 0 + ? fmtMoney(item.qty * item.unitPrice) + : "", style: "tdCell", alignment: "right", noWrap: true, @@ -231,7 +263,7 @@ export async function generateQuote( number, number, number, - number, + number ], info: { @@ -563,7 +595,7 @@ export async function generateQuote( table: { headerRows: 1, dontBreakRows: true, - widths: showPricing ? [40, "*", 75, 75] : [40, "*"], + widths: showPricing ? [40, "*", 75, 75] : showDiscount ? [40, "*", 75] : [40, "*"], body: [tableHeader, ...tableRows], }, layout: { @@ -614,6 +646,26 @@ export async function generateQuote( border: [false, false, false, true], }, ], + ...(hasDiscounts + ? [ + [ + { + text: "Discount", + style: "totalsLabel", + margin: [0, 5, 0, 5], + border: [false, false, false, true], + }, + { + text: fmtMoney(discountTotal), + style: "totalsValue", + alignment: "right", + noWrap: true, + margin: [0, 5, 0, 5], + border: [false, false, false, true], + }, + ], + ] + : []), [ { text: data.tax.label, @@ -651,7 +703,8 @@ export async function generateQuote( ], }, layout: { - hLineWidth: (i: number) => (i >= 1 && i <= 2 ? 0.5 : 0), + hLineWidth: (i: number) => + i >= 1 && i <= (hasDiscounts ? 3 : 2) ? 0.5 : 0, vLineWidth: () => 0, hLineColor: () => "#E0D6C6", }, diff --git a/api/src/modules/workers/cache/refreshActiveOpportunities.ts b/api/src/modules/workers/cache/refreshActiveOpportunities.ts deleted file mode 100644 index f81a94c..0000000 --- a/api/src/modules/workers/cache/refreshActiveOpportunities.ts +++ /dev/null @@ -1,427 +0,0 @@ -import { Socket } from "socket.io-client"; -import { - createWorkerJob, - emitWorkerGlobalEvent, - workerLog, -} from "../jobFactory"; -import { WorkerQueue } from "../queues"; -import { - TTL_ARCHIVED_MS, - fetchAndCacheActivities, - fetchAndCacheNotes, - fetchAndCacheContacts, - fetchAndCacheProducts, - fetchAndCacheOppCwData, - fetchAndCacheCompanyCwData, - companyCwCacheKey, -} from "../../cache/opportunityCache"; -import { computeCacheTTL } from "../../algorithms/computeCacheTTL"; -import { prisma, redis } from "../../../constants"; - -/** - * Worker factory for active opportunity cache refresh. - * - * Runs the unified opportunity cache refresh pass for all opportunities. - * Active/recent opportunities use adaptive TTL, while archived opportunities - * (where adaptive TTL resolves to null) are refreshed with TTL_ARCHIVED_MS. - * Checks which cache keys have expired and re-fetches only those from - * ConnectWise. - * - * Designed to be called on the active cache job interval. - * - * @param socket - Socket.IO client connection to manager - * @returns Promise that resolves when refresh completes - */ -export async function refreshActiveOpportunitiesWorker( - socket: Socket, - opts?: { - runFullRefresh?: () => Promise; - }, -): Promise { - return createWorkerJob( - socket, - WorkerQueue.REFRESH_ACTIVE_OPPORTUNITIES, - async (workerSocket: Socket) => { - if (opts?.runFullRefresh) { - workerLog( - workerSocket, - "[active-refresh] Starting full opportunities refresh stage", - ); - await opts.runFullRefresh(); - workerLog( - workerSocket, - "[active-refresh] Completed full opportunities refresh stage", - ); - } - - const lockKey = "worker-lock:cache:opportunities:refresh:active"; - const lockValue = `${process.pid}:${Date.now()}:${Math.random()}`; - const lockTtlMs = Number(Bun.env.ACTIVE_REFRESH_LOCK_TTL_MS ?? "1800000"); - const lockSet = await redis.set( - lockKey, - lockValue, - "PX", - lockTtlMs, - "NX", - ); - - if (lockSet !== "OK") { - workerLog( - workerSocket, - `[active-refresh] Skipping run: lock already held (${lockKey})`, - "WARN", - ); - return; - } - - try { - await performActiveOpportunityRefresh(workerSocket); - } finally { - const currentLockValue = await redis.get(lockKey); - if (currentLockValue === lockValue) { - await redis.del(lockKey); - } - } - }, - ); -} - -/** - * Core logic for active opportunity cache refresh. - * - * Queries all opportunities, checks which cache keys have expired, and - * re-fetches from ConnectWise only for expired entries. - */ -async function performActiveOpportunityRefresh( - workerSocket: Socket, -): Promise { - const opportunities = await prisma.opportunity.findMany({ - select: { - cwOpportunityId: true, - closedFlag: true, - closedDate: true, - expectedCloseDate: true, - cwLastUpdated: true, - statusCwId: true, - company: { select: { cw_CompanyId: true } }, - }, - orderBy: { cwLastUpdated: "desc" }, - }); - - workerLog( - workerSocket, - `[active-refresh] Starting refresh for ${opportunities.length} opportunities`, - ); - - emitWorkerGlobalEvent(workerSocket, "cache:opportunities:refresh:started", { - totalOpportunities: opportunities.length, - }); - - let activitiesRefreshed = 0; - let companiesRefreshed = 0; - let notesRefreshed = 0; - let contactsRefreshed = 0; - let productsRefreshed = 0; - let oppCwDataRefreshed = 0; - let archivedCount = 0; - - const eligibleOpportunities: Array<{ - cwOpportunityId: number; - ttl: number; - companyId: number | null; - }> = []; - const companyTtlById = new Map(); - - for (const opp of opportunities) { - const adaptiveTtl = computeCacheTTL({ - closedFlag: opp.closedFlag, - closedDate: opp.closedDate, - expectedCloseDate: opp.expectedCloseDate, - lastUpdated: opp.cwLastUpdated, - }); - const ttl = adaptiveTtl ?? TTL_ARCHIVED_MS; - if (adaptiveTtl === null) archivedCount++; - - const companyId = opp.company?.cw_CompanyId ?? null; - eligibleOpportunities.push({ - cwOpportunityId: opp.cwOpportunityId, - ttl, - companyId, - }); - - if (companyId === null) continue; - - const prevTtl = companyTtlById.get(companyId) ?? 0; - companyTtlById.set(companyId, Math.max(prevTtl, ttl)); - } - - // Batch-check which keys already exist via a single pipeline. - // One EXISTS command per key avoids Redis EXISTS multi-key count semantics. - const pipeline = redis.pipeline(); - for (const opp of eligibleOpportunities) { - pipeline.exists(`opp:cw-data:${opp.cwOpportunityId}`); - pipeline.exists(`opp:activities:${opp.cwOpportunityId}`); - pipeline.exists(`opp:notes:${opp.cwOpportunityId}`); - pipeline.exists(`opp:contacts:${opp.cwOpportunityId}`); - pipeline.exists(`opp:products:${opp.cwOpportunityId}`); - } - for (const companyId of Array.from(companyTtlById.keys())) { - pipeline.exists(companyCwCacheKey(companyId)); - } - const existsResults = (await pipeline.exec()) || []; - - const existsAt = (index: number): boolean => { - const value = existsResults[index]?.[1]; - return typeof value === "number" && value > 0; - }; - - let existsIndex = 0; - const oppExistsById = new Map< - number, - { - oppCwDataExists: boolean; - activitiesExists: boolean; - notesExists: boolean; - contactsExists: boolean; - productsExists: boolean; - } - >(); - - for (const opp of eligibleOpportunities) { - oppExistsById.set(opp.cwOpportunityId, { - oppCwDataExists: existsAt(existsIndex++), - activitiesExists: existsAt(existsIndex++), - notesExists: existsAt(existsIndex++), - contactsExists: existsAt(existsIndex++), - productsExists: existsAt(existsIndex++), - }); - } - - const companyCacheExistsById = new Map(); - for (const companyId of Array.from(companyTtlById.keys())) { - companyCacheExistsById.set(companyId, existsAt(existsIndex++)); - } - - const refreshTasks: (() => Promise)[] = []; - let plannedOppCwData = 0; - let plannedActivities = 0; - let plannedNotes = 0; - let plannedContacts = 0; - let plannedProducts = 0; - let plannedCompanies = 0; - - for (const opp of eligibleOpportunities) { - const existsForOpp = oppExistsById.get(opp.cwOpportunityId); - if (!existsForOpp) continue; - - if (!existsForOpp.oppCwDataExists) { - plannedOppCwData++; - refreshTasks.push(async () => { - try { - await fetchAndCacheOppCwData(opp.cwOpportunityId, opp.ttl); - oppCwDataRefreshed++; - } catch (error) { - workerLog( - workerSocket, - `[active-refresh] oppCwData refresh failed for opp${opp.cwOpportunityId}: ${describeError(error)}`, - ); - } - }); - } - - if (!existsForOpp.activitiesExists) { - plannedActivities++; - refreshTasks.push(async () => { - try { - await fetchAndCacheActivities(opp.cwOpportunityId, opp.ttl); - activitiesRefreshed++; - } catch (error) { - workerLog( - workerSocket, - `[active-refresh] activities refresh failed for opp${opp.cwOpportunityId}: ${describeError(error)}`, - ); - } - }); - } - - if (!existsForOpp.notesExists) { - plannedNotes++; - refreshTasks.push(async () => { - try { - await fetchAndCacheNotes(opp.cwOpportunityId, opp.ttl); - notesRefreshed++; - } catch (error) { - workerLog( - workerSocket, - `[active-refresh] notes refresh failed for opp${opp.cwOpportunityId}: ${describeError(error)}`, - ); - } - }); - } - - if (!existsForOpp.contactsExists) { - plannedContacts++; - refreshTasks.push(async () => { - try { - await fetchAndCacheContacts(opp.cwOpportunityId, opp.ttl); - contactsRefreshed++; - } catch (error) { - workerLog( - workerSocket, - `[active-refresh] contacts refresh failed for opp${opp.cwOpportunityId}: ${describeError(error)}`, - ); - } - }); - } - - if (!existsForOpp.productsExists) { - plannedProducts++; - refreshTasks.push(async () => { - try { - await fetchAndCacheProducts(opp.cwOpportunityId, opp.ttl); - productsRefreshed++; - } catch (error) { - workerLog( - workerSocket, - `[active-refresh] products refresh failed for opp${opp.cwOpportunityId}: ${describeError(error)}`, - ); - } - }); - } - } - - for (const [companyId, ttl] of Array.from(companyTtlById.entries())) { - const companyExists = companyCacheExistsById.get(companyId) ?? false; - if (companyExists) continue; - - plannedCompanies++; - refreshTasks.push(async () => { - try { - await fetchAndCacheCompanyCwData(companyId, ttl); - companiesRefreshed++; - } catch (error) { - workerLog( - workerSocket, - `[active-refresh] company data refresh failed for company${companyId}: ${describeError(error)}`, - ); - } - }); - } - - if (companyTtlById.size > 0) { - const missingCompanies = Array.from(companyTtlById.keys()).filter( - (id) => !(companyCacheExistsById.get(id) ?? false), - ).length; - workerLog( - workerSocket, - `[active-refresh] Company cache checks: ${companyTtlById.size} unique, ${missingCompanies} missing`, - ); - } - - workerLog( - workerSocket, - `[active-refresh] Planned tasks: eligible=${eligibleOpportunities.length}, archived=${archivedCount}, totalTasks=${refreshTasks.length}, oppCwData=${plannedOppCwData}, activities=${plannedActivities}, notes=${plannedNotes}, contacts=${plannedContacts}, products=${plannedProducts}, companies=${plannedCompanies}`, - ); - - if (refreshTasks.length === 0) { - workerLog(workerSocket, `[active-refresh] No cache keys needed refresh`); - } - - // Run refresh tasks via a continuous worker pool (no inter-batch idle waits). - const parsedConcurrency = Number(Bun.env.ACTIVE_REFRESH_CONCURRENCY ?? "12"); - const CONCURRENCY = Number.isFinite(parsedConcurrency) - ? Math.max(1, Math.floor(parsedConcurrency)) - : 12; - const progressEvery = Math.max( - 1, - Number(Bun.env.ACTIVE_REFRESH_PROGRESS_EVERY ?? "50") || 50, - ); - - workerLog( - workerSocket, - `[active-refresh] Runner config: concurrency=${CONCURRENCY}, progressEvery=${progressEvery}`, - "DEBUG", - ); - - let completedTasks = 0; - let failedTasks = 0; - let nextTaskIndex = 0; - - const runWorker = async () => { - while (true) { - const taskIndex = nextTaskIndex; - nextTaskIndex++; - - const task = refreshTasks[taskIndex]; - if (!task) return; - - try { - await task(); - } catch (error) { - failedTasks++; - workerLog( - workerSocket, - `[active-refresh] task ${taskIndex + 1}/${refreshTasks.length} failed: ${describeError(error)}`, - ); - } - - completedTasks++; - const shouldLogProgress = - completedTasks % progressEvery === 0 || - completedTasks === refreshTasks.length; - if (shouldLogProgress) { - workerLog( - workerSocket, - `[active-refresh] Progress: completedTasks=${completedTasks}/${refreshTasks.length}, failedTasks=${failedTasks}`, - "DEBUG", - ); - } - } - }; - - await Promise.all( - Array.from( - { length: Math.min(CONCURRENCY, Math.max(1, refreshTasks.length)) }, - () => runWorker(), - ), - ); - - if (failedTasks > 0) { - workerLog( - workerSocket, - `[active-refresh] ${failedTasks} task(s) encountered errors`, - ); - } - - emitWorkerGlobalEvent(workerSocket, "cache:opportunities:refresh:completed", { - totalOpportunities: opportunities.length, - activitiesRefreshed, - companiesRefreshed, - notesRefreshed, - contactsRefreshed, - productsRefreshed, - oppCwDataRefreshed, - archivedCount, - }); - - workerLog( - workerSocket, - `[active-refresh] Completed: ${activitiesRefreshed} activities, ${notesRefreshed} notes, ${contactsRefreshed} contacts, ${productsRefreshed} products, ${oppCwDataRefreshed} opp cw data, ${companiesRefreshed} companies, ${archivedCount} archived`, - ); -} - -/** - * Build a concise error description for logging. - */ -function describeError(err: unknown): string { - if (typeof err !== "object" || err === null) return String(err); - const e = err as Record; - if (e.isAxiosError) { - const method = (e.config?.method ?? "?").toUpperCase(); - const url = e.config?.url ?? "unknown"; - const code = e.code ?? ""; - const status = e.response?.status ?? ""; - return `${method} ${url} -> ${code || `HTTP ${status}`} (${e.message})`; - } - return e.message ?? String(err); -} diff --git a/api/src/modules/workers/cache/refreshArchivedOpportunities.ts b/api/src/modules/workers/cache/refreshArchivedOpportunities.ts deleted file mode 100644 index be3a07f..0000000 --- a/api/src/modules/workers/cache/refreshArchivedOpportunities.ts +++ /dev/null @@ -1,375 +0,0 @@ -import { Socket } from "socket.io-client"; -import { createWorkerJob, workerLog } from "../jobFactory"; -import { WorkerQueue } from "../queues"; -import { - TTL_ARCHIVED_MS, - fetchAndCacheActivities, - fetchAndCacheNotes, - fetchAndCacheContacts, - fetchAndCacheProducts, - fetchAndCacheOppCwData, - fetchAndCacheCompanyCwData, - companyCwCacheKey, -} from "../../cache/opportunityCache"; -import { prisma, redis } from "../../../constants"; - -interface ArchiveRefreshOptions { - /** - * When true, overwrite every cache key without checking if it exists. - * Used for midnight rebuild to ensure all keys are fresh. - * - * When false, only populate missing keys. Used on startup to avoid - * large CW bursts on every process restart. - * - * Defaults to false. - */ - force?: boolean; -} - -/** - * Worker factory for archived opportunity cache refresh. - * - * Refreshes cache for opportunities that are closed more than 30 days ago. - * These opportunities fall outside the adaptive TTL window and are rebuilt - * with a fixed 24-hour TTL. - * - * Typically called once per day at midnight (with force=true) to ensure - * archived deals are not stale. On startup, force=false to avoid large - * CW bursts. - * - * @param socket - Socket.IO client connection to manager - * @param options - Configuration options (force, etc.) - * @returns Promise that resolves when refresh completes - */ -export async function refreshArchivedOpportunitiesWorker( - socket: Socket, - options: ArchiveRefreshOptions = {}, -): Promise { - return createWorkerJob( - socket, - WorkerQueue.REFRESH_ARCHIVED_OPPORTUNITIES, - async (workerSocket: Socket) => { - const lockKey = "worker-lock:cache:opportunities:refresh:archived"; - const lockValue = `${process.pid}:${Date.now()}:${Math.random()}`; - const lockTtlMs = Number( - Bun.env.ARCHIVED_REFRESH_LOCK_TTL_MS ?? "10800000", - ); - const lockSet = await redis.set( - lockKey, - lockValue, - "PX", - lockTtlMs, - "NX", - ); - - if (lockSet !== "OK") { - workerLog( - workerSocket, - `[archived-refresh] Skipping run: lock already held (${lockKey})`, - "WARN", - ); - return; - } - - try { - await performArchivedOpportunityRefresh( - workerSocket, - options.force ?? false, - ); - } finally { - const currentLockValue = await redis.get(lockKey); - if (currentLockValue === lockValue) { - await redis.del(lockKey); - } - } - }, - ); -} - -/** - * Core logic for archived opportunity cache refresh. - * - * Queries opportunities closed more than 30 days ago and refreshes their cache - * with a fixed 24-hour TTL. - * - * @param workerSocket - Worker socket for logging - * @param force - If true, refresh all keys. If false, only refresh missing keys. - */ -async function performArchivedOpportunityRefresh( - workerSocket: Socket, - force: boolean, -): Promise { - const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); - - const opportunities = await prisma.opportunity.findMany({ - where: { - closedFlag: true, - OR: [{ closedDate: { lt: thirtyDaysAgo } }, { closedDate: null }], - }, - select: { - cwOpportunityId: true, - company: { select: { cw_CompanyId: true } }, - }, - orderBy: { cwLastUpdated: "desc" }, - }); - - const label = force ? "midnight rebuild" : "startup warm"; - workerLog( - workerSocket, - `[archived-refresh] Starting ${label} for ${opportunities.length} archived opportunities`, - ); - - if (opportunities.length === 0) { - workerLog( - workerSocket, - `[archived-refresh] No archived opportunities found`, - ); - return; - } - - const uniqueCompanyIds = Array.from( - new Set( - opportunities - .map((opp) => opp.company?.cw_CompanyId) - .filter((id): id is number => id !== undefined), - ), - ); - - const oppMissingById = new Map< - number, - { - oppCwDataMissing: boolean; - activitiesMissing: boolean; - notesMissing: boolean; - contactsMissing: boolean; - productsMissing: boolean; - } - >(); - const companyMissingById = new Map(); - - if (force) { - for (const opp of opportunities) { - oppMissingById.set(opp.cwOpportunityId, { - oppCwDataMissing: true, - activitiesMissing: true, - notesMissing: true, - contactsMissing: true, - productsMissing: true, - }); - } - for (const companyId of uniqueCompanyIds) { - companyMissingById.set(companyId, true); - } - } else { - const pipeline = redis.pipeline(); - for (const opp of opportunities) { - pipeline.exists(`opp:cw-data:${opp.cwOpportunityId}`); - pipeline.exists(`opp:activities:${opp.cwOpportunityId}`); - pipeline.exists(`opp:notes:${opp.cwOpportunityId}`); - pipeline.exists(`opp:contacts:${opp.cwOpportunityId}`); - pipeline.exists(`opp:products:${opp.cwOpportunityId}`); - } - for (const companyId of uniqueCompanyIds) { - pipeline.exists(companyCwCacheKey(companyId)); - } - - const results = (await pipeline.exec()) || []; - const existsAt = (index: number): boolean => { - const value = results[index]?.[1]; - return typeof value === "number" && value > 0; - }; - - let idx = 0; - for (const opp of opportunities) { - oppMissingById.set(opp.cwOpportunityId, { - oppCwDataMissing: !existsAt(idx++), - activitiesMissing: !existsAt(idx++), - notesMissing: !existsAt(idx++), - contactsMissing: !existsAt(idx++), - productsMissing: !existsAt(idx++), - }); - } - for (const companyId of uniqueCompanyIds) { - companyMissingById.set(companyId, !existsAt(idx++)); - } - } - - const refreshTasks: (() => Promise)[] = []; - let plannedOppCwData = 0; - let plannedActivities = 0; - let plannedNotes = 0; - let plannedContacts = 0; - let plannedProducts = 0; - let plannedCompanies = 0; - - for (const opp of opportunities) { - const missing = oppMissingById.get(opp.cwOpportunityId); - if (!missing) continue; - - if (missing.oppCwDataMissing) { - plannedOppCwData++; - refreshTasks.push(async () => { - try { - await fetchAndCacheOppCwData(opp.cwOpportunityId, TTL_ARCHIVED_MS); - } catch (error) { - workerLog( - workerSocket, - `[archived-refresh] oppCwData failed for opp${opp.cwOpportunityId}: ${describeError(error)}`, - ); - } - }); - } - - if (missing.activitiesMissing) { - plannedActivities++; - refreshTasks.push(async () => { - try { - await fetchAndCacheActivities(opp.cwOpportunityId, TTL_ARCHIVED_MS); - } catch (error) { - workerLog( - workerSocket, - `[archived-refresh] activities failed for opp${opp.cwOpportunityId}: ${describeError(error)}`, - ); - } - }); - } - - if (missing.notesMissing) { - plannedNotes++; - refreshTasks.push(async () => { - try { - await fetchAndCacheNotes(opp.cwOpportunityId, TTL_ARCHIVED_MS); - } catch (error) { - workerLog( - workerSocket, - `[archived-refresh] notes failed for opp${opp.cwOpportunityId}: ${describeError(error)}`, - ); - } - }); - } - - if (missing.contactsMissing) { - plannedContacts++; - refreshTasks.push(async () => { - try { - await fetchAndCacheContacts(opp.cwOpportunityId, TTL_ARCHIVED_MS); - } catch (error) { - workerLog( - workerSocket, - `[archived-refresh] contacts failed for opp${opp.cwOpportunityId}: ${describeError(error)}`, - ); - } - }); - } - - if (missing.productsMissing) { - plannedProducts++; - refreshTasks.push(async () => { - try { - await fetchAndCacheProducts(opp.cwOpportunityId, TTL_ARCHIVED_MS); - } catch (error) { - workerLog( - workerSocket, - `[archived-refresh] products failed for opp${opp.cwOpportunityId}: ${describeError(error)}`, - ); - } - }); - } - } - - for (const companyId of uniqueCompanyIds) { - const companyMissing = companyMissingById.get(companyId) ?? true; - if (!companyMissing) continue; - - plannedCompanies++; - refreshTasks.push(async () => { - try { - await fetchAndCacheCompanyCwData(companyId, TTL_ARCHIVED_MS); - } catch (error) { - workerLog( - workerSocket, - `[archived-refresh] company data failed for company${companyId}: ${describeError(error)}`, - ); - } - }); - } - - workerLog( - workerSocket, - `[archived-refresh] Planned tasks (${label}): opportunities=${opportunities.length}, totalTasks=${refreshTasks.length}, oppCwData=${plannedOppCwData}, activities=${plannedActivities}, notes=${plannedNotes}, contacts=${plannedContacts}, products=${plannedProducts}, companies=${plannedCompanies}, uniqueCompanies=${uniqueCompanyIds.length}`, - ); - - if (refreshTasks.length === 0) { - workerLog( - workerSocket, - `[archived-refresh] No cache keys needed refresh (${label})`, - ); - return; - } - - // Run with bounded concurrency and inter-batch delay - const CONCURRENCY = 6; - const BATCH_DELAY_MS = 250; - let failCount = 0; - let completedTasks = 0; - const totalBatches = Math.ceil(refreshTasks.length / CONCURRENCY); - - for (let i = 0; i < refreshTasks.length; i += CONCURRENCY) { - const batch = refreshTasks.slice(i, i + CONCURRENCY); - const batchNumber = Math.floor(i / CONCURRENCY) + 1; - try { - await Promise.all(batch.map((task) => task())); - completedTasks += batch.length; - - const shouldLogProgress = - totalBatches <= 3 || - batchNumber % 5 === 0 || - batchNumber === totalBatches; - if (shouldLogProgress) { - workerLog( - workerSocket, - `[archived-refresh] Progress: batch ${batchNumber}/${totalBatches}, completedTasks=${completedTasks}/${refreshTasks.length}`, - "DEBUG", - ); - } - } catch (error) { - failCount++; - workerLog( - workerSocket, - `[archived-refresh] error in batch at index ${i}: ${describeError(error)}`, - ); - } - - if (i + CONCURRENCY < refreshTasks.length) { - await new Promise((resolve) => setTimeout(resolve, BATCH_DELAY_MS)); - } - } - - if (failCount > 0) { - workerLog( - workerSocket, - `[archived-refresh] ${failCount} batch(es) encountered errors`, - ); - } - - workerLog( - workerSocket, - `[archived-refresh] Completed (${label}): ${opportunities.length} archived opportunities, ${refreshTasks.length} tasks`, - ); -} - -/** - * Build a concise error description for logging. - */ -function describeError(err: unknown): string { - if (typeof err !== "object" || err === null) return String(err); - const e = err as Record; - if (e.isAxiosError) { - const method = (e.config?.method ?? "?").toUpperCase(); - const url = e.config?.url ?? "unknown"; - const code = e.code ?? ""; - const status = e.response?.status ?? ""; - return `${method} ${url} → ${code || `HTTP ${status}`} (${e.message})`; - } - return e.message ?? String(err); -} diff --git a/api/src/modules/workers/coms.ts b/api/src/modules/workers/coms.ts index 79a2e46..0ef4a64 100644 --- a/api/src/modules/workers/coms.ts +++ b/api/src/modules/workers/coms.ts @@ -1,7 +1,6 @@ import { Server } from "socket.io"; import { events, EventTypes } from "../globalEvents"; import { WorkerQueue } from "./queues"; -import { reserveWorkerId } from "../../workert"; function emitGlobalEvent( name: K, diff --git a/api/src/modules/workers/dalpuri-sync.ts b/api/src/modules/workers/dalpuri-sync.ts new file mode 100644 index 0000000..f83b6e2 --- /dev/null +++ b/api/src/modules/workers/dalpuri-sync.ts @@ -0,0 +1,18 @@ +import { Socket } from "socket.io-client"; +import { executeFullDalpuriSync, executeForcedIncrementalDalpuriSync } from "dalpuri"; + +/** + * Execute a full sync from Dalpuri (ConnectWise) to the API database. + * This is the main entry point for the dalpuri sync worker. + */ +export async function executeFullSync(_workerSocket: Socket): Promise { + return executeFullDalpuriSync(); +} + +/** + * Execute an incremental sync from Dalpuri (ConnectWise) to the API database. + * Called every 5 seconds via PgBoss from the API process interval. + */ +export async function executeIncrementalSync(): Promise { + return executeForcedIncrementalDalpuriSync(); +} diff --git a/api/src/modules/workers/incremental-sync.ts b/api/src/modules/workers/incremental-sync.ts new file mode 100644 index 0000000..c455f2b --- /dev/null +++ b/api/src/modules/workers/incremental-sync.ts @@ -0,0 +1,10 @@ +import { getBoss } from "../../workert"; +import { WorkerQueue } from "./queues"; + +/** + * Enqueue a single incremental sync job via PgBoss. + * Called on an interval from the main API process so it survives worker restarts. + */ +export async function enqueueIncrementalSync(): Promise { + await getBoss().send(WorkerQueue.DALPURI_INCREMENTAL_SYNC, {}); +} diff --git a/api/src/modules/workers/queues.ts b/api/src/modules/workers/queues.ts index f013e2e..c80f70e 100644 --- a/api/src/modules/workers/queues.ts +++ b/api/src/modules/workers/queues.ts @@ -1,7 +1,10 @@ export enum WorkerQueue { - WORKER_NAMESPACE_RESERVATION = "workers/namespace/reservation", REFRESH_COMPANIES = "cw/companies/refresh", REFRESH_OPPORTUNITIES = "cw/opportunities/refresh", REFRESH_ACTIVE_OPPORTUNITIES = "cache/opportunities/refresh/active", REFRESH_ARCHIVED_OPPORTUNITIES = "cache/opportunities/refresh/archived", + DALPURI_FULL_SYNC = "dalpuri/sync/full", + DALPURI_INCREMENTAL_SYNC = "dalpuri/sync/incremental", + WORKER_NAMESPACE_RESERVATION = "worker/namespace/reservation", + REFRESH_SALES_METRICS = "cache/sales/metrics/refresh", } diff --git a/api/src/modules/workers/sales-metrics.ts b/api/src/modules/workers/sales-metrics.ts new file mode 100644 index 0000000..5a2a46d --- /dev/null +++ b/api/src/modules/workers/sales-metrics.ts @@ -0,0 +1,24 @@ +import { refreshSalesOpportunityMetricsCache } from "../cache/salesOpportunityMetricsCache"; + +const LOG_PREFIX = "[job:salesMetrics]"; + +/** + * Execute the sales opportunity metrics cache refresh. + * This is the handler function registered with PgBoss for the + * REFRESH_SALES_METRICS queue. + */ +export async function executeSalesMetricsRefresh(opts?: { + forceColdLoad?: boolean; +}): Promise { + const start = Date.now(); + console.log( + `${LOG_PREFIX} refresh started | forceColdLoad=${opts?.forceColdLoad ?? false}` + ); + try { + await refreshSalesOpportunityMetricsCache({ forceColdLoad: opts?.forceColdLoad }); + console.log(`${LOG_PREFIX} refresh completed in ${Date.now() - start}ms`); + } catch (err) { + console.error(`${LOG_PREFIX} refresh failed in ${Date.now() - start}ms`, err); + throw err; + } +} diff --git a/api/src/modules/workers/sync-manager.ts b/api/src/modules/workers/sync-manager.ts new file mode 100644 index 0000000..d015e8a --- /dev/null +++ b/api/src/modules/workers/sync-manager.ts @@ -0,0 +1,50 @@ +import { Socket } from "socket.io-client"; +import { WorkerQueue } from "./queues"; +import { createWorkerJob, workerLog } from "./jobFactory"; +import { executeFullSync } from "./dalpuri-sync"; + +/** + * Enqueue a full sync from Dalpuri (ConnectWise) to the API. + * This will run asynchronously in the worker process. + * + * @param managerSocket - The manager socket connection for communicating with worker + * @returns Promise that resolves when sync completes + * + * @example + * const socket = await ensureManagerSocketReady(); + * await enqueueDalpuriFullSync(socket); + * console.log("Sync completed!"); + */ +export async function enqueueDalpuriFullSync( + managerSocket: Socket +): Promise { + return createWorkerJob( + managerSocket, + WorkerQueue.DALPURI_FULL_SYNC, + async (workerSocket) => { + workerLog( + workerSocket, + "[dalpuri] Starting full sync from ConnectWise (CW) to API database", + "INFO" + ); + + try { + await executeFullSync(workerSocket); + workerLog( + workerSocket, + "[dalpuri] Full sync completed successfully", + "INFO" + ); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + workerLog( + workerSocket, + `[dalpuri] Full sync failed: ${errorMessage}`, + "ERROR" + ); + throw error; + } + } + ); +} diff --git a/api/src/types/CompanySourceTypes.ts b/api/src/types/CompanySourceTypes.ts index 6f407e8..559892e 100644 --- a/api/src/types/CompanySourceTypes.ts +++ b/api/src/types/CompanySourceTypes.ts @@ -1,97 +1,6 @@ import { Company } from "./ConnectWiseTypes"; -/** - * Collector-side Company shape (dalpuri Prisma model projection). - * This is intentionally verbose so collector payload handling remains type-safe. - */ -export interface CollectorCompanyRecord { - companyRecId: number; - companyId: string | null; - companyName: string | null; - phoneNbr: string | null; - phoneNbrFax: string | null; - websiteUrl: string | null; - accountNbr: string | null; - timeZoneRecId: number | null; - sicCodeId: string | null; - remitToRecId: number | null; - lastUpdate: string | Date; - updatedBy: string; - companyStatusRecId: number | null; - taxCodeRecId: number | null; - currencyRecId: number | null; - ownerLevelRecId: number | null; - userfield1: string | null; - userfield2: string | null; - userfield3: string | null; - userfield4: string | null; - userfield5: string | null; - userfield6: string | null; - userfield7: string | null; - userfield8: string | null; - userfield9: string | null; - userfield10: string | null; - deleteFlag: boolean; - dateDeleted: string | Date | null; - deletedBy: string | null; - marketRecId: number | null; - leadFlag: boolean; - leadSource: string | null; - parentCompanyRecId: number | null; - annualRevenue: string | number; - revenueYear: number | null; - nbrEmployees: number | null; - ownershipTypeRecId: number | null; - dateEntered: string | Date; - billingTermsRecId: number | null; - billingDeliveryRecId: number | null; - internalFlag: boolean; - srNotify: string | null; - autoAssignFlag: boolean; - srSignoffRecId: number; - billOverrideFlag: boolean; - billSrFlag: boolean; - billCompleteSrFlag: boolean; - billUnapprovedSrFlag: boolean; - billCompletePmFlag: boolean; - billUnapprovedPmFlag: boolean; - billRestrictDownPaymentPmFlag: boolean; - approvalFlag: boolean; - taxId: string | null; - exchangeHref: string | null; - unsubscribeFlag: boolean | null; - vendorNbr: string | null; - ivPriceHeaderRecId: number | null; - emailCcFlag: boolean; - emailCcAddress: string | null; - mobileGuid: string; - officeCalendarRecId: number | null; - lastUpdateUtc: string | Date; - dateDeletedUtc: string | Date | null; - dateEnteredUtc: string | Date; - enteredBy: string; - dateAcquiredUtc: string | Date | null; - profileLastUpdateUtc: string | Date | null; - profileLastUpdatedBy: string | null; - annualRevenueUpdateUtc: string | Date; - customerUsageTypeRecId: number | null; - notificationHistory: string; - blInvtemplateSetupRecId: number | null; - emailTemplateRecId: number | null; - optionsLastUpdateUtc: string | Date; - optionsUpdatedBy: string; - yearEstablished: number | null; - resellerId: string | null; - coreEntityCompanyId: string | null; - coreEntityUpdatedBy: string; - coreEntityLastUpdateUtc: string | Date; - m365ContactSyncFlag: boolean; - id: string | null; - creditLimit: string | number | null; - additionalDebt: string | number | null; -} - -export type CompanySourceRecord = Company | CollectorCompanyRecord; +export type CompanySourceRecord = Company; export interface NormalizedCompanyRecord { id: number; diff --git a/api/src/types/PermissionNodes.ts b/api/src/types/PermissionNodes.ts index 84863f4..b6c135a 100644 --- a/api/src/types/PermissionNodes.ts +++ b/api/src/types/PermissionNodes.ts @@ -1030,6 +1030,7 @@ export const PERMISSION_NODES = { "obj.user.permissions", "obj.user.login", "obj.user.email", + "obj.user.cwMemberId", "obj.user.image", "obj.user.createdAt", "obj.user.updatedAt", @@ -1217,7 +1218,7 @@ function collectPermissions(category: PermissionCategory): PermissionNode[] { */ export function getAllPermissionNodes(): PermissionNode[] { return Object.values(PERMISSION_NODES).flatMap((category) => - collectPermissions(category), + collectPermissions(category) ); } @@ -1239,7 +1240,7 @@ export function getPermissionNode(nodeId: string): PermissionNode | undefined { * Utility function to get all permissions in a specific category */ export function getPermissionsByCategory( - categoryKey: keyof typeof PERMISSION_NODES, + categoryKey: keyof typeof PERMISSION_NODES ): PermissionNode[] { return PERMISSION_NODES[categoryKey].permissions; } diff --git a/api/src/workert.ts b/api/src/workert.ts index 833afa6..b95c3ed 100644 --- a/api/src/workert.ts +++ b/api/src/workert.ts @@ -1,9 +1,6 @@ import { PgBoss } from "pg-boss"; import { io, Socket } from "socket.io-client"; import { WorkerQueue } from "./modules/workers/queues"; -import { refreshActiveOpportunitiesWorker } from "./modules/workers/cache/refreshActiveOpportunities"; -import { refreshOpportunities } from "./modules/cw-utils/opportunities/refreshOpportunities"; -import { opportunityCw } from "./modules/cw-utils/opportunities/opportunities"; import { setupEventDebugger } from "./modules/logging/eventDebugger"; const boss = new PgBoss(process.env.DATABASE_URL!); @@ -14,7 +11,6 @@ boss.on("error", (err) => { let bossStartPromise: Promise | null = null; let reservationQueueReady = false; -let opportunityWorkersRegistered = false; let managerSocket: Socket | null = null; let managerSocketReadyPromise: Promise | null = null; @@ -36,13 +32,16 @@ async function ensureReservationQueue(): Promise { reservationQueueReady = true; } -function ensureManagerSocketReady(): Promise { +export function ensureManagerSocketReady(): Promise { if (managerSocket && managerSocket.connected) return Promise.resolve(managerSocket); if (managerSocketReadyPromise) return managerSocketReadyPromise; managerSocket = io("http://localhost:8671", { reconnection: true, + reconnectionDelay: 1000, + reconnectionDelayMax: 5000, + reconnectionAttempts: Infinity, }); managerSocket.on("connect", () => { @@ -57,7 +56,11 @@ function ensureManagerSocketReady(): Promise { console.error("[worker] Manager socket connect_error", err.message); }); - managerSocketReadyPromise = new Promise((resolve, reject) => { + managerSocket.on("reconnect_attempt", () => { + console.log("[worker] Attempting to reconnect to manager socket..."); + }); + + managerSocketReadyPromise = new Promise((resolve) => { const socket = managerSocket!; const onConnect = () => { @@ -66,13 +69,12 @@ function ensureManagerSocketReady(): Promise { }; const onConnectError = (err: Error) => { - socket.off("connect", onConnect); - managerSocketReadyPromise = null; - reject(err); + // Log the error but don't reject - let Socket.IO handle reconnection + console.warn("[worker] Connection attempt failed:", err.message, "- retrying..."); }; socket.once("connect", onConnect); - socket.once("connect_error", onConnectError); + socket.on("connect_error", onConnectError); }); return managerSocketReadyPromise; @@ -106,70 +108,93 @@ export async function reserveWorkerId(queueType: WorkerQueue): Promise { return workerId; } -async function ensureOpportunityQueues(): Promise { +async function ensureDalpuriSyncQueue(): Promise { try { - await boss.createQueue(WorkerQueue.REFRESH_ACTIVE_OPPORTUNITIES); + await boss.createQueue(WorkerQueue.DALPURI_FULL_SYNC); } catch { - // Queue may already exist; ignore. + // Queue may already exist; ignore to keep this idempotent. + } + try { + await boss.createQueue(WorkerQueue.DALPURI_INCREMENTAL_SYNC); + } catch { + // Queue may already exist; ignore to keep this idempotent. + } + try { + await boss.createQueue(WorkerQueue.REFRESH_SALES_METRICS); + } catch { + // Queue may already exist; ignore to keep this idempotent. } } -export async function startOpportunityCacheWorkers(): Promise { - if (opportunityWorkersRegistered) return; - +/** + * Initialize the worker system. Must be called before enqueueing jobs. + * Starts PgBoss connection to PostgreSQL. + */ +export async function initializeWorkerSystem(): Promise { await ensureBossStarted(); await ensureReservationQueue(); - await ensureOpportunityQueues(); - - boss.work(WorkerQueue.REFRESH_ACTIVE_OPPORTUNITIES, async () => { - const socket = await ensureManagerSocketReady(); - await refreshActiveOpportunitiesWorker(socket, { - runFullRefresh: () => - refreshOpportunities({ - collectorFetch: () => - opportunityCw.fetchAllOpportunitiesFromCollector(), - }), - }); - }); - - opportunityWorkersRegistered = true; + await ensureDalpuriSyncQueue(); + console.log("[worker] Worker system initialized - ready for job enqueueing"); } -export async function enqueueActiveOpportunityRefreshJob(): Promise { - await ensureBossStarted(); - await ensureOpportunityQueues(); - - const jobId = await boss.send(WorkerQueue.REFRESH_ACTIVE_OPPORTUNITIES, { - enqueuedAt: new Date().toISOString(), - }); - - if (!jobId) { - throw new Error("Failed to enqueue active opportunity refresh job"); - } - - return jobId; -} - -export async function enqueueArchivedOpportunityRefreshJob( - force = false, -): Promise { - console.warn( - `[worker] enqueueArchivedOpportunityRefreshJob(force=${force}) is deprecated; enqueueing unified active refresh job instead`, - ); - return enqueueActiveOpportunityRefreshJob(); +/** + * Get the PgBoss instance for direct job enqueueing. + * Must call initializeWorkerSystem() first. + */ +export function getBoss(): PgBoss { + return boss; } if (import.meta.main) { - if (Bun.env.NODE_ENV === "development") { - setupEventDebugger({ processLabel: "WORKER" }); - } + // if (Bun.env.NODE_ENV === "development") { + // setupEventDebugger({ processLabel: "WORKER" }); + // } - startOpportunityCacheWorkers() - .then(() => { - console.log("[worker] Opportunity cache workers started"); - }) - .catch((err) => { - console.error("[worker] Failed to start opportunity cache workers", err); - process.exit(1); - }); + console.log("[worker] Worker process starting..."); + console.log( + "[worker] Connecting to PgBoss on DATABASE_URL and SocketIO on :8671" + ); + + // Ensure PgBoss is connected and queues exist + await ensureBossStarted(); + await ensureDalpuriSyncQueue(); + + // Register job handler for DALPURI_FULL_SYNC + const { enqueueDalpuriFullSync } = await import("./modules/workers/sync-manager"); + const { executeIncrementalSync } = await import("./modules/workers/dalpuri-sync"); + await boss.work(WorkerQueue.DALPURI_FULL_SYNC, async () => { + const socket = await ensureManagerSocketReady(); + await enqueueDalpuriFullSync(); + }); + console.log("[worker] Registered DALPURI_FULL_SYNC job handler"); + + await boss.work(WorkerQueue.DALPURI_INCREMENTAL_SYNC, async () => { + await executeIncrementalSync(); + }); + console.log("[worker] Registered DALPURI_INCREMENTAL_SYNC job handler"); + + // Register job handler for REFRESH_SALES_METRICS + const { executeSalesMetricsRefresh } = await import("./modules/workers/sales-metrics"); + await boss.work(WorkerQueue.REFRESH_SALES_METRICS, async (jobs) => { + const job = Array.isArray(jobs) ? jobs[0] : jobs; + const data = job?.data as { forceColdLoad?: boolean } | undefined; + const forceColdLoad = data?.forceColdLoad ?? false; + await executeSalesMetricsRefresh({ forceColdLoad }); + }); + console.log("[worker] Registered REFRESH_SALES_METRICS job handler"); + + // Initiate manager socket connection (will reconnect automatically if it fails) + ensureManagerSocketReady().catch((err) => { + console.error("[worker] Failed to establish manager socket:", err.message); + console.log("[worker] Will continue retrying in background..."); + }); + + console.log("[worker] Worker process ready - waiting for job requests"); + + // Keep process alive + process.on("SIGTERM", async () => { + console.log("[worker] SIGTERM received, shutting down gracefully"); + await boss.stop(); + process.exit(0); + }); } diff --git a/api/tests/setup.ts b/api/tests/setup.ts index 8ae242b..8d95629 100644 --- a/api/tests/setup.ts +++ b/api/tests/setup.ts @@ -50,7 +50,7 @@ mock.module("../src/managers/sessions", () => ({ Promise.resolve({ accessToken: "mock-access", refreshToken: "mock-refresh", - }), + }) ), fetch: mock(() => Promise.resolve(null)), }, @@ -93,6 +93,14 @@ mock.module("../src/constants", () => ({ unifiPassword: "test-pass", io: { of: mock(() => ({ on: mock() })) }, engine: {}, + collectorSocket: { + connected: false, + connect: mock(), + disconnect: mock(), + on: mock(), + emit: mock(), + }, + connectCollectorSocket: mock(), })); // --------------------------------------------------------------------------- @@ -108,7 +116,7 @@ mock.module("../src/constants", () => ({ * never encounter "export not found" errors. */ export function buildMockConstants( - overrides: Record = {}, + overrides: Record = {} ): Record { return { prisma: createMockPrisma(), @@ -142,6 +150,14 @@ export function buildMockConstants( unifiPassword: "test-pass", io: { of: mock(() => ({ on: mock() })) }, engine: {}, + collectorSocket: { + connected: false, + connect: mock(), + disconnect: mock(), + on: mock(), + emit: mock(), + }, + connectCollectorSocket: mock(), ...overrides, }; } @@ -152,7 +168,7 @@ export function buildMockConstants( * never encounter "export not found" errors. */ export function buildMockGlobalEvents( - overrides: Record = {}, + overrides: Record = {} ): Record { return { events: { @@ -175,7 +191,7 @@ export function createMockPrisma() { get(_target, prop) { return mock(() => Promise.resolve(null)); }, - }, + } ); return new Proxy( @@ -186,7 +202,7 @@ export function createMockPrisma() { return mock(() => Promise.resolve()); return createModelProxy(); }, - }, + } ); } @@ -200,7 +216,7 @@ export function createMockUnifi() { updateWlanConf: mock(() => Promise.resolve({})), getNetworks: mock(() => Promise.resolve([])), createSite: mock(() => - Promise.resolve({ name: "default", description: "Default" }), + Promise.resolve({ name: "default", description: "Default" }) ), getWlanGroups: mock(() => Promise.resolve([])), createWlanGroup: mock(() => Promise.resolve({})), @@ -252,10 +268,13 @@ export function buildMockRole(overrides: Record = {}) { /** Build a minimal Prisma-shaped Company row. */ export function buildMockCompany(overrides: Record = {}) { return { - id: "company-1", + id: 123, + uid: "company-1", name: "Test Company", - cw_Identifier: "TestCo", - cw_CompanyId: 123, + phone: "555-1234", + website: "https://test.com", + contacts: [], + companyAddresses: [], createdAt: new Date("2025-01-01"), updatedAt: new Date("2025-01-01"), ...overrides, @@ -354,16 +373,16 @@ export function buildMockUnifiSite(overrides: Record = {}) { /** Build a minimal Prisma-shaped Opportunity row. */ export function buildMockOpportunity(overrides: Record = {}) { return { - id: "opp-1", - cwOpportunityId: 1001, + uid: "opp-1", + id: 1001, name: "Test Opportunity", notes: "Some notes", - typeName: "New Business", - typeCwId: 1, + typeId: 1, + type: { id: 1, name: "New Business" }, stageName: "Proposal", stageCwId: 2, - statusName: "Active", - statusCwId: 3, + statusId: 3, + status: { id: 3, name: "Active" }, priorityName: "High", priorityCwId: 4, ratingName: "Hot", @@ -379,12 +398,14 @@ export function buildMockOpportunity(overrides: Record = {}) { secondarySalesRepCwId: null, companyCwId: 123, companyName: "Test Company", + companyId: "company-1", contactCwId: 200, contactName: "Jane Doe", siteCwId: 300, siteName: "Main Office", customerPO: "PO-12345", totalSalesTax: 50.0, + probability: 80, locationName: "HQ", locationCwId: 400, departmentName: "Sales", @@ -396,8 +417,9 @@ export function buildMockOpportunity(overrides: Record = {}) { closedFlag: false, closedByName: null, closedByCwId: null, - companyId: "company-1", cwLastUpdated: new Date("2026-02-28"), + cwDateEntered: new Date("2026-01-01"), + productSequence: [], createdAt: new Date("2026-01-01"), updatedAt: new Date("2026-02-28"), company: null, diff --git a/api/tests/unit/activitiesManager.test.ts b/api/tests/unit/activitiesManager.test.ts index 80432f5..6ecef0e 100644 --- a/api/tests/unit/activitiesManager.test.ts +++ b/api/tests/unit/activitiesManager.test.ts @@ -6,7 +6,7 @@ import { buildMockCWActivity } from "../setup"; // --------------------------------------------------------------------------- function createStablePrismaMock( - overrides: Record> = {}, + overrides: Record> = {} ) { return new Proxy( {}, @@ -17,7 +17,7 @@ function createStablePrismaMock( if (overrides[model]) return overrides[model]; return new Proxy({}, { get: () => mock(() => Promise.resolve(null)) }); }, - }, + } ); } @@ -34,54 +34,15 @@ describe("activities manager", () => { // fetchItem // ------------------------------------------------------------------- describe("fetchItem()", () => { - test("returns an ActivityController on success", async () => { - const cwData = buildMockCWActivity(); - mock.module("../../src/modules/cw-utils/activities/activities", () => ({ - activityCw: { - fetch: mock(() => Promise.resolve(cwData)), - fetchByCompany: mock(() => Promise.resolve([])), - fetchByOpportunity: mock(() => Promise.resolve([])), - delete: mock(() => Promise.resolve()), - countItems: mock(() => Promise.resolve(0)), - update: mock(() => Promise.resolve(cwData)), - }, - })); - mock.module("../../src/constants", () => ({ - prisma: createStablePrismaMock(), - connectWiseApi: { - get: mock(() => Promise.resolve({ data: {} })), - post: mock(() => Promise.resolve({ data: {} })), - patch: mock(() => Promise.resolve({ data: {} })), - delete: mock(() => Promise.resolve({ data: {} })), - }, - })); - - const { activities } = await import("../../src/managers/activities"); - const result = await activities.fetchItem(5001); - expect(result).toBeDefined(); - expect(result.cwActivityId).toBe(5001); - }); - - test("throws GenericError on failure", async () => { - mock.module("../../src/modules/cw-utils/activities/activities", () => ({ - activityCw: { - fetch: mock(() => Promise.reject(new Error("CW API down"))), - }, - })); - mock.module("../../src/constants", () => ({ - prisma: createStablePrismaMock(), - connectWiseApi: { - get: mock(() => Promise.resolve({ data: {} })), - }, - })); - + test("throws NotAvailable error (not yet implemented)", async () => { const { activities } = await import("../../src/managers/activities"); try { - await activities.fetchItem(9999); - expect(true).toBe(false); + await activities.fetchItem(5001); + expect(true).toBe(false); // Should not reach here } catch (e: any) { - expect(e.name).toBe("FetchActivityError"); - expect(e.status).toBeDefined(); + expect(e.name).toBe("NotAvailable"); + expect(e.status).toBe(501); + expect(e.message).toContain("not yet implemented"); } }); }); @@ -90,38 +51,16 @@ describe("activities manager", () => { // fetchPages // ------------------------------------------------------------------- describe("fetchPages()", () => { - test("returns array of ActivityControllers", async () => { - const cwData = [buildMockCWActivity(), buildMockCWActivity({ id: 5002 })]; - mock.module("../../src/constants", () => ({ - prisma: createStablePrismaMock(), - connectWiseApi: { - get: mock(() => Promise.resolve({ data: cwData })), - post: mock(() => Promise.resolve({ data: {} })), - }, - })); - mock.module("../../src/modules/cw-utils/activities/activities", () => ({ - activityCw: {}, - })); - + test("returns empty array (not yet implemented)", async () => { const { activities } = await import("../../src/managers/activities"); const result = await activities.fetchPages(1, 10); - expect(result).toBeArrayOfSize(2); + expect(result).toBeArrayOfSize(0); }); - test("clamps page to minimum 1", async () => { - const getMock = mock(() => Promise.resolve({ data: [] })); - mock.module("../../src/constants", () => ({ - prisma: createStablePrismaMock(), - connectWiseApi: { get: getMock }, - })); - mock.module("../../src/modules/cw-utils/activities/activities", () => ({ - activityCw: {}, - })); - + test("returns empty array for negative page (not yet implemented)", async () => { const { activities } = await import("../../src/managers/activities"); - await activities.fetchPages(-5, 10); - const url = getMock.mock.calls[0]?.[0] as string; - expect(url).toContain("page=1"); + const result = await activities.fetchPages(-5, 10); + expect(result).toBeArrayOfSize(0); }); }); @@ -129,23 +68,10 @@ describe("activities manager", () => { // fetchByCompany // ------------------------------------------------------------------- describe("fetchByCompany()", () => { - test("returns ActivityControllers for a company", async () => { - const items = [buildMockCWActivity()]; - mock.module("../../src/modules/cw-utils/activities/activities", () => ({ - activityCw: { - fetchByCompany: mock(() => Promise.resolve(items)), - }, - })); - mock.module("../../src/constants", () => ({ - prisma: createStablePrismaMock(), - connectWiseApi: { - get: mock(() => Promise.resolve({ data: {} })), - }, - })); - + test("returns empty array (not yet implemented)", async () => { const { activities } = await import("../../src/managers/activities"); const result = await activities.fetchByCompany(123); - expect(result).toBeArrayOfSize(1); + expect(result).toBeArrayOfSize(0); }); }); @@ -153,23 +79,10 @@ describe("activities manager", () => { // fetchByOpportunity // ------------------------------------------------------------------- describe("fetchByOpportunity()", () => { - test("returns ActivityControllers for an opportunity", async () => { - const items = [buildMockCWActivity()]; - mock.module("../../src/modules/cw-utils/activities/activities", () => ({ - activityCw: { - fetchByOpportunity: mock(() => Promise.resolve(items)), - }, - })); - mock.module("../../src/constants", () => ({ - prisma: createStablePrismaMock(), - connectWiseApi: { - get: mock(() => Promise.resolve({ data: {} })), - }, - })); - + test("returns empty array (not yet implemented)", async () => { const { activities } = await import("../../src/managers/activities"); const result = await activities.fetchByOpportunity(1001); - expect(result).toBeArrayOfSize(1); + expect(result).toBeArrayOfSize(0); }); }); @@ -221,22 +134,10 @@ describe("activities manager", () => { // count // ------------------------------------------------------------------- describe("count()", () => { - test("returns count from activityCw", async () => { - mock.module("../../src/modules/cw-utils/activities/activities", () => ({ - activityCw: { - countItems: mock(() => Promise.resolve(42)), - }, - })); - mock.module("../../src/constants", () => ({ - prisma: createStablePrismaMock(), - connectWiseApi: { - get: mock(() => Promise.resolve({ data: {} })), - }, - })); - + test("returns 0 (not yet implemented)", async () => { const { activities } = await import("../../src/managers/activities"); const result = await activities.count(); - expect(result).toBe(42); + expect(result).toBe(0); }); }); }); diff --git a/api/tests/unit/controllers/CompanyController.fetchConfigurations.test.ts b/api/tests/unit/controllers/CompanyController.fetchConfigurations.test.ts new file mode 100644 index 0000000..aee9ad6 --- /dev/null +++ b/api/tests/unit/controllers/CompanyController.fetchConfigurations.test.ts @@ -0,0 +1,129 @@ +import { beforeEach, describe, expect, mock, test } from "bun:test"; +import { buildMockCompany } from "../../setup"; + +const getMock: any = mock(() => Promise.resolve({ data: [] as any[] })); + +mock.module("../../../src/constants", () => ({ + prisma: new Proxy( + {}, + { + get() { + return mock(() => Promise.resolve([])); + }, + }, + ), + connectWiseApi: { + get: getMock, + post: mock(() => Promise.resolve({ data: {} })), + put: mock(() => Promise.resolve({ data: {} })), + patch: mock(() => Promise.resolve({ data: {} })), + delete: mock(() => Promise.resolve({ data: {} })), + }, +})); + +function buildConfig(id: number) { + return { + id, + name: `Config ${id}`, + activeFlag: true, + serialNumber: `SN-${id}`, + type: { id: 9, name: "Firewall" }, + notes: "test notes", + status: { id: 2, name: "Active" }, + questions: [ + { + questionId: 11, + question: "Hostname", + answer: `host-${id}`, + fieldType: "Text", + }, + ], + _info: { lastUpdated: "2026-03-31T00:00:00Z" }, + }; +} + +describe("CompanyController.fetchConfigurations", () => { + beforeEach(() => { + getMock.mockReset(); + }); + + test("fetches and transforms company configurations from ConnectWise", async () => { + const { CompanyController } = await import( + "../../../src/controllers/CompanyController" + ); + + getMock.mockImplementation(() => Promise.resolve({ data: [buildConfig(1)] })); + + const controller = new CompanyController(buildMockCompany({ id: 123 }) as any); + const result = await controller.fetchConfigurations(); + + expect(getMock).toHaveBeenCalledTimes(1); + const requestUrl = String((getMock.mock.calls[0] as any[])?.[0] ?? ""); + expect(requestUrl).toContain( + "/company/configurations?page=1&pageSize=1000&conditions=company%2Fid%3D123", + ); + + expect(result).toHaveLength(1); + const first = result[0] as any; + expect(first.id).toBe(1); + expect(first.name).toBe("Config 1"); + expect(first.active).toBe(true); + expect(first.serialNumber).toBe("SN-1"); + expect(first.type?.id).toBe(9); + expect(first.status?.name).toBe("Active"); + expect(first.questions?.[0]?.question).toBe("Hostname"); + }); + + test("paginates when page is full", async () => { + const { CompanyController } = await import( + "../../../src/controllers/CompanyController" + ); + + const firstPage = Array.from({ length: 1000 }, (_, i) => buildConfig(i + 1)); + const secondPage = [buildConfig(1001)]; + + getMock + .mockImplementationOnce(() => Promise.resolve({ data: firstPage })) + .mockImplementationOnce(() => Promise.resolve({ data: secondPage })); + + const controller = new CompanyController(buildMockCompany({ id: 456 }) as any); + const result = await controller.fetchConfigurations(); + + expect(getMock).toHaveBeenCalledTimes(2); + const firstUrl = String((getMock.mock.calls[0] as any[])?.[0] ?? ""); + const secondUrl = String((getMock.mock.calls[1] as any[])?.[0] ?? ""); + expect(firstUrl).toContain("page=1"); + expect(secondUrl).toContain("page=2"); + expect(result).toHaveLength(1001); + }); + + test("throws GenericError when CW request fails", async () => { + const { CompanyController } = await import( + "../../../src/controllers/CompanyController" + ); + + const err: any = new Error("Unauthorized"); + err.isAxiosError = true; + err.response = { + status: 401, + data: { message: "Unauthorized" }, + statusText: "Unauthorized", + }; + + getMock.mockImplementation(() => Promise.reject(err)); + + const controller = new CompanyController(buildMockCompany({ id: 321 }) as any); + + try { + await controller.fetchConfigurations(); + expect(true).toBe(false); + } catch (error: any) { + expect(error.name).toBe("ConnectWiseFetchFailed"); + expect(error.status).toBe(401); + expect(error.message).toBe( + "Failed to fetch company configurations from ConnectWise", + ); + expect(error.cause).toBe("Unauthorized"); + } + }); +}); diff --git a/api/tests/unit/controllers/CwMemberController.test.ts b/api/tests/unit/controllers/CwMemberController.test.ts index 02ccddf..ab9ede2 100644 --- a/api/tests/unit/controllers/CwMemberController.test.ts +++ b/api/tests/unit/controllers/CwMemberController.test.ts @@ -72,83 +72,26 @@ describe("CwMemberController", () => { test("returns trimmed name when lastName is empty", () => { const ctrl = new CwMemberController( - buildMockCwMember({ lastName: "" }) as any, + buildMockCwMember({ lastName: "" }) as any ); expect(ctrl.fullName).toBe("John"); }); test("returns trimmed name when firstName is empty", () => { const ctrl = new CwMemberController( - buildMockCwMember({ firstName: "" }) as any, + buildMockCwMember({ firstName: "" }) as any ); expect(ctrl.fullName).toBe("Doe"); }); test("falls back to identifier when both names are empty", () => { const ctrl = new CwMemberController( - buildMockCwMember({ firstName: "", lastName: "" }) as any, + buildMockCwMember({ firstName: "", lastName: "" }) as any ); expect(ctrl.fullName).toBe("jdoe"); }); }); - // ----------------------------------------------------------------- - // mapCwToDb (static) - // ----------------------------------------------------------------- - describe("mapCwToDb", () => { - test("maps CW member fields to DB schema", () => { - const cwItem = { - identifier: "jdoe", - firstName: "John", - lastName: "Doe", - officeEmail: "jdoe@example.com", - inactiveFlag: false, - _info: { lastUpdated: "2026-02-01T12:00:00Z" }, - }; - - const result = CwMemberController.mapCwToDb(cwItem as any); - expect(result.identifier).toBe("jdoe"); - expect(result.firstName).toBe("John"); - expect(result.lastName).toBe("Doe"); - expect(result.officeEmail).toBe("jdoe@example.com"); - expect(result.inactiveFlag).toBe(false); - expect(result.cwLastUpdated).toEqual(new Date("2026-02-01T12:00:00Z")); - }); - - test("handles null/missing fields with defaults", () => { - const cwItem = { - identifier: "empty", - firstName: null, - lastName: null, - officeEmail: null, - inactiveFlag: null, - _info: null, - }; - - const result = CwMemberController.mapCwToDb(cwItem as any); - expect(result.firstName).toBe(""); - expect(result.lastName).toBe(""); - expect(result.officeEmail).toBeNull(); - expect(result.inactiveFlag).toBe(false); - expect(result.cwLastUpdated).toBeInstanceOf(Date); - }); - - test("handles undefined _info.lastUpdated", () => { - const cwItem = { - identifier: "test", - firstName: "A", - lastName: "B", - officeEmail: null, - inactiveFlag: false, - _info: {}, - }; - - const result = CwMemberController.mapCwToDb(cwItem as any); - // Without lastUpdated, falls through to new Date() - expect(result.cwLastUpdated).toBeInstanceOf(Date); - }); - }); - // ----------------------------------------------------------------- // toJson // ----------------------------------------------------------------- @@ -173,7 +116,7 @@ describe("CwMemberController", () => { test("includes fullName in JSON", () => { const ctrl = new CwMemberController( - buildMockCwMember({ firstName: "", lastName: "" }) as any, + buildMockCwMember({ firstName: "", lastName: "" }) as any ); expect(ctrl.toJson().fullName).toBe("jdoe"); }); diff --git a/api/tests/unit/controllers/OpportunityController.test.ts b/api/tests/unit/controllers/OpportunityController.test.ts index 3fa069f..64ca78e 100644 --- a/api/tests/unit/controllers/OpportunityController.test.ts +++ b/api/tests/unit/controllers/OpportunityController.test.ts @@ -164,10 +164,9 @@ describe("OpportunityController", () => { test("maps type, stage, status references", () => { const result = OpportunityController.mapCwToDb(cwOpportunity); - expect(result.typeName).toBe("New Business"); - expect(result.typeCwId).toBe(1); - expect(result.stageName).toBe("Proposal"); - expect(result.statusName).toBe("Active"); + expect(result.typeId).toBe(1); + expect(result.stageId).toBe(2); + expect(result.statusId).toBe(3); }); test("maps null references to null", () => { diff --git a/api/tests/unit/procurementManager.test.ts b/api/tests/unit/procurementManager.test.ts index c10a63f..459a4ae 100644 --- a/api/tests/unit/procurementManager.test.ts +++ b/api/tests/unit/procurementManager.test.ts @@ -6,7 +6,7 @@ import { buildMockCatalogItem, buildMockConstants } from "../setup"; // --------------------------------------------------------------------------- function createStablePrismaMock( - overrides: Record> = {}, + overrides: Record> = {} ) { return new Proxy( {}, @@ -17,7 +17,7 @@ function createStablePrismaMock( if (overrides[model]) return overrides[model]; return new Proxy({}, { get: () => mock(() => Promise.resolve(null)) }); }, - }, + } ); } @@ -43,7 +43,7 @@ describe("procurement manager", () => { findFirst: mock(() => Promise.resolve(mockData)), }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); @@ -60,7 +60,7 @@ describe("procurement manager", () => { prisma: createStablePrismaMock({ catalogItem: { findFirst }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); @@ -77,7 +77,7 @@ describe("procurement manager", () => { findFirst: mock(() => Promise.resolve(null)), }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); @@ -108,7 +108,7 @@ describe("procurement manager", () => { findFirst: mock(() => Promise.resolve(null)), }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); @@ -126,7 +126,7 @@ describe("procurement manager", () => { findFirst: mock(() => Promise.resolve(null)), }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); @@ -134,6 +134,48 @@ describe("procurement manager", () => { const opts = findMany.mock.calls[0]?.[0]; expect(opts.skip).toBe(0); // (max(0,1)-1) * 10 = 0 }); + + test("falls back to safe pagination when page/rpp are invalid", async () => { + const findMany = mock(() => Promise.resolve([])); + mock.module("../../src/constants", () => + buildMockConstants({ + prisma: createStablePrismaMock({ + catalogItem: { + findMany, + findFirst: mock(() => Promise.resolve(null)), + }, + }), + }) + ); + + const { procurement } = await import("../../src/managers/procurement"); + await procurement.fetchPages(Number.NaN, Number.NaN); + const opts = findMany.mock.calls[0]?.[0]; + expect(opts.skip).toBe(0); + expect(opts.take).toBe(30); + }); + + test("uses relational manufacturer filter when manufacturer option is provided", async () => { + const findMany = mock(() => Promise.resolve([])); + mock.module("../../src/constants", () => + buildMockConstants({ + prisma: createStablePrismaMock({ + catalogItem: { + findMany, + findFirst: mock(() => Promise.resolve(null)), + }, + }), + }) + ); + + const { procurement } = await import("../../src/managers/procurement"); + await procurement.fetchPages(1, 20, { manufacturer: "Ubiquiti" }); + + const where = findMany.mock.calls[0]?.[0]?.where; + expect(JSON.stringify(where)).toContain( + '"manufacturer":{"is":{"name":{"contains":"Ubiquiti"' + ); + }); }); // ------------------------------------------------------------------- @@ -150,13 +192,59 @@ describe("procurement manager", () => { findFirst: mock(() => Promise.resolve(null)), }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); const result = await procurement.search("switch", 1, 10); expect(result).toBeArrayOfSize(1); }); + + test("ignores invalid numeric filters instead of passing NaN to Prisma", async () => { + const findMany = mock(() => Promise.resolve([])); + mock.module("../../src/constants", () => + buildMockConstants({ + prisma: createStablePrismaMock({ + catalogItem: { + findMany, + findFirst: mock(() => Promise.resolve(null)), + }, + }), + }) + ); + + const { procurement } = await import("../../src/managers/procurement"); + await procurement.search("switch", Number.NaN, Number.NaN, { + minPrice: Number.NaN, + maxPrice: Number.NaN, + }); + + const where = findMany.mock.calls[0]?.[0]?.where; + expect(where).toHaveProperty("OR"); + expect(JSON.stringify(where)).not.toContain("NaN"); + }); + + test("uses relational manufacturer search clause", async () => { + const findMany = mock(() => Promise.resolve([])); + mock.module("../../src/constants", () => + buildMockConstants({ + prisma: createStablePrismaMock({ + catalogItem: { + findMany, + findFirst: mock(() => Promise.resolve(null)), + }, + }), + }) + ); + + const { procurement } = await import("../../src/managers/procurement"); + await procurement.search("wir", 1, 20); + + const where = findMany.mock.calls[0]?.[0]?.where; + expect(JSON.stringify(where)).toContain( + '"manufacturer":{"is":{"name":{"contains":"wir"' + ); + }); }); // ------------------------------------------------------------------- @@ -172,7 +260,7 @@ describe("procurement manager", () => { findFirst: mock(() => Promise.resolve(null)), }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); @@ -194,7 +282,7 @@ describe("procurement manager", () => { findFirst: mock(() => Promise.resolve(null)), }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); @@ -217,7 +305,7 @@ describe("procurement manager", () => { findFirst: mock(() => Promise.resolve(null)), }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); @@ -235,7 +323,7 @@ describe("procurement manager", () => { findFirst: mock(() => Promise.resolve(null)), }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); @@ -256,7 +344,7 @@ describe("procurement manager", () => { findFirst: mock(() => Promise.resolve(null)), }, }), - }), + }) ); const { procurement } = await import("../../src/managers/procurement"); diff --git a/api/tmp-check-api-tables.mjs b/api/tmp-check-api-tables.mjs new file mode 100644 index 0000000..3b16d71 --- /dev/null +++ b/api/tmp-check-api-tables.mjs @@ -0,0 +1,15 @@ +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(); diff --git a/api/tmp-check-perms.ts b/api/tmp-check-perms.ts new file mode 100644 index 0000000..b6c02c8 --- /dev/null +++ b/api/tmp-check-perms.ts @@ -0,0 +1,24 @@ +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(); diff --git a/api/tmp-check-perms2.ts b/api/tmp-check-perms2.ts new file mode 100644 index 0000000..fa1a33c --- /dev/null +++ b/api/tmp-check-perms2.ts @@ -0,0 +1,24 @@ +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(); diff --git a/api/utils/generate24HourAccessToken.ts b/api/utils/generate24HourAccessToken.ts new file mode 100644 index 0000000..04d6f4d --- /dev/null +++ b/api/utils/generate24HourAccessToken.ts @@ -0,0 +1,52 @@ +import jwt from "jsonwebtoken"; +import { accessTokenPrivateKey, prisma } from "../src/constants"; + +await (async () => { + const userIdentifier = Bun.argv[2]?.trim(); + + if (Bun.argv.length !== 3 || !userIdentifier) { + console.error( + "1 argument expected.\nFormat: bun run utils:gen_access_token {user_identifier}" + ); + process.exit(1); + } + + const user = await prisma.user.findFirst({ + where: { + OR: [ + { id: userIdentifier }, + { login: userIdentifier }, + { email: userIdentifier }, + { userId: userIdentifier }, + ], + }, + }); + + if (!user) { + console.error(`User with identifier '${userIdentifier}' doesn't exist.`); + process.exit(1); + } + + // A backing session is required because access-token validation resolves by sessionKey. + const session = await prisma.session.create({ + data: { + userId: user.id, + expires: new Date(Date.now() + 24 * 60 * 60 * 1000), + }, + }); + + const token = jwt.sign( + { + sessionKey: session.sessionKey, + userID: user.id, + }, + accessTokenPrivateKey, + { + algorithm: "RS256", + expiresIn: "24h", + } + ); + + console.log(token); + process.exit(0); +})(); \ No newline at end of file diff --git a/bun.lock b/bun.lock index 5e9cf7b..20f6f47 100644 --- a/bun.lock +++ b/bun.lock @@ -36,6 +36,7 @@ "blakets": "^0.1.12", "cors": "^2.8.6", "cuid": "^3.0.0", + "dalpuri": "workspace:*", "hono": "^4.11.5", "ioredis": "^5.10.0", "jsonwebtoken": "^9.0.3", @@ -45,7 +46,6 @@ "pg-boss": "^12.14.0", "prisma": "^7.3.0", "socket.io": "^4.8.3", - "socket.io-client": "^4.8.3", "zod": "^4.3.6", "zon": "^1.0.3", }, @@ -61,6 +61,7 @@ "name": "dalpuri", "dependencies": { "@prisma/adapter-mssql": "^7.5.0", + "@prisma/adapter-pg": "^7.5.0", "@prisma/client": "^7.5.0", "prisma": "^7.5.0", "socket.io": "^4.8.3", @@ -69,13 +70,14 @@ "@types/bun": "latest", }, "peerDependencies": { - "typescript": "^5", + "typescript": "^5.9.2", }, }, "ui": { "name": "ui", "version": "0.1.7", "dependencies": { + "@event-calendar/core": "^5.6.0", "axios": "^1.13.3", "dotenv": "^17.2.3", "electron-squirrel-startup": "^1.0.1", @@ -382,6 +384,8 @@ "@eslint/eslintrc": ["@eslint/eslintrc@0.4.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" } }, "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw=="], + "@event-calendar/core": ["@event-calendar/core@5.6.0", "", { "dependencies": { "svelte": "^5.55.0" } }, "sha512-fUtYK/B7JNYzAAd0yRJTT/bOfpDe259xozwGOo+5XKm04OR9fo/01vX1U/66dBg8hkGGlXB+jk9ha7eo5eQ6Uw=="], + "@gar/promisify": ["@gar/promisify@1.1.3", "", {}, "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw=="], "@hapi/accept": ["@hapi/accept@5.0.2", "", { "dependencies": { "@hapi/boom": "9.x.x", "@hapi/hoek": "9.x.x" } }, "sha512-CmzBx/bXUR8451fnZRuZAJRlzgm0Jgu5dltTX/bszmR2lheb9BpyN47Q1RbaGTsvFzn0PXAEs+lXDKfshccYZw=="], @@ -2664,6 +2668,8 @@ "@electron/windows-sign/minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "@event-calendar/core/svelte": ["svelte@5.55.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.4", "esm-env": "^1.2.1", "esrap": "^2.2.4", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-QjvU7EFemf6mRzdMGlAFttMWtAAVXrax61SZYHdkD6yoVGQ89VeyKfZD4H1JrV1WLmJBxWhFch9H6ig/87VGjw=="], + "@inquirer/checkbox/@inquirer/type": ["@inquirer/type@2.0.0", "", { "dependencies": { "mute-stream": "^1.0.0" } }, "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag=="], "@inquirer/confirm/@inquirer/type": ["@inquirer/type@2.0.0", "", { "dependencies": { "mute-stream": "^1.0.0" } }, "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag=="], @@ -3154,6 +3160,10 @@ "@electron/universal/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "@event-calendar/core/svelte/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "@event-calendar/core/svelte/aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="], + "@inquirer/checkbox/@inquirer/type/mute-stream": ["mute-stream@1.0.0", "", {}, "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA=="], "@inquirer/confirm/@inquirer/type/mute-stream": ["mute-stream@1.0.0", "", {}, "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA=="], diff --git a/dalpuri/.DS_Store b/dalpuri/.DS_Store index 0ee4f51..265a618 100644 Binary files a/dalpuri/.DS_Store and b/dalpuri/.DS_Store differ diff --git a/dalpuri/backfill-classid.ts b/dalpuri/backfill-classid.ts new file mode 100644 index 0000000..efe8bda --- /dev/null +++ b/dalpuri/backfill-classid.ts @@ -0,0 +1,83 @@ +/** + * One-time backfill: copies IV_Class_ID (classId) from CW MSSQL ProductCatalog + * to the API PostgreSQL CatalogItem table. + * + * Run with: bun backfill-classid.ts + */ + +import { PrismaMssql } from "@prisma/adapter-mssql"; +import { PrismaClient as CwPrismaClient } from "./generated/prisma/client"; +import pg from "/root/projects/optima/node_modules/.bun/pg@8.20.0+52bd52a0bccfa6a2/node_modules/pg/lib/index.js"; + +// In dalpuri's .env, DATABASE_URL is the CW MSSQL connection string. +const CW_DATABASE_URL = process.env.DATABASE_URL!; + +// The API PostgreSQL URL — hardcoded to avoid env var ambiguity +const API_DATABASE_URL = "postgresql://optima:123web123@localhost:5432/optima"; + +const cwPrisma = new CwPrismaClient({ adapter: new PrismaMssql(CW_DATABASE_URL) }); +const pgPool = new pg.Pool({ connectionString: API_DATABASE_URL }); + +async function main() { + console.log("[backfill-classid] Fetching ProductCatalog classId from CW MSSQL..."); + const cwItems = await cwPrisma.productCatalog.findMany({ + select: { catalogRecId: true, classId: true }, + }); + console.log(`[backfill-classid] Fetched ${cwItems.length} CW catalog items`); + + let updated = 0; + let skipped = 0; + let failed = 0; + + // Batch updates in groups of 500 + const BATCH_SIZE = 500; + for (let i = 0; i < cwItems.length; i += BATCH_SIZE) { + const batch = cwItems.slice(i, i + BATCH_SIZE); + await Promise.all( + batch.map(async (item) => { + try { + const result = await pgPool.query( + 'UPDATE "CatalogItem" SET "classId" = $1 WHERE "id" = $2', + [item.classId, item.catalogRecId] + ); + if (result.rowCount && result.rowCount > 0) { + updated++; + } else { + skipped++; + } + } catch (err) { + failed++; + console.error(`[backfill-classid] Failed for id=${item.catalogRecId}:`, err); + } + }) + ); + console.log(`[backfill-classid] Progress: ${Math.min(i + BATCH_SIZE, cwItems.length)} / ${cwItems.length}`); + } + + console.log(`[backfill-classid] Done. Updated: ${updated}, Skipped (not in API DB): ${skipped}, Failed: ${failed}`); + + // Verify + const verifyRes = await pgPool.query<{nonNull: string, total: string}>( + 'SELECT COUNT(*) FILTER (WHERE "classId" IS NOT NULL) as "nonNull", COUNT(*) as total FROM "CatalogItem"' + ); + const { nonNull, total } = verifyRes.rows[0]; + console.log(`[backfill-classid] Verification: ${nonNull} / ${total} catalog items now have classId`); + + // Breakdown + const breakdownRes = await pgPool.query<{classId: string | null, count: string}>( + 'SELECT "classId", COUNT(*) as count FROM "CatalogItem" GROUP BY "classId" ORDER BY "classId"' + ); + for (const row of breakdownRes.rows) { + const label = row.classId === 'S' ? 'Service/Labor' : row.classId === 'I' ? 'Inventory' : row.classId === 'N' ? 'Non-inventory' : 'null'; + console.log(` classId=${row.classId ?? 'null'} (${label}): ${row.count}`); + } +} + +main() + .catch((err) => { + console.error("[backfill-classid] Fatal error:", err); + process.exit(1); + }) + .finally(async () => { + await Promise.all([cwPrisma.$disconnect(), pgPool.end()]); + }); diff --git a/dalpuri/configurations-first-10-with-relations.json b/dalpuri/configurations-first-10-with-relations.json new file mode 100644 index 0000000..1227f77 --- /dev/null +++ b/dalpuri/configurations-first-10-with-relations.json @@ -0,0 +1,3770 @@ +[ + { + "configRecId": 3, + "csSurveyRecId": 49, + "configName": "Alarm System", + "mfgCompanyRecId": null, + "serialNumber": "", + "datePurchased": null, + "dateInstalled": null, + "installedBy": null, + "dateExpiration": null, + "companyRecId": 19297, + "contactRecId": 7, + "companyAddressRecId": 1002, + "ownerLevelRecId": 11, + "billingUnitRecId": 1, + "contractRecId": null, + "notes": "", + "lastUpdate": "2025-07-14T08:07:43.000Z", + "updatedBy": "slencki", + "modelNumber": "", + "tagNumber": "", + "notificationHistory": ",", + "configStatusRecId": 3, + "deviceId": "", + "mgmtLink": null, + "lastLogin": "", + "mspLastUpdatedBy": null, + "mspLastUpdated": null, + "mobileGuid": "ea1575ba-9852-4fdd-9154-8a3b0788cc2a", + "parentRecId": null, + "billFlag": true, + "backupSuccesses": 0, + "backupIncomplete": 0, + "backupFailed": 0, + "backupRestores": 0, + "backupLastBackupDate": null, + "backupServerName": "", + "backupBillableSpaceGb": "0", + "backupProtectedDeviceList": "", + "backupYear": 0, + "backupMonth": 0, + "macAddress": "", + "customField": "", + "ipAddress": "", + "defaultGateway": "", + "osType": "", + "osInfo": "", + "cpuSpeed": "", + "ram": "", + "localHardDrives": "", + "manufacturerRecId": null, + "srSlaRecId": null, + "remoteLink": null, + "lastUpdateUtc": "2025-07-14T13:07:43.000Z", + "dateEnteredUtc": "2018-08-01T17:17:49.000Z", + "enteredBy": "CRobinso", + "ivProductRecId": null, + "incompleteFlag": false, + "conversionSource": null, + "showRemoteFlag": false, + "showAutomateFlag": false, + "needsRenewal": false, + "manufacturerPartNumber": null, + "id": "ecf5ba78-70d0-ef11-b2e0-000c29c55070", + "configStatus": { + "configStatusRecId": 3, + "description": "Inactive", + "closedFlag": false, + "updatedBy": "hbarnett", + "lastUpdate": "2018-11-29T14:20:43.000Z", + "defaultFlag": false, + "lastUpdateUtc": "2018-11-29T20:20:43.000Z", + "enteredBy": "CONVERSION", + "dateEnteredUtc": "2005-05-26T16:57:27.000Z", + "id": "b602bb78-70d0-ef11-b2e0-000c29c55070" + }, + "configurationAudits": [ + { + "configurationAuditRecId": 11919, + "configurationRecId": 3, + "localeKeyRecId": 2036, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "slencki", + "lastUpdatedUtc": "2025-07-14T13:07:42.597Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 24452, + "auditToken": "old_value", + "auditValueRecId": 2, + "auditValue": "Active", + "configurationAuditRecId": 11919, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 24453, + "auditToken": "new_value", + "auditValueRecId": 3, + "auditValue": "Inactive", + "configurationAuditRecId": 11919, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3, + "configurationRecId": 3, + "localeKeyRecId": 270, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "CRobinso", + "lastUpdatedUtc": "2018-08-01T17:17:48.820Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 3, + "auditToken": "config_name", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 3, + "localeKeyRecId": null + } + ] + } + ] + }, + { + "configRecId": 5, + "csSurveyRecId": 49, + "configName": "Alarm System - Burg", + "mfgCompanyRecId": null, + "serialNumber": "", + "datePurchased": null, + "dateInstalled": "2015-06-19T00:00:00.000Z", + "installedBy": null, + "dateExpiration": "2021-06-01T00:00:00.000Z", + "companyRecId": 19306, + "contactRecId": 18, + "companyAddressRecId": 1012, + "ownerLevelRecId": 11, + "billingUnitRecId": 1, + "contractRecId": null, + "notes": "Cancelled by customer 060222", + "lastUpdate": "2023-04-18T10:29:58.000Z", + "updatedBy": "gthornto", + "modelNumber": "", + "tagNumber": "", + "notificationHistory": ",", + "configStatusRecId": 3, + "deviceId": "", + "mgmtLink": null, + "lastLogin": "", + "mspLastUpdatedBy": null, + "mspLastUpdated": null, + "mobileGuid": "c898d3ee-9e77-4ceb-84fb-89091226c976", + "parentRecId": null, + "billFlag": true, + "backupSuccesses": 0, + "backupIncomplete": 0, + "backupFailed": 0, + "backupRestores": 0, + "backupLastBackupDate": null, + "backupServerName": "", + "backupBillableSpaceGb": "0", + "backupProtectedDeviceList": "", + "backupYear": 0, + "backupMonth": 0, + "macAddress": "", + "customField": "", + "ipAddress": "", + "defaultGateway": "", + "osType": "", + "osInfo": "", + "cpuSpeed": "", + "ram": "", + "localHardDrives": "", + "manufacturerRecId": null, + "srSlaRecId": null, + "remoteLink": null, + "lastUpdateUtc": "2023-04-18T15:29:58.000Z", + "dateEnteredUtc": "2018-09-03T21:08:31.000Z", + "enteredBy": "hbarnett", + "ivProductRecId": null, + "incompleteFlag": false, + "conversionSource": null, + "showRemoteFlag": false, + "showAutomateFlag": false, + "needsRenewal": false, + "manufacturerPartNumber": null, + "id": "6ff8ba78-70d0-ef11-b2e0-000c29c55070", + "configStatus": { + "configStatusRecId": 3, + "description": "Inactive", + "closedFlag": false, + "updatedBy": "hbarnett", + "lastUpdate": "2018-11-29T14:20:43.000Z", + "defaultFlag": false, + "lastUpdateUtc": "2018-11-29T20:20:43.000Z", + "enteredBy": "CONVERSION", + "dateEnteredUtc": "2005-05-26T16:57:27.000Z", + "id": "b602bb78-70d0-ef11-b2e0-000c29c55070" + }, + "configurationAudits": [ + { + "configurationAuditRecId": 4669, + "configurationRecId": 5, + "localeKeyRecId": 2036, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "gthornto", + "lastUpdatedUtc": "2022-06-17T19:52:34.907Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 9741, + "auditToken": "old_value", + "auditValueRecId": 2, + "auditValue": "Active", + "configurationAuditRecId": 4669, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 9742, + "auditToken": "new_value", + "auditValueRecId": 3, + "auditValue": "Inactive", + "configurationAuditRecId": 4669, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 4668, + "configurationRecId": 5, + "localeKeyRecId": 256, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "gthornto", + "lastUpdatedUtc": "2022-06-17T19:52:34.890Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 9739, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 4668, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 9740, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Cancelled by customer 060222", + "configurationAuditRecId": 4668, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3483, + "configurationRecId": 5, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T19:47:07.413Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6964, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "NOT USED", + "configurationAuditRecId": 3483, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6965, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "07-July", + "configurationAuditRecId": 3483, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6966, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3483, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3482, + "configurationRecId": 5, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T19:47:07.397Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6961, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Account Acquired From", + "configurationAuditRecId": 3482, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6962, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3482, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6963, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Houston Security", + "configurationAuditRecId": 3482, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3481, + "configurationRecId": 5, + "localeKeyRecId": 2043, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T19:47:07.350Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6959, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3481, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6960, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "6/1/2021", + "configurationAuditRecId": 3481, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3480, + "configurationRecId": 5, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T19:47:07.333Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6957, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 3480, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6958, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm System - Burg", + "configurationAuditRecId": 3480, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 24, + "configurationRecId": 5, + "localeKeyRecId": 2560, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T18:37:28.453Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 44, + "auditToken": "attachment_filename", + "auditValueRecId": null, + "auditValue": "Sales Contract.pdf", + "configurationAuditRecId": 24, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 45, + "auditToken": "previous_attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Install pt.2", + "configurationAuditRecId": 24, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 46, + "auditToken": "new_attachment_title", + "auditValueRecId": null, + "auditValue": "Contract", + "configurationAuditRecId": 24, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 23, + "configurationRecId": 5, + "localeKeyRecId": 2560, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T18:37:18.420Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 41, + "auditToken": "attachment_filename", + "auditValueRecId": null, + "auditValue": "Contract.pdf", + "configurationAuditRecId": 23, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 42, + "auditToken": "previous_attachment_title", + "auditValueRecId": null, + "auditValue": "Contract", + "configurationAuditRecId": 23, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 43, + "auditToken": "new_attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Installs", + "configurationAuditRecId": 23, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 22, + "configurationRecId": 5, + "localeKeyRecId": 2560, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T18:36:11.227Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 38, + "auditToken": "attachment_filename", + "auditValueRecId": null, + "auditValue": "Sales Contract.pdf", + "configurationAuditRecId": 22, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 39, + "auditToken": "previous_attachment_title", + "auditValueRecId": null, + "auditValue": "Sales Contract", + "configurationAuditRecId": 22, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 40, + "auditToken": "new_attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Install pt.2", + "configurationAuditRecId": 22, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 14, + "configurationRecId": 5, + "localeKeyRecId": 2560, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T16:51:02.827Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 22, + "auditToken": "attachment_filename", + "auditValueRecId": null, + "auditValue": "Purchase Invoice.pdf", + "configurationAuditRecId": 14, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 23, + "auditToken": "previous_attachment_title", + "auditValueRecId": null, + "auditValue": "Purchase Invoice", + "configurationAuditRecId": 14, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 24, + "auditToken": "new_attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Install", + "configurationAuditRecId": 14, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 13, + "configurationRecId": 5, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T16:44:51.730Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 21, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Contract", + "configurationAuditRecId": 13, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 12, + "configurationRecId": 5, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T16:44:41.373Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 20, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Purchase Invoice", + "configurationAuditRecId": 12, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 11, + "configurationRecId": 5, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T16:44:33.713Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 19, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Sales Contract", + "configurationAuditRecId": 11, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 10, + "configurationRecId": 5, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T16:44:23.867Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 18, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Sentrynet Agreement", + "configurationAuditRecId": 10, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 9, + "configurationRecId": 5, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T16:21:39.307Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 15, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Monitoring Bill Month", + "configurationAuditRecId": 9, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 16, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 9, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 17, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "07-July", + "configurationAuditRecId": 9, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 8, + "configurationRecId": 5, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-03T21:11:55.210Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 12, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Monitoring ID", + "configurationAuditRecId": 8, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 13, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 8, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 14, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "FW7855", + "configurationAuditRecId": 8, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 7, + "configurationRecId": 5, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-03T21:11:55.147Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 9, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Monitoring Bill Month", + "configurationAuditRecId": 7, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 10, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "01-January", + "configurationAuditRecId": 7, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 11, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 7, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 6, + "configurationRecId": 5, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-03T21:11:55.063Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Panel Type", + "configurationAuditRecId": 6, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 6, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 8, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Qolsys", + "configurationAuditRecId": 6, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 5, + "configurationRecId": 5, + "localeKeyRecId": 270, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-03T21:08:30.690Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 5, + "auditToken": "config_name", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 5, + "localeKeyRecId": null + } + ] + } + ] + }, + { + "configRecId": 6, + "csSurveyRecId": 49, + "configName": "Alarm System - Burg", + "mfgCompanyRecId": null, + "serialNumber": "", + "datePurchased": null, + "dateInstalled": "2010-06-29T00:00:00.000Z", + "installedBy": null, + "dateExpiration": "2021-06-01T00:00:00.000Z", + "companyRecId": 19311, + "contactRecId": 23, + "companyAddressRecId": 1017, + "ownerLevelRecId": 11, + "billingUnitRecId": 1, + "contractRecId": null, + "notes": "House\nCancelled\n", + "lastUpdate": "2021-06-25T16:08:34.000Z", + "updatedBy": "jgnech", + "modelNumber": "", + "tagNumber": "", + "notificationHistory": ",", + "configStatusRecId": 3, + "deviceId": "", + "mgmtLink": null, + "lastLogin": "", + "mspLastUpdatedBy": null, + "mspLastUpdated": null, + "mobileGuid": "0b153c9e-f084-44fa-bc85-cb92eea83843", + "parentRecId": null, + "billFlag": true, + "backupSuccesses": 0, + "backupIncomplete": 0, + "backupFailed": 0, + "backupRestores": 0, + "backupLastBackupDate": null, + "backupServerName": "", + "backupBillableSpaceGb": "0", + "backupProtectedDeviceList": "", + "backupYear": 0, + "backupMonth": 0, + "macAddress": "", + "customField": "", + "ipAddress": "", + "defaultGateway": "", + "osType": "", + "osInfo": "", + "cpuSpeed": "", + "ram": "", + "localHardDrives": "", + "manufacturerRecId": null, + "srSlaRecId": null, + "remoteLink": null, + "lastUpdateUtc": "2021-06-25T21:08:34.000Z", + "dateEnteredUtc": "2018-09-12T17:29:03.000Z", + "enteredBy": "hbarnett", + "ivProductRecId": null, + "incompleteFlag": false, + "conversionSource": null, + "showRemoteFlag": false, + "showAutomateFlag": false, + "needsRenewal": false, + "manufacturerPartNumber": null, + "id": "70f8ba78-70d0-ef11-b2e0-000c29c55070", + "configStatus": { + "configStatusRecId": 3, + "description": "Inactive", + "closedFlag": false, + "updatedBy": "hbarnett", + "lastUpdate": "2018-11-29T14:20:43.000Z", + "defaultFlag": false, + "lastUpdateUtc": "2018-11-29T20:20:43.000Z", + "enteredBy": "CONVERSION", + "dateEnteredUtc": "2005-05-26T16:57:27.000Z", + "id": "b602bb78-70d0-ef11-b2e0-000c29c55070" + }, + "configurationAudits": [ + { + "configurationAuditRecId": 3605, + "configurationRecId": 6, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T21:08:34.480Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7277, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "NOT USED", + "configurationAuditRecId": 3605, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7278, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "07-July", + "configurationAuditRecId": 3605, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7279, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3605, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3604, + "configurationRecId": 6, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T21:08:34.463Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7274, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Account Acquired From", + "configurationAuditRecId": 3604, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7275, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3604, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7276, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Houston Security", + "configurationAuditRecId": 3604, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3603, + "configurationRecId": 6, + "localeKeyRecId": 2043, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T21:08:34.417Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7272, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3603, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7273, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "6/1/2021", + "configurationAuditRecId": 3603, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1881, + "configurationRecId": 6, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-06-15T20:20:43.377Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 2900, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm System - House", + "configurationAuditRecId": 1881, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2901, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm System - Burg", + "configurationAuditRecId": 1881, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1882, + "configurationRecId": 6, + "localeKeyRecId": 256, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-06-15T20:20:43.377Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 2902, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Cancelled", + "configurationAuditRecId": 1882, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2903, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "House\nCancelled\n", + "configurationAuditRecId": 1882, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1558, + "configurationRecId": 6, + "localeKeyRecId": 2036, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-05-27T14:47:34.307Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 2224, + "auditToken": "old_value", + "auditValueRecId": 2, + "auditValue": "Active", + "configurationAuditRecId": 1558, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2225, + "auditToken": "new_value", + "auditValueRecId": 3, + "auditValue": "Inactive", + "configurationAuditRecId": 1558, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1557, + "configurationRecId": 6, + "localeKeyRecId": 256, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-05-27T14:47:34.257Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 2222, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 1557, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2223, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Cancelled", + "configurationAuditRecId": 1557, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 662, + "configurationRecId": 6, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-12-18T17:18:38.840Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 839, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 662, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 840, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm System - House", + "configurationAuditRecId": 662, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 25, + "configurationRecId": 6, + "localeKeyRecId": 2046, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T18:39:05.307Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 47, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 25, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 48, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "6/29/2010", + "configurationAuditRecId": 25, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 20, + "configurationRecId": 6, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T17:33:35.137Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 36, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Install", + "configurationAuditRecId": 20, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 19, + "configurationRecId": 6, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T17:33:26.273Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 35, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Sentreynet Monitoring Agreement", + "configurationAuditRecId": 19, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 18, + "configurationRecId": 6, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T17:33:15.490Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 32, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Monitoring Bill Month", + "configurationAuditRecId": 18, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 33, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "01-January", + "configurationAuditRecId": 18, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 34, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "07-July", + "configurationAuditRecId": 18, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 17, + "configurationRecId": 6, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T17:33:15.417Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 29, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Communicator", + "configurationAuditRecId": 17, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 30, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm.com", + "configurationAuditRecId": 17, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 31, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Teleguard", + "configurationAuditRecId": 17, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 16, + "configurationRecId": 6, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T17:33:15.387Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 26, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Panel Type", + "configurationAuditRecId": 16, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 27, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 16, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 28, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Power", + "configurationAuditRecId": 16, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 15, + "configurationRecId": 6, + "localeKeyRecId": 270, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T17:29:03.503Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 25, + "auditToken": "config_name", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 15, + "localeKeyRecId": null + } + ] + } + ] + }, + { + "configRecId": 7, + "csSurveyRecId": 49, + "configName": "Alarm System - Burg", + "mfgCompanyRecId": null, + "serialNumber": "", + "datePurchased": null, + "dateInstalled": "2010-06-25T00:00:00.000Z", + "installedBy": null, + "dateExpiration": "2021-06-01T00:00:00.000Z", + "companyRecId": 19314, + "contactRecId": 26, + "companyAddressRecId": 1020, + "ownerLevelRecId": 11, + "billingUnitRecId": 1, + "contractRecId": null, + "notes": "", + "lastUpdate": "2025-01-08T08:44:02.000Z", + "updatedBy": "slencki", + "modelNumber": "", + "tagNumber": "", + "notificationHistory": ",", + "configStatusRecId": 3, + "deviceId": "", + "mgmtLink": null, + "lastLogin": "", + "mspLastUpdatedBy": null, + "mspLastUpdated": null, + "mobileGuid": "a68cf3e6-a256-460a-83ce-57460a5c05db", + "parentRecId": null, + "billFlag": true, + "backupSuccesses": 0, + "backupIncomplete": 0, + "backupFailed": 0, + "backupRestores": 0, + "backupLastBackupDate": null, + "backupServerName": "", + "backupBillableSpaceGb": "0", + "backupProtectedDeviceList": "", + "backupYear": 0, + "backupMonth": 0, + "macAddress": "", + "customField": "", + "ipAddress": "", + "defaultGateway": "", + "osType": "", + "osInfo": "", + "cpuSpeed": "", + "ram": "", + "localHardDrives": "", + "manufacturerRecId": null, + "srSlaRecId": null, + "remoteLink": null, + "lastUpdateUtc": "2025-01-08T14:44:02.000Z", + "dateEnteredUtc": "2018-09-12T18:35:13.000Z", + "enteredBy": "hbarnett", + "ivProductRecId": null, + "incompleteFlag": false, + "conversionSource": null, + "showRemoteFlag": false, + "showAutomateFlag": false, + "needsRenewal": false, + "manufacturerPartNumber": null, + "id": "71f8ba78-70d0-ef11-b2e0-000c29c55070", + "configStatus": { + "configStatusRecId": 3, + "description": "Inactive", + "closedFlag": false, + "updatedBy": "hbarnett", + "lastUpdate": "2018-11-29T14:20:43.000Z", + "defaultFlag": false, + "lastUpdateUtc": "2018-11-29T20:20:43.000Z", + "enteredBy": "CONVERSION", + "dateEnteredUtc": "2005-05-26T16:57:27.000Z", + "id": "b602bb78-70d0-ef11-b2e0-000c29c55070" + }, + "configurationAudits": [ + { + "configurationAuditRecId": 9886, + "configurationRecId": 7, + "localeKeyRecId": 2036, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "slencki", + "lastUpdatedUtc": "2025-01-08T14:44:01.843Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 20170, + "auditToken": "old_value", + "auditValueRecId": 2, + "auditValue": "Active", + "configurationAuditRecId": 9886, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 20171, + "auditToken": "new_value", + "auditValueRecId": 3, + "auditValue": "Inactive", + "configurationAuditRecId": 9886, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 7047, + "configurationRecId": 7, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "gthornto", + "lastUpdatedUtc": "2024-05-02T17:21:12.150Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 14574, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Monitoring ID", + "configurationAuditRecId": 7047, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 14575, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "G57044", + "configurationAuditRecId": 7047, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 14576, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "1502-7044", + "configurationAuditRecId": 7047, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 7046, + "configurationRecId": 7, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "gthornto", + "lastUpdatedUtc": "2024-05-02T17:21:12.140Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 14571, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Monitoring Company ", + "configurationAuditRecId": 7046, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 14572, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Design Communications", + "configurationAuditRecId": 7046, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 14573, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "C.O.P.S.", + "configurationAuditRecId": 7046, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 7045, + "configurationRecId": 7, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "gthornto", + "lastUpdatedUtc": "2024-05-02T17:21:12.130Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 14568, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Communicator", + "configurationAuditRecId": 7045, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 14569, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "HUB-01", + "configurationAuditRecId": 7045, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 14570, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "M2M", + "configurationAuditRecId": 7045, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3360, + "configurationRecId": 7, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T18:12:31.617Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6653, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Account Acquired From", + "configurationAuditRecId": 3360, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6654, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Pro-Tec Security", + "configurationAuditRecId": 3360, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6655, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Houston Security", + "configurationAuditRecId": 3360, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 2909, + "configurationRecId": 7, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-24T16:32:03.177Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 5466, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "NOT USED", + "configurationAuditRecId": 2909, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 5467, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "07-July", + "configurationAuditRecId": 2909, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 5468, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 2909, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 2908, + "configurationRecId": 7, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-24T16:32:03.143Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 5463, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Account Acquired From", + "configurationAuditRecId": 2908, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 5464, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 2908, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 5465, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Pro-Tec Security", + "configurationAuditRecId": 2908, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 2907, + "configurationRecId": 7, + "localeKeyRecId": 2043, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-24T16:32:03.113Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 5461, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 2907, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 5462, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "6/1/2021", + "configurationAuditRecId": 2907, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1661, + "configurationRecId": 7, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-06-15T18:37:33.150Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 2438, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Baker, Jared & Christy", + "configurationAuditRecId": 1661, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2439, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm System - Burg", + "configurationAuditRecId": 1661, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1301, + "configurationRecId": 7, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-01-15T17:58:17.077Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 1739, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 1301, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1740, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Baker, Jared & Christy", + "configurationAuditRecId": 1301, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1216, + "configurationRecId": 7, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2020-12-02T16:31:45.163Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 1594, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Monitoring ID", + "configurationAuditRecId": 1216, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1595, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "FW7744", + "configurationAuditRecId": 1216, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1596, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "G57044", + "configurationAuditRecId": 1216, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1215, + "configurationRecId": 7, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2020-12-02T16:31:45.127Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 1591, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Monitoring Company ", + "configurationAuditRecId": 1215, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1592, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "SentryNet ", + "configurationAuditRecId": 1215, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1593, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Design Communications", + "configurationAuditRecId": 1215, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1214, + "configurationRecId": 7, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2020-12-02T16:31:45.100Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 1588, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Communicator", + "configurationAuditRecId": 1214, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1589, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Teleguard", + "configurationAuditRecId": 1214, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1590, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "HUB-01", + "configurationAuditRecId": 1214, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 29, + "configurationRecId": 7, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T18:50:19.443Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 53, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Sentrynet Monitoring Agreement", + "configurationAuditRecId": 29, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 28, + "configurationRecId": 7, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T18:50:05.033Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 52, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Install", + "configurationAuditRecId": 28, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 27, + "configurationRecId": 7, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T18:49:57.610Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 51, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Contract", + "configurationAuditRecId": 27, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 26, + "configurationRecId": 7, + "localeKeyRecId": 2072, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T18:40:06.967Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 49, + "auditToken": "old_value", + "auditValueRecId": 6, + "auditValue": "General", + "configurationAuditRecId": 26, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 50, + "auditToken": "new_value", + "auditValueRecId": 1, + "auditValue": "Security", + "configurationAuditRecId": 26, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 21, + "configurationRecId": 7, + "localeKeyRecId": 270, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T18:35:12.847Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 37, + "auditToken": "config_name", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 21, + "localeKeyRecId": null + } + ] + } + ] + }, + { + "configRecId": 8, + "csSurveyRecId": 49, + "configName": "Alarm System - Burg", + "mfgCompanyRecId": null, + "serialNumber": "", + "datePurchased": null, + "dateInstalled": "2008-05-15T00:00:00.000Z", + "installedBy": null, + "dateExpiration": "2021-06-01T00:00:00.000Z", + "companyRecId": 19321, + "contactRecId": 33, + "companyAddressRecId": 1027, + "ownerLevelRecId": 11, + "billingUnitRecId": 1, + "contractRecId": null, + "notes": "Cancelled ", + "lastUpdate": "2021-06-25T13:08:37.000Z", + "updatedBy": "jgnech", + "modelNumber": "", + "tagNumber": "", + "notificationHistory": ",", + "configStatusRecId": 3, + "deviceId": "", + "mgmtLink": null, + "lastLogin": "", + "mspLastUpdatedBy": null, + "mspLastUpdated": null, + "mobileGuid": "42fb3051-274d-4319-9bc6-e7f18f98afd7", + "parentRecId": null, + "billFlag": true, + "backupSuccesses": 0, + "backupIncomplete": 0, + "backupFailed": 0, + "backupRestores": 0, + "backupLastBackupDate": null, + "backupServerName": "", + "backupBillableSpaceGb": "0", + "backupProtectedDeviceList": "", + "backupYear": 0, + "backupMonth": 0, + "macAddress": "", + "customField": "", + "ipAddress": "", + "defaultGateway": "", + "osType": "", + "osInfo": "", + "cpuSpeed": "", + "ram": "", + "localHardDrives": "", + "manufacturerRecId": null, + "srSlaRecId": null, + "remoteLink": null, + "lastUpdateUtc": "2021-06-25T18:08:37.000Z", + "dateEnteredUtc": "2018-09-12T19:13:45.000Z", + "enteredBy": "hbarnett", + "ivProductRecId": null, + "incompleteFlag": false, + "conversionSource": null, + "showRemoteFlag": false, + "showAutomateFlag": false, + "needsRenewal": false, + "manufacturerPartNumber": null, + "id": "72f8ba78-70d0-ef11-b2e0-000c29c55070", + "configStatus": { + "configStatusRecId": 3, + "description": "Inactive", + "closedFlag": false, + "updatedBy": "hbarnett", + "lastUpdate": "2018-11-29T14:20:43.000Z", + "defaultFlag": false, + "lastUpdateUtc": "2018-11-29T20:20:43.000Z", + "enteredBy": "CONVERSION", + "dateEnteredUtc": "2005-05-26T16:57:27.000Z", + "id": "b602bb78-70d0-ef11-b2e0-000c29c55070" + }, + "configurationAudits": [ + { + "configurationAuditRecId": 3350, + "configurationRecId": 8, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T18:08:37.147Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6627, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "NOT USED", + "configurationAuditRecId": 3350, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6628, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "05-May", + "configurationAuditRecId": 3350, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6629, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3350, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3349, + "configurationRecId": 8, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T18:08:37.117Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6624, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Account Acquired From", + "configurationAuditRecId": 3349, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6625, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3349, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6626, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Houston Security", + "configurationAuditRecId": 3349, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3348, + "configurationRecId": 8, + "localeKeyRecId": 2043, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T18:08:37.087Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6622, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3348, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6623, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "6/1/2021", + "configurationAuditRecId": 3348, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3347, + "configurationRecId": 8, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T18:08:37.070Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6620, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 3347, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6621, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm System - Burg", + "configurationAuditRecId": 3347, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1535, + "configurationRecId": 8, + "localeKeyRecId": 256, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-05-21T16:14:25.033Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 2183, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 1535, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2184, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Cancelled ", + "configurationAuditRecId": 1535, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 693, + "configurationRecId": 8, + "localeKeyRecId": 2036, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2019-02-01T16:31:55.497Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 888, + "auditToken": "old_value", + "auditValueRecId": 2, + "auditValue": "Active", + "configurationAuditRecId": 693, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 889, + "auditToken": "new_value", + "auditValueRecId": 3, + "auditValue": "Inactive", + "configurationAuditRecId": 693, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 32, + "configurationRecId": 8, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T19:17:40.823Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 56, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Sentrynet Monitoring Agreement", + "configurationAuditRecId": 32, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 31, + "configurationRecId": 8, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T19:17:32.523Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 55, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Install", + "configurationAuditRecId": 31, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 30, + "configurationRecId": 8, + "localeKeyRecId": 270, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T19:13:45.260Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 54, + "auditToken": "config_name", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 30, + "localeKeyRecId": null + } + ] + } + ] + }, + { + "configRecId": 9, + "csSurveyRecId": 49, + "configName": "Alarm System - Burg", + "mfgCompanyRecId": null, + "serialNumber": "", + "datePurchased": null, + "dateInstalled": "2012-07-07T00:00:00.000Z", + "installedBy": null, + "dateExpiration": null, + "companyRecId": 19322, + "contactRecId": 34, + "companyAddressRecId": 1028, + "ownerLevelRecId": 11, + "billingUnitRecId": 1, + "contractRecId": null, + "notes": "Cancelled 7-29-20", + "lastUpdate": "2021-07-07T13:32:01.000Z", + "updatedBy": "mscarbro", + "modelNumber": "", + "tagNumber": "", + "notificationHistory": ",", + "configStatusRecId": 3, + "deviceId": "", + "mgmtLink": null, + "lastLogin": "", + "mspLastUpdatedBy": null, + "mspLastUpdated": null, + "mobileGuid": "a3b29d0c-a4d7-41c0-adde-332f56eb7158", + "parentRecId": null, + "billFlag": true, + "backupSuccesses": 0, + "backupIncomplete": 0, + "backupFailed": 0, + "backupRestores": 0, + "backupLastBackupDate": null, + "backupServerName": "", + "backupBillableSpaceGb": "0", + "backupProtectedDeviceList": "", + "backupYear": 0, + "backupMonth": 0, + "macAddress": "", + "customField": "", + "ipAddress": "", + "defaultGateway": "", + "osType": "", + "osInfo": "", + "cpuSpeed": "", + "ram": "", + "localHardDrives": "", + "manufacturerRecId": null, + "srSlaRecId": null, + "remoteLink": null, + "lastUpdateUtc": "2021-07-07T18:32:01.000Z", + "dateEnteredUtc": "2018-09-12T19:43:04.000Z", + "enteredBy": "hbarnett", + "ivProductRecId": null, + "incompleteFlag": false, + "conversionSource": null, + "showRemoteFlag": false, + "showAutomateFlag": false, + "needsRenewal": false, + "manufacturerPartNumber": null, + "id": "73f8ba78-70d0-ef11-b2e0-000c29c55070", + "configStatus": { + "configStatusRecId": 3, + "description": "Inactive", + "closedFlag": false, + "updatedBy": "hbarnett", + "lastUpdate": "2018-11-29T14:20:43.000Z", + "defaultFlag": false, + "lastUpdateUtc": "2018-11-29T20:20:43.000Z", + "enteredBy": "CONVERSION", + "dateEnteredUtc": "2005-05-26T16:57:27.000Z", + "id": "b602bb78-70d0-ef11-b2e0-000c29c55070" + }, + "configurationAudits": [ + { + "configurationAuditRecId": 3794, + "configurationRecId": 9, + "localeKeyRecId": 256, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-07-07T18:32:00.800Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7721, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3794, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7722, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Cancelled 7-29-20", + "configurationAuditRecId": 3794, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3793, + "configurationRecId": 9, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-07-07T18:32:00.783Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7719, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm System ", + "configurationAuditRecId": 3793, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7720, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm System - Burg", + "configurationAuditRecId": 3793, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1265, + "configurationRecId": 9, + "localeKeyRecId": 2036, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-01-14T21:44:30.050Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 1672, + "auditToken": "old_value", + "auditValueRecId": 2, + "auditValue": "Active", + "configurationAuditRecId": 1265, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1673, + "auditToken": "new_value", + "auditValueRecId": 3, + "auditValue": "Inactive", + "configurationAuditRecId": 1265, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 36, + "configurationRecId": 9, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T19:47:26.027Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 60, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Contract", + "configurationAuditRecId": 36, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 35, + "configurationRecId": 9, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T19:47:18.123Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 59, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Install", + "configurationAuditRecId": 35, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 34, + "configurationRecId": 9, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T19:47:10.810Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 58, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Sentrynet Monitoring Agreement", + "configurationAuditRecId": 34, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 33, + "configurationRecId": 9, + "localeKeyRecId": 270, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T19:43:04.400Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 57, + "auditToken": "config_name", + "auditValueRecId": null, + "auditValue": "Alarm System ", + "configurationAuditRecId": 33, + "localeKeyRecId": null + } + ] + } + ] + }, + { + "configRecId": 10, + "csSurveyRecId": 49, + "configName": "Alarm System - Burg", + "mfgCompanyRecId": null, + "serialNumber": "", + "datePurchased": null, + "dateInstalled": "2011-03-10T00:00:00.000Z", + "installedBy": null, + "dateExpiration": "2021-06-01T00:00:00.000Z", + "companyRecId": 20208, + "contactRecId": 42, + "companyAddressRecId": 1036, + "ownerLevelRecId": 11, + "billingUnitRecId": 1, + "contractRecId": null, + "notes": "", + "lastUpdate": "2025-07-25T10:21:56.000Z", + "updatedBy": "slencki", + "modelNumber": "", + "tagNumber": "", + "notificationHistory": ",", + "configStatusRecId": 3, + "deviceId": "", + "mgmtLink": null, + "lastLogin": "", + "mspLastUpdatedBy": null, + "mspLastUpdated": null, + "mobileGuid": "fded2368-db83-415b-8571-59586f095fe7", + "parentRecId": null, + "billFlag": true, + "backupSuccesses": 0, + "backupIncomplete": 0, + "backupFailed": 0, + "backupRestores": 0, + "backupLastBackupDate": null, + "backupServerName": "", + "backupBillableSpaceGb": "0", + "backupProtectedDeviceList": "", + "backupYear": 0, + "backupMonth": 0, + "macAddress": "", + "customField": "", + "ipAddress": "", + "defaultGateway": "", + "osType": "", + "osInfo": "", + "cpuSpeed": "", + "ram": "", + "localHardDrives": "", + "manufacturerRecId": null, + "srSlaRecId": null, + "remoteLink": null, + "lastUpdateUtc": "2025-07-25T15:21:56.000Z", + "dateEnteredUtc": "2018-09-12T20:07:58.000Z", + "enteredBy": "hbarnett", + "ivProductRecId": null, + "incompleteFlag": false, + "conversionSource": null, + "showRemoteFlag": false, + "showAutomateFlag": false, + "needsRenewal": false, + "manufacturerPartNumber": null, + "id": "74f8ba78-70d0-ef11-b2e0-000c29c55070", + "configStatus": { + "configStatusRecId": 3, + "description": "Inactive", + "closedFlag": false, + "updatedBy": "hbarnett", + "lastUpdate": "2018-11-29T14:20:43.000Z", + "defaultFlag": false, + "lastUpdateUtc": "2018-11-29T20:20:43.000Z", + "enteredBy": "CONVERSION", + "dateEnteredUtc": "2005-05-26T16:57:27.000Z", + "id": "b602bb78-70d0-ef11-b2e0-000c29c55070" + }, + "configurationAudits": [ + { + "configurationAuditRecId": 12112, + "configurationRecId": 10, + "localeKeyRecId": 2036, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "slencki", + "lastUpdatedUtc": "2025-07-25T15:21:55.887Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 24768, + "auditToken": "old_value", + "auditValueRecId": 2, + "auditValue": "Active", + "configurationAuditRecId": 12112, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 24769, + "auditToken": "new_value", + "auditValueRecId": 3, + "auditValue": "Inactive", + "configurationAuditRecId": 12112, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3419, + "configurationRecId": 10, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T19:17:42.007Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6802, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "NOT USED", + "configurationAuditRecId": 3419, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6803, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "03-March", + "configurationAuditRecId": 3419, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6804, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3419, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3418, + "configurationRecId": 10, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T19:17:41.990Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6799, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Account Acquired From", + "configurationAuditRecId": 3418, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6800, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3418, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6801, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Houston Security", + "configurationAuditRecId": 3418, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3417, + "configurationRecId": 10, + "localeKeyRecId": 2043, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T19:17:41.960Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 6797, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3417, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 6798, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "6/1/2021", + "configurationAuditRecId": 3417, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1693, + "configurationRecId": 10, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-06-15T18:55:40.950Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 2502, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Bona, Leah", + "configurationAuditRecId": 1693, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2503, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm System - Burg", + "configurationAuditRecId": 1693, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1117, + "configurationRecId": 10, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2020-07-31T20:02:52.900Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 1440, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 1117, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1441, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Bona, Leah", + "configurationAuditRecId": 1117, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 40, + "configurationRecId": 10, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:17:37.277Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 64, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Contract", + "configurationAuditRecId": 40, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 39, + "configurationRecId": 10, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:17:30.803Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 63, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Install", + "configurationAuditRecId": 39, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 38, + "configurationRecId": 10, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:17:23.380Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 62, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Sentrynet Monitoring Agreement", + "configurationAuditRecId": 38, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 37, + "configurationRecId": 10, + "localeKeyRecId": 270, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:07:58.123Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 61, + "auditToken": "config_name", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 37, + "localeKeyRecId": null + } + ] + } + ] + }, + { + "configRecId": 11, + "csSurveyRecId": 49, + "configName": "Alarm System - Burg", + "mfgCompanyRecId": null, + "serialNumber": "", + "datePurchased": null, + "dateInstalled": "2014-05-30T00:00:00.000Z", + "installedBy": null, + "dateExpiration": null, + "companyRecId": 19337, + "contactRecId": 48, + "companyAddressRecId": 1043, + "ownerLevelRecId": 11, + "billingUnitRecId": 1, + "contractRecId": null, + "notes": "Cancelled - 1-15--21", + "lastUpdate": "2021-06-15T13:52:27.000Z", + "updatedBy": "mscarbro", + "modelNumber": "", + "tagNumber": "", + "notificationHistory": ",", + "configStatusRecId": 3, + "deviceId": "", + "mgmtLink": null, + "lastLogin": "", + "mspLastUpdatedBy": null, + "mspLastUpdated": null, + "mobileGuid": "ad5f7a45-ac0a-4b77-9be9-b6686bba7d35", + "parentRecId": null, + "billFlag": true, + "backupSuccesses": 0, + "backupIncomplete": 0, + "backupFailed": 0, + "backupRestores": 0, + "backupLastBackupDate": null, + "backupServerName": "", + "backupBillableSpaceGb": "0", + "backupProtectedDeviceList": "", + "backupYear": 0, + "backupMonth": 0, + "macAddress": "", + "customField": "", + "ipAddress": "", + "defaultGateway": "", + "osType": "", + "osInfo": "", + "cpuSpeed": "", + "ram": "", + "localHardDrives": "", + "manufacturerRecId": null, + "srSlaRecId": null, + "remoteLink": null, + "lastUpdateUtc": "2021-06-15T18:52:27.000Z", + "dateEnteredUtc": "2018-09-12T20:39:58.000Z", + "enteredBy": "hbarnett", + "ivProductRecId": null, + "incompleteFlag": false, + "conversionSource": null, + "showRemoteFlag": false, + "showAutomateFlag": false, + "needsRenewal": false, + "manufacturerPartNumber": null, + "id": "75f8ba78-70d0-ef11-b2e0-000c29c55070", + "configStatus": { + "configStatusRecId": 3, + "description": "Inactive", + "closedFlag": false, + "updatedBy": "hbarnett", + "lastUpdate": "2018-11-29T14:20:43.000Z", + "defaultFlag": false, + "lastUpdateUtc": "2018-11-29T20:20:43.000Z", + "enteredBy": "CONVERSION", + "dateEnteredUtc": "2005-05-26T16:57:27.000Z", + "id": "b602bb78-70d0-ef11-b2e0-000c29c55070" + }, + "configurationAudits": [ + { + "configurationAuditRecId": 1689, + "configurationRecId": 11, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-06-15T18:52:26.920Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 2494, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm Systems ", + "configurationAuditRecId": 1689, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2495, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm System - Burg", + "configurationAuditRecId": 1689, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1577, + "configurationRecId": 11, + "localeKeyRecId": 256, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-05-28T19:42:07.500Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 2261, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 1577, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2262, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Cancelled - 1-15--21", + "configurationAuditRecId": 1577, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1272, + "configurationRecId": 11, + "localeKeyRecId": 2036, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-01-14T23:12:17.337Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 1682, + "auditToken": "old_value", + "auditValueRecId": 2, + "auditValue": "Active", + "configurationAuditRecId": 1272, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1683, + "auditToken": "new_value", + "auditValueRecId": 3, + "auditValue": "Inactive", + "configurationAuditRecId": 1272, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 43, + "configurationRecId": 11, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:42:20.010Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 67, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Contract", + "configurationAuditRecId": 43, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 42, + "configurationRecId": 11, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:42:10.237Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 66, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Sentrynet Monitoring Agreement", + "configurationAuditRecId": 42, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 41, + "configurationRecId": 11, + "localeKeyRecId": 270, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:39:57.897Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 65, + "auditToken": "config_name", + "auditValueRecId": null, + "auditValue": "Alarm Systems ", + "configurationAuditRecId": 41, + "localeKeyRecId": null + } + ] + } + ] + }, + { + "configRecId": 12, + "csSurveyRecId": 49, + "configName": "Alarm System - Burg", + "mfgCompanyRecId": null, + "serialNumber": "", + "datePurchased": null, + "dateInstalled": "2015-08-15T00:00:00.000Z", + "installedBy": null, + "dateExpiration": "2021-06-01T00:00:00.000Z", + "companyRecId": 19339, + "contactRecId": 50, + "companyAddressRecId": 1045, + "ownerLevelRecId": 11, + "billingUnitRecId": 1, + "contractRecId": null, + "notes": "Service cancelled 3-10-2021 per customer request.\n\nLetter sent to customer / Invoice written off. (twade)", + "lastUpdate": "2021-06-25T16:26:12.000Z", + "updatedBy": "jgnech", + "modelNumber": "", + "tagNumber": "", + "notificationHistory": ",", + "configStatusRecId": 3, + "deviceId": "", + "mgmtLink": null, + "lastLogin": "", + "mspLastUpdatedBy": null, + "mspLastUpdated": null, + "mobileGuid": "2fd15717-c11c-44f6-a600-b189f34a4d2a", + "parentRecId": null, + "billFlag": true, + "backupSuccesses": 0, + "backupIncomplete": 0, + "backupFailed": 0, + "backupRestores": 0, + "backupLastBackupDate": null, + "backupServerName": "", + "backupBillableSpaceGb": "0", + "backupProtectedDeviceList": "", + "backupYear": 0, + "backupMonth": 0, + "macAddress": "", + "customField": "", + "ipAddress": "", + "defaultGateway": "", + "osType": "", + "osInfo": "", + "cpuSpeed": "", + "ram": "", + "localHardDrives": "", + "manufacturerRecId": null, + "srSlaRecId": null, + "remoteLink": null, + "lastUpdateUtc": "2021-06-25T21:26:12.000Z", + "dateEnteredUtc": "2018-09-12T20:51:59.000Z", + "enteredBy": "hbarnett", + "ivProductRecId": null, + "incompleteFlag": false, + "conversionSource": null, + "showRemoteFlag": false, + "showAutomateFlag": false, + "needsRenewal": false, + "manufacturerPartNumber": null, + "id": "76f8ba78-70d0-ef11-b2e0-000c29c55070", + "configStatus": { + "configStatusRecId": 3, + "description": "Inactive", + "closedFlag": false, + "updatedBy": "hbarnett", + "lastUpdate": "2018-11-29T14:20:43.000Z", + "defaultFlag": false, + "lastUpdateUtc": "2018-11-29T20:20:43.000Z", + "enteredBy": "CONVERSION", + "dateEnteredUtc": "2005-05-26T16:57:27.000Z", + "id": "b602bb78-70d0-ef11-b2e0-000c29c55070" + }, + "configurationAudits": [ + { + "configurationAuditRecId": 3670, + "configurationRecId": 12, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T21:26:11.817Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7443, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "NOT USED", + "configurationAuditRecId": 3670, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7444, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "04-April", + "configurationAuditRecId": 3670, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7445, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3670, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3669, + "configurationRecId": 12, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T21:26:11.800Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7440, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Account Acquired From", + "configurationAuditRecId": 3669, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7441, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3669, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7442, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Houston Security", + "configurationAuditRecId": 3669, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3668, + "configurationRecId": 12, + "localeKeyRecId": 2043, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T21:26:11.753Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7438, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3668, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7439, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "6/1/2021", + "configurationAuditRecId": 3668, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3667, + "configurationRecId": 12, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T21:26:11.737Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7436, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 3667, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7437, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm System - Burg", + "configurationAuditRecId": 3667, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1377, + "configurationRecId": 12, + "localeKeyRecId": 256, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "twade", + "lastUpdatedUtc": "2021-03-10T22:31:24.507Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 1877, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Service cancelled 3-10-2021 per customer request.\n\nLetter sent to customer / Invoice written off.", + "configurationAuditRecId": 1377, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1878, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Service cancelled 3-10-2021 per customer request.\n\nLetter sent to customer / Invoice written off. (twade)", + "configurationAuditRecId": 1377, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1376, + "configurationRecId": 12, + "localeKeyRecId": 256, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "twade", + "lastUpdatedUtc": "2021-03-10T22:31:12.257Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 1875, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 1376, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1876, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Service cancelled 3-10-2021 per customer request.\n\nLetter sent to customer / Invoice written off.", + "configurationAuditRecId": 1376, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1375, + "configurationRecId": 12, + "localeKeyRecId": 2036, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "twade", + "lastUpdatedUtc": "2021-03-10T22:30:30.460Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 1873, + "auditToken": "old_value", + "auditValueRecId": 2, + "auditValue": "Active", + "configurationAuditRecId": 1375, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 1874, + "auditToken": "new_value", + "auditValueRecId": 3, + "auditValue": "Inactive", + "configurationAuditRecId": 1375, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 47, + "configurationRecId": 12, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:54:58.180Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 71, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Contract", + "configurationAuditRecId": 47, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 46, + "configurationRecId": 12, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:54:52.253Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 70, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Install", + "configurationAuditRecId": 46, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 45, + "configurationRecId": 12, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:54:45.483Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 69, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Sentrynet Monitoring Agreement", + "configurationAuditRecId": 45, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 44, + "configurationRecId": 12, + "localeKeyRecId": 270, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T20:51:59.520Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 68, + "auditToken": "config_name", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 44, + "localeKeyRecId": null + } + ] + } + ] + }, + { + "configRecId": 13, + "csSurveyRecId": 49, + "configName": "Alarm System - Burg", + "mfgCompanyRecId": null, + "serialNumber": "", + "datePurchased": null, + "dateInstalled": "2012-12-27T00:00:00.000Z", + "installedBy": null, + "dateExpiration": "2021-06-01T00:00:00.000Z", + "companyRecId": 19333, + "contactRecId": 44, + "companyAddressRecId": 1039, + "ownerLevelRecId": 11, + "billingUnitRecId": 1, + "contractRecId": null, + "notes": "", + "lastUpdate": "2024-12-02T07:08:27.000Z", + "updatedBy": "slencki", + "modelNumber": "", + "tagNumber": "", + "notificationHistory": ",", + "configStatusRecId": 3, + "deviceId": "", + "mgmtLink": null, + "lastLogin": "", + "mspLastUpdatedBy": null, + "mspLastUpdated": null, + "mobileGuid": "30b80cdf-92c5-4ace-a097-79ab87428490", + "parentRecId": null, + "billFlag": true, + "backupSuccesses": 0, + "backupIncomplete": 0, + "backupFailed": 0, + "backupRestores": 0, + "backupLastBackupDate": null, + "backupServerName": "", + "backupBillableSpaceGb": "0", + "backupProtectedDeviceList": "", + "backupYear": 0, + "backupMonth": 0, + "macAddress": "", + "customField": "", + "ipAddress": "", + "defaultGateway": "", + "osType": "", + "osInfo": "", + "cpuSpeed": "", + "ram": "", + "localHardDrives": "", + "manufacturerRecId": null, + "srSlaRecId": null, + "remoteLink": null, + "lastUpdateUtc": "2024-12-02T13:08:27.000Z", + "dateEnteredUtc": "2018-09-12T21:04:38.000Z", + "enteredBy": "hbarnett", + "ivProductRecId": null, + "incompleteFlag": false, + "conversionSource": null, + "showRemoteFlag": false, + "showAutomateFlag": false, + "needsRenewal": false, + "manufacturerPartNumber": null, + "id": "77f8ba78-70d0-ef11-b2e0-000c29c55070", + "configStatus": { + "configStatusRecId": 3, + "description": "Inactive", + "closedFlag": false, + "updatedBy": "hbarnett", + "lastUpdate": "2018-11-29T14:20:43.000Z", + "defaultFlag": false, + "lastUpdateUtc": "2018-11-29T20:20:43.000Z", + "enteredBy": "CONVERSION", + "dateEnteredUtc": "2005-05-26T16:57:27.000Z", + "id": "b602bb78-70d0-ef11-b2e0-000c29c55070" + }, + "configurationAudits": [ + { + "configurationAuditRecId": 8757, + "configurationRecId": 13, + "localeKeyRecId": 2036, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "slencki", + "lastUpdatedUtc": "2024-12-02T13:08:26.837Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 18428, + "auditToken": "old_value", + "auditValueRecId": 2, + "auditValue": "Active", + "configurationAuditRecId": 8757, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 18429, + "auditToken": "new_value", + "auditValueRecId": 3, + "auditValue": "Inactive", + "configurationAuditRecId": 8757, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 6744, + "configurationRecId": 13, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "gthornto", + "lastUpdatedUtc": "2024-02-14T18:14:57.383Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 13879, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Monitoring ID", + "configurationAuditRecId": 6744, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 13880, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "FW7820", + "configurationAuditRecId": 6744, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 13881, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "1502-7820", + "configurationAuditRecId": 6744, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 6743, + "configurationRecId": 13, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "gthornto", + "lastUpdatedUtc": "2024-02-14T18:14:57.373Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 13876, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Monitoring Company ", + "configurationAuditRecId": 6743, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 13877, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "SentryNet ", + "configurationAuditRecId": 6743, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 13878, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "C.O.P.S.", + "configurationAuditRecId": 6743, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 5612, + "configurationRecId": 13, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "gthornto", + "lastUpdatedUtc": "2023-06-01T20:23:06.283Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 11643, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Communicator", + "configurationAuditRecId": 5612, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 11644, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Connect24", + "configurationAuditRecId": 5612, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 11645, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm.com", + "configurationAuditRecId": 5612, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 4185, + "configurationRecId": 13, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "mscarbro", + "lastUpdatedUtc": "2021-08-05T16:41:08.157Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 8734, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Panel Type", + "configurationAuditRecId": 4185, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 8735, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Impassa", + "configurationAuditRecId": 4185, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 8736, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Qolsys", + "configurationAuditRecId": 4185, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3530, + "configurationRecId": 13, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T20:17:18.040Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7085, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "Account Acquired From", + "configurationAuditRecId": 3530, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7086, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3530, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7087, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Houston Security", + "configurationAuditRecId": 3530, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3529, + "configurationRecId": 13, + "localeKeyRecId": 2043, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T20:17:17.993Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7083, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 3529, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7084, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "6/1/2021", + "configurationAuditRecId": 3529, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 3528, + "configurationRecId": 13, + "localeKeyRecId": 2038, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "jgnech", + "lastUpdatedUtc": "2021-06-25T20:17:17.960Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 7081, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 3528, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 7082, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "Alarm System - Burg", + "configurationAuditRecId": 3528, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 1629, + "configurationRecId": 13, + "localeKeyRecId": 282, + "auditSourceRecId": 1, + "auditTypeRecId": 17, + "updatedBy": "crobinso", + "lastUpdatedUtc": "2021-06-09T16:24:13.500Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 2362, + "auditToken": "question", + "auditValueRecId": null, + "auditValue": "NOT USED", + "configurationAuditRecId": 1629, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2363, + "auditToken": "old_value", + "auditValueRecId": null, + "auditValue": "01-January", + "configurationAuditRecId": 1629, + "localeKeyRecId": null + }, + { + "configurationAuditValueRecId": 2364, + "auditToken": "new_value", + "auditValueRecId": null, + "auditValue": "\" \"", + "configurationAuditRecId": 1629, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 51, + "configurationRecId": 13, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T21:09:46.887Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 75, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Contract", + "configurationAuditRecId": 51, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 50, + "configurationRecId": 13, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T21:09:41.190Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 74, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Initial Install", + "configurationAuditRecId": 50, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 49, + "configurationRecId": 13, + "localeKeyRecId": 143, + "auditSourceRecId": 1, + "auditTypeRecId": 6, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T21:09:35.773Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 73, + "auditToken": "attachment_title", + "auditValueRecId": null, + "auditValue": "Sentrynet Monitoring Agreement", + "configurationAuditRecId": 49, + "localeKeyRecId": null + } + ] + }, + { + "configurationAuditRecId": 48, + "configurationRecId": 13, + "localeKeyRecId": 270, + "auditSourceRecId": 1, + "auditTypeRecId": 16, + "updatedBy": "hbarnett", + "lastUpdatedUtc": "2018-09-12T21:04:37.683Z", + "configurationValues": [ + { + "configurationAuditValueRecId": 72, + "auditToken": "config_name", + "auditValueRecId": null, + "auditValue": "Alarm System", + "configurationAuditRecId": 48, + "localeKeyRecId": null + } + ] + } + ] + } +] \ No newline at end of file diff --git a/dalpuri/package.json b/dalpuri/package.json index 4e7bccb..fc36aee 100644 --- a/dalpuri/package.json +++ b/dalpuri/package.json @@ -2,20 +2,27 @@ "name": "dalpuri", "module": "src/index.ts", "type": "module", - "private": true, + "exports": { + ".": { + "import": "./src/index.ts" + } + }, "devDependencies": { "@types/bun": "latest" }, "peerDependencies": { - "typescript": "^5" + "typescript": "^5.9.2" }, "scripts": { "dev": "NODE_ENV=development bun --watch src/index.ts", "db:generate": "bunx prisma generate", - "export:products": "bun run src/export-products.ts" + "export:products": "bun run src/export-products.ts", + "sync:cw-to-api": "bun run src/sync.ts", + "sync:table": "bun run src/sync-table-updates.ts" }, "dependencies": { "@prisma/adapter-mssql": "^7.5.0", + "@prisma/adapter-pg": "^7.5.0", "@prisma/client": "^7.5.0", "prisma": "^7.5.0", "socket.io": "^4.8.3" diff --git a/dalpuri/prisma/schema.prisma b/dalpuri/prisma/schema.prisma index 5ea04d4..16f19c7 100644 --- a/dalpuri/prisma/schema.prisma +++ b/dalpuri/prisma/schema.prisma @@ -64,15 +64,204 @@ model ProductCatalog { autoUpdateUnitCostFlag Boolean @map("Auto_Update_Unit_Cost_Flag") autoUpdateUnitPriceFlag Boolean @map("Auto_Update_Unit_Price_Flag") - subcategory ProductSubcategory? @relation(fields: [subcategoryRecId], references: [subcategoryRecId], onDelete: NoAction, onUpdate: NoAction) - manufacturer Manufacturer? @relation(fields: [manufacturerRecId], references: [manufacturerRecId], onDelete: NoAction, onUpdate: NoAction) - inventory ProductInventory[] - itemVendors ItemVendor[] + type ProductType? @relation(fields: [typeRecId], references: [typeRecId], onDelete: NoAction, onUpdate: NoAction) + subcategory ProductSubcategory? @relation(fields: [subcategoryRecId], references: [subcategoryRecId], onDelete: NoAction, onUpdate: NoAction) + manufacturer Manufacturer? @relation(fields: [manufacturerRecId], references: [manufacturerRecId], onDelete: NoAction, onUpdate: NoAction) + ivProducts IV_Product[] + inventory ProductInventory[] + itemVendors ItemVendor[] + minimumStockByWarehouse MinimumStockByWarehouse[] @@map("IV_Item") @@schema("dbo") } +model IV_Product { + ivProductRecId Int @id @map("IV_Product_RecID") + billingLogRecId Int? @map("Billing_Log_RecID") + catalogRecId Int @map("IV_Item_RecID") + description String? @map("Description") @db.NVarChar(Max) + quantity Decimal? @map("Quantity") @db.Decimal(18, 2) + + unitPrice Decimal? @map("Unit_Price") @db.Decimal(18, 6) + unitCost Decimal? @map("Unit_Cost") @db.Decimal(18, 6) + + billableFlag Boolean @map("Billable_Flag") + invoiceFlag Boolean @map("Invoice_Flag") + taxableFlag Boolean @map("Taxable_Flag") + + ownerLevelRecId Int @map("Owner_Level_RecID") + billingUnitRecId Int? @map("Billing_Unit_RecID") + + srServiceRecId Int? @map("SR_Service_RecID") + pmProjectRecId Int? @map("PM_Project_RecID") + agrHeaderRecId Int? @map("AGR_Header_RecID") + + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime + + agrAmount Decimal? @map("AGR_Amount") @db.Decimal(18, 2) + dateEntered DateTime? @map("Date_Entered") @db.DateTime + agrMonth Int? @map("AGR_Month") @db.SmallInt + agrYear Int? @map("AGR_Year") @db.SmallInt + teStatusId Int @map("TE_Status_ID") @db.SmallInt + agrDetailRecId Int? @map("AGR_Detail_RecID") + + billingSr Int? @map("billing_sr") + dropShipFlag Boolean @map("DropShip_Flag") + shipmentMethodRecId Int? @map("ShipmentMethod_RecID") + subSuppliedFlag Boolean @map("Sub_Supplied_Flag") + subShipToRecId Int? @map("Sub_ShipTo_RecID") + subLimit Decimal? @map("Sub_Limit") @db.Decimal(18, 2) + + cnGuid String? @map("cn_guid") @db.NVarChar(50) + cnDirtyFlag Boolean @map("Cn_Dirty_Flag") + mobileGuid String @map("Mobile_Guid") @db.UniqueIdentifier + + warehouseBinRecId Int? @map("Warehouse_Bin_RecID") + warehouseRecId Int? @map("Warehouse_RecID") + received Boolean? @map("Received") + orderHeaderRecId Int? @map("Order_Header_RecID") + + cancelFlag Boolean @map("Cancel_Flag") + cancelDate DateTime? @map("Cancel_Date") @db.DateTime + cancelBy Int? @map("Cancel_By") + cancelReason String? @map("Cancel_Reason") @db.NVarChar(100) + quantityCancelled Decimal? @map("Quantity_Cancelled") @db.Decimal(18, 2) + + internalNote String? @map("Internal_Note") @db.NVarChar(1000) + poApprovedFlag Boolean @map("PO_Approved_Flag") + + auditText String? @map("Audit_Text") @db.NVarChar(50) + + opportunityRecId Int? @map("Opportunity_RecID") + soForecastDtlRecId Int? @map("SO_Forecast_Dtl_RecID") + calcPriceFlag Boolean @map("Calc_Price_Flag") + calcCostFlag Boolean @map("Calc_Cost_Flag") + + quoteFlag Boolean @map("Quote_Flag") + quoteWerksFlag Boolean @map("QuoteWerks_Flag") + + subNumber Int @map("Sub_Number") + quoteWerksDocNo String? @map("QuoteWerks_DocNO") @db.NVarChar(50) + quoteWerksDocName String? @map("QuoteWerks_DocName") @db.NVarChar(255) + + closedFlag Boolean @map("Closed_Flag") + closedBy String? @map("Closed_By") @db.NVarChar(15) + dateClosed DateTime? @map("Date_Closed") @db.DateTime + + flatrateFlag Boolean @map("Flatrate_Flag") + + extendedPriceAmount Decimal? @map("Extended_Price_Amount") @db.Decimal(18, 6) + extendedCostAmount Decimal? @map("Extended_Cost_Amount") @db.Decimal(18, 6) + + convertedRecId Int? @map("Converted_RecID") + qtyIncluded Decimal @map("Qty_Included") @db.Decimal(18, 2) + specialOrderFlag Boolean @map("Special_Order_Flag") + minimumStockFlag Boolean @map("Minimum_Stock_Flag") + + qtyShipped Decimal @map("Qty_Shipped") @db.Decimal(18, 2) + qtyPicked Decimal @map("Qty_Picked") @db.Decimal(18, 2) + + receivedFlag Boolean @map("Received_Flag") + + detailShipmentMethod String? @map("Detail_ShipmentMethod") @db.NVarChar(200) + purchaseInfo String? @map("Purchase_Info") @db.NVarChar(Max) + detailSerialList String? @map("Detail_Serial_List") @db.NVarChar(Max) + agdAdditionSerialNumber String? @map("AGD_Addition_Serial_Number") @db.NVarChar(50) + phaseProductFlag Boolean @map("Phase_Product_Flag") + + shortDescription String? @map("Short_Description") @db.NVarChar(2000) + + convertedProjectRecId Int? @map("Converted_Project_RecID") + sequenceNumber Decimal @map("Sequence_Number") @db.Decimal(8, 2) + childInvoiceOverrideFlag Boolean @map("Child_Invoice_Override_Flag") + opportunityExtPriceAmount Decimal? @map("Opportunity_Ext_Price_Amount") @db.Decimal(18, 6) + opportunityExtCostAmount Decimal? @map("Opportunity_Ext_Cost_Amount") @db.Decimal(18, 6) + soOppStatusRecId Int? @map("SO_Opp_Status_RecID") + qtyPurchased Int @map("Qty_Purchased") + vendorRecId Int? @map("Vendor_RecID") + srSlaRecId Int? @map("SR_SLA_RecID") + qtyConvert Int @map("Qty_Convert") + + recurringRevenue Decimal? @map("Recurring_Revenue") @db.Decimal(18, 2) + recurringCost Decimal? @map("Recurring_Cost") @db.Decimal(18, 6) + recurringDateStart DateTime? @map("Recurring_Date_Start") @db.DateTime + recurringDateEnd DateTime? @map("Recurring_Date_End") @db.DateTime + + billCycleId String? @map("Bill_Cycle_ID") @db.NVarChar(5) + cycleBasisId String? @map("Cycle_Basis_ID") @db.Char(1) + recurringFlag Boolean @map("Recurring_Flag") + + customerUsageTypeRecId Int? @map("Customer_Usage_Type_RecID") + purchaseDate DateTime? @map("Purchase_Date") @db.Date + ivPriceMethodId String? @map("IV_Price_Method_ID") @db.NVarChar(2) + discountAmount Decimal? @map("Discount_Amount") @db.Decimal(18, 6) + + listPrice Decimal? @map("List_Price") @db.Decimal(18, 6) + vendorSku String? @map("Vendor_SKU") @db.NVarChar(50) + convertedAgrDetailRecId Int? @map("Converted_AGR_Detail_RecID") + lastUpdatedUTC DateTime @map("Last_Update_UTC") @db.DateTime + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime + + purchaseTrackingNumbers String? @map("Purchase_Tracking_Numbers") @db.NVarChar(200) + recurringOneTimeFlag Boolean @map("Recurring_OneTime_Flag") + + enteredBy String @map("Entered_By") @db.NVarChar(15) + cancelDateUtc DateTime? @map("Cancel_Date_UTC") @db.DateTime + dateClosedUtc DateTime? @map("Date_Closed_UTC") @db.DateTime + + shipSet String? @map("Ship_Set") @db.NVarChar(10) + invoiceGroupingRecId Int? @map("Invoice_Grouping_RecID") + asioSubscriptionsId String? @map("Asio_Subscriptions_ID") @db.UniqueIdentifier + + id String? @map("Id") @db.UniqueIdentifier + + changeOrderFlag Boolean @map("Change_Order_Flag") + agrTypeRecId Int? @map("AGR_Type_RecID") + splitFromRecId Int? @map("Split_From_RecID") + + customFields V_IV_Product_Custom_Fields[] + productCatalog ProductCatalog @relation(fields: [catalogRecId], references: [catalogRecId], onDelete: NoAction, onUpdate: NoAction) + ownerLevel OwnerLevel @relation(fields: [ownerLevelRecId], references: [ownerLevelRecId], onDelete: NoAction, onUpdate: NoAction) + billingUnit BillingUnit? @relation(fields: [billingUnitRecId], references: [billingUnitRecId], onDelete: NoAction, onUpdate: NoAction) + srService SrService? @relation(fields: [srServiceRecId], references: [srServiceRecId], onDelete: NoAction, onUpdate: NoAction) + pmProject PmProject? @relation("IVProductProject", fields: [pmProjectRecId], references: [pmProjectRecId], onDelete: NoAction, onUpdate: NoAction) + opportunity Opportunity? @relation(fields: [opportunityRecId], references: [opportunityRecId], onDelete: NoAction, onUpdate: NoAction) + forecastItem SoForecastDtl? @relation(fields: [soForecastDtlRecId], references: [soForecastDtlRecId], onDelete: NoAction, onUpdate: NoAction) + soOppStatus SoOppStatus? @relation(fields: [soOppStatusRecId], references: [soOppStatusRecId], onDelete: NoAction, onUpdate: NoAction) + agrType AgrType? @relation(fields: [agrTypeRecId], references: [agrTypeRecId], onDelete: NoAction, onUpdate: NoAction) + warehouse Warehouse? @relation(fields: [warehouseRecId], references: [warehouseRecId], onDelete: NoAction, onUpdate: NoAction) + warehouseBin WarehouseBin? @relation(fields: [warehouseBinRecId], references: [warehouseBinRecId], onDelete: NoAction, onUpdate: NoAction) + convertedFrom IV_Product? @relation("IVProductConvertedFrom", fields: [convertedRecId], references: [ivProductRecId], onDelete: NoAction, onUpdate: NoAction) + convertedTo IV_Product[] @relation("IVProductConvertedFrom") + splitFrom IV_Product? @relation("IVProductSplitFrom", fields: [splitFromRecId], references: [ivProductRecId], onDelete: NoAction, onUpdate: NoAction) + splitChildren IV_Product[] @relation("IVProductSplitFrom") + convertedProject PmProject? @relation("IVProductConvertedProject", fields: [convertedProjectRecId], references: [pmProjectRecId], onDelete: NoAction, onUpdate: NoAction) + + @@map("IV_Product") + @@schema("dbo") +} + +model ProductType { + typeRecId Int @id @map("IV_Type_RecID") + description String? @map("Description") @db.NVarChar(50) + inactiveFlag Boolean @map("Inactive_Flag") + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + itemTypeXref String? @map("Item_Type_Xref") @db.NVarChar(30) + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + defaultFlag Boolean @map("Default_Flag") + id String @map("Id") @db.UniqueIdentifier + + products ProductCatalog[] + + @@map("IV_Type") + @@schema("dbo") +} + model ProductInventory { inventoryRecId Int @id @map("Inventory_By_Warehouse_RecID") catalogRecId Int @map("IV_Item_RecID") @@ -83,11 +272,93 @@ model ProductInventory { updatedBy String? @map("Updated_By") @db.NVarChar(15) productCatalog ProductCatalog @relation(fields: [catalogRecId], references: [catalogRecId], onDelete: NoAction, onUpdate: NoAction) + warehouse Warehouse @relation(fields: [warehouseRecId], references: [warehouseRecId], onDelete: NoAction, onUpdate: NoAction) + warehouseBin WarehouseBin @relation(fields: [warehouseBinRecId], references: [warehouseBinRecId], onDelete: NoAction, onUpdate: NoAction) @@map("Inventory_By_Warehouse") @@schema("dbo") } +model Warehouse { + warehouseRecId Int @id @map("Warehouse_RecID") + companyRecId Int? @map("Company_RecID") + ownerLevelRecId Int? @map("Owner_Level_RecID") + billingUnitRecId Int? @map("Billing_Unit_RecID") + memberRecId Int? @map("Member_RecID") + companyAddressRecId Int? @map("Company_Address_RecID") + warehouseName String? @map("Warehouse_Name") @db.NVarChar(50) + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + defaultFlag Boolean? @map("Default_Flag") + overallDefaultFlag Boolean @map("OverallDefault_Flag") + locationXref String? @map("Location_Xref") @db.NVarChar(10) + inactiveFlag Boolean @map("Inactive_Flag") + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + contactRecId Int? @map("Contact_RecID") + lockedFlag Boolean @map("Locked_Flag") + id String @map("Id") @db.UniqueIdentifier + + inventory ProductInventory[] + ivProducts IV_Product[] + warehouseBins WarehouseBin[] + minimumStockByWarehouse MinimumStockByWarehouse[] + members Member[] + + @@map("Warehouse") + @@schema("dbo") +} + +model WarehouseBin { + warehouseBinRecId Int @id @map("Warehouse_Bin_RecID") + warehouseRecId Int? @map("Warehouse_RecID") + description String? @map("Description") @db.NVarChar(50) + ownerLevelRecId Int? @map("Owner_Level_RecID") + billingUnitRecId Int? @map("Billing_Unit_RecID") + length String? @map("Warehouse_Bin_Length") @db.NVarChar(10) + width String? @map("Warehouse_Bin_Width") @db.NVarChar(10) + height String? @map("Warehouse_Bin_Height") @db.NVarChar(10) + weight Int? @map("Warehouse_Bin_Weight") + minQuantity Decimal? @map("Min_Quantity") @db.Decimal(18, 2) + maxQuantity Decimal? @map("Max_Quantity") @db.Decimal(18, 2) + overflowBin Int? @map("Overflow_Bin") + memberRecId Int? @map("Member_RecID") + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + defaultFlag Boolean? @map("Default_Flag") + inactiveFlag Boolean @map("Inactive_Flag") + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + id String @map("Id") @db.UniqueIdentifier + + warehouse Warehouse? @relation(fields: [warehouseRecId], references: [warehouseRecId], onDelete: NoAction, onUpdate: NoAction) + inventory ProductInventory[] + ivProducts IV_Product[] + members Member[] + + @@map("Warehouse_Bin") + @@schema("dbo") +} + +model MinimumStockByWarehouse { + minimumStockByWarehouseRecId Int @id @map("Minimum_Stock_By_Warehouse_RecID") + catalogRecId Int @map("IV_Item_RecID") + warehouseRecId Int @map("Warehouse_RecID") + minimumStock Int @map("Minimum_Stock") + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + updatedBy String? @map("Updated_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + enteredBy String? @map("Entered_By") @db.NVarChar(15) + + productCatalog ProductCatalog @relation(fields: [catalogRecId], references: [catalogRecId], onDelete: NoAction, onUpdate: NoAction) + warehouse Warehouse @relation(fields: [warehouseRecId], references: [warehouseRecId], onDelete: NoAction, onUpdate: NoAction) + + @@map("Minimum_Stock_By_Warehouse") + @@schema("dbo") +} + model ProductSubcategory { subcategoryRecId Int @id @map("IV_SubCat_RecID") categoryRecId Int @map("IV_Cat_RecID") @@ -386,10 +657,15 @@ model Member { useCalltoFlag Boolean? @map("Use_Callto_Flag") id String @map("Id") @db.UniqueIdentifier - company Company? @relation(fields: [ownerLevelRecId], references: [companyRecId], onDelete: NoAction, onUpdate: NoAction, map: "Member_Company_fk") + company Company? @relation(fields: [ownerLevelRecId], references: [companyRecId], onDelete: NoAction, onUpdate: NoAction, map: "Member_Company_fk") + warehouse Warehouse? @relation(fields: [warehouseRecId], references: [warehouseRecId], onDelete: NoAction, onUpdate: NoAction) + warehouseBin WarehouseBin? @relation(fields: [warehouseBinRecId], references: [warehouseBinRecId], onDelete: NoAction, onUpdate: NoAction) - approvedOpportunities Opportunity[] @relation("OpportunityApprovedBy") - rejectedOpportunities Opportunity[] @relation("OpportunityRejectedBy") + ticketNotes TicketNote[] + + approvedOpportunities Opportunity[] @relation("OpportunityApprovedBy") + rejectedOpportunities Opportunity[] @relation("OpportunityRejectedBy") + opportunityMembers OpportunityMember[] @relation("OpportunityMemberToMember") @@map("Member") @@schema("dbo") @@ -481,9 +757,12 @@ model Opportunity { // Reverse relations (one-to-many from Opportunity) activities SoActivity[] + customValues V_RptOpportunityCustomFields[] opportunityNotes SoNote[] forecastItems SoForecastDtl[] + ivProducts IV_Product[] contacts SoInfluencer[] + members OpportunityMember[] @relation("OpportunityMemberToOpportunity") @@map("SO_Opportunity") @@schema("dbo") @@ -529,6 +808,7 @@ model SoOppStatus { id String @map("Id") @db.UniqueIdentifier opportunities Opportunity[] + ivProducts IV_Product[] @@map("SO_Opp_Status") @@schema("dbo") @@ -612,20 +892,21 @@ model OwnerLevel { id String @map("Id") @db.UniqueIdentifier opportunities Opportunity[] + ivProducts IV_Product[] @@map("Owner_Level") @@schema("dbo") } model Department { - departmentRecId Int @id @map("Department_RecID") - departmentName String? @map("Department_Name") @db.NVarChar(30) - lastUpdate DateTime @map("Last_Update") @db.DateTime2 - updatedBy String? @map("Updated_By") @db.NVarChar(30) - lastUpdateUtc DateTime @map("Last_Update_UTC") @db.DateTime2 - enteredBy String @map("Entered_By") @db.NVarChar(15) - dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 - id String @map("Id") @db.UniqueIdentifier + departmentRecId Int @id @map("Department_RecID") + departmentName String? @map("Department_Name") @db.NVarChar(30) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + updatedBy String? @map("Updated_By") @db.NVarChar(30) + lastUpdatedUTC DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + id String @map("Id") @db.UniqueIdentifier contacts Contact[] @relation("ContactDepartment") @@ -707,6 +988,7 @@ model Contact { id String? @map("Id") @db.UniqueIdentifier opportunities Opportunity[] @relation("OpportunityContact") + ticketNotes TicketNote[] department Department? @relation("ContactDepartment", fields: [departmentRecId], references: [departmentRecId]) @@map("Contact") @@ -759,6 +1041,130 @@ model CompanyAddress { @@schema("dbo") } +// ===================== +// CONFIGURATION +// ===================== + +model Configuration { + configRecId Int @id @map("Config_RecID") + csSurveyRecId Int @map("CS_Survey_RecID") + configName String? @map("Config_Name") @db.NVarChar(100) + mfgCompanyRecId Int? @map("Mfg_Company_RecID") + serialNumber String? @map("Serial_Number") @db.NVarChar(Max) + datePurchased DateTime? @map("Date_Purchased") @db.DateTime + dateInstalled DateTime? @map("Date_Installed") @db.DateTime + installedBy Int? @map("Installed_By") + dateExpiration DateTime? @map("Date_Expiration") @db.DateTime + companyRecId Int @map("Company_RecID") + contactRecId Int? @map("Contact_RecID") + companyAddressRecId Int? @map("Company_Address_RecID") + ownerLevelRecId Int? @map("Owner_Level_RecID") + billingUnitRecId Int? @map("Billing_Unit_RecID") + contractRecId Int? @map("Contract_RecID") + notes String? @map("Notes") @db.NVarChar(Max) + lastUpdate DateTime? @map("Last_Update") @db.DateTime2 + updatedBy String? @map("Updated_By") @db.NVarChar(15) + modelNumber String? @map("Model_Number") @db.NVarChar(50) + tagNumber String? @map("Tag_Number") @db.NVarChar(50) + notificationHistory String? @map("Notification_History") @db.VarChar(Max) + configStatusRecId Int? @map("Config_Status_RecID") + deviceId String? @map("Device_ID") @db.NVarChar(100) + mgmtLink String? @map("Mgmt_Link") @db.NVarChar(1000) + lastLogin String? @map("Last_Login") @db.NVarChar(100) + mspLastUpdatedBy String? @map("Msp_Last_Updated_By") @db.NVarChar(50) + mspLastUpdated DateTime? @map("Msp_Last_Updated") @db.DateTime + mobileGuid String @map("Mobile_Guid") @db.UniqueIdentifier + parentRecId Int? @map("Parent_RecID") + billFlag Boolean @map("Bill_Flag") + backupSuccesses Int? @map("Backup_Successes") + backupIncomplete Int? @map("Backup_Incomplete") + backupFailed Int? @map("Backup_Failed") + backupRestores Int? @map("Backup_Restores") + backupLastBackupDate DateTime? @map("Backup_Last_Backup_Date") @db.DateTime + backupServerName String? @map("Backup_Server_Name") @db.NVarChar(50) + backupBillableSpaceGb Decimal? @map("Backup_Billable_Space_Gb") @db.Decimal(18, 2) + backupProtectedDeviceList String? @map("Backup_Protected_Device_List") @db.NVarChar(Max) + backupYear Int? @map("Backup_Year") + backupMonth Int? @map("Backup_Month") + macAddress String? @map("MAC_Address") @db.NVarChar(25) + customField String? @map("Custom_Field") @db.NVarChar(Max) + ipAddress String? @map("IP_Address") @db.NVarChar(50) + defaultGateway String? @map("Default_Gateway") @db.NVarChar(50) + osType String? @map("Os_Type") @db.NVarChar(250) + osInfo String? @map("Os_Info") @db.NVarChar(250) + cpuSpeed String? @map("Cpu_Speed") @db.NVarChar(100) + ram String? @map("RAM") @db.NVarChar(25) + localHardDrives String? @map("Local_Hard_Drives") @db.NVarChar(Max) + manufacturerRecId Int? @map("Manufacturer_RecID") + srSlaRecId Int? @map("SR_SLA_RecID") + remoteLink String? @map("Remote_Link") @db.NVarChar(1000) + lastUpdatedUTC DateTime @map("Last_Update_UTC") @db.DateTime2 + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + ivProductRecId Int? @map("IV_Product_RecID") + incompleteFlag Boolean @map("Incomplete_Flag") + conversionSource String? @map("Conversion_Source") @db.NVarChar(15) + showRemoteFlag Boolean @map("Show_Remote_Flag") + showAutomateFlag Boolean @map("Show_Automate_Flag") + needsRenewal Boolean @map("Needs_Renewal") + manufacturerPartNumber String? @map("Manufacturer_Part_Number") @db.NVarChar(50) + id String @map("Id") @db.UniqueIdentifier + + configStatus ConfigurationStatus? @relation(fields: [configStatusRecId], references: [configStatusRecId], onDelete: NoAction, onUpdate: NoAction) + configurationAudits ConfigurationAudit[] + + @@map("Config") + @@schema("dbo") +} + +model ConfigurationStatus { + configStatusRecId Int @id @map("Config_Status_RecID") + description String? @map("Description") @db.NVarChar(50) + closedFlag Boolean? @map("Closed_Flag") + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + defaultFlag Boolean? @map("Default_Flag") + lastUpdatedUTC DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + id String @map("Id") @db.UniqueIdentifier + + configurations Configuration[] + + @@map("Config_Status") + @@schema("dbo") +} + +model ConfigurationAudit { + configurationAuditRecId Int @id @map("Configuration_Audit_RecId") + configurationRecId Int @map("Configuration_RecId") + localeKeyRecId Int? @map("Locale_Key_RecId") + auditSourceRecId Int @map("Audit_Source_RecId") @db.SmallInt + auditTypeRecId Int @map("Audit_Type_RecId") @db.SmallInt + updatedBy String @map("Updated_by") @db.NVarChar(15) + lastUpdatedUtc DateTime @map("Last_Updated_utc") @db.DateTime2 + + configuration Configuration @relation(fields: [configurationRecId], references: [configRecId], onDelete: NoAction, onUpdate: NoAction) + configurationValues ConfigurationAuditValue[] + + @@map("Configuration_Audit") + @@schema("dbo") +} + +model ConfigurationAuditValue { + configurationAuditValueRecId Int @id @map("Configuration_Audit_Value_RecID") + auditToken String @map("Audit_Token") @db.NVarChar(2000) + auditValueRecId Int? @map("Audit_Value_RecID") + auditValue String @map("Audit_Value") @db.NVarChar(Max) + configurationAuditRecId Int @map("Configuration_Audit_RecID") + localeKeyRecId Int? @map("Locale_Key_RecID") + + configurationAudit ConfigurationAudit @relation(fields: [configurationAuditRecId], references: [configurationAuditRecId], onDelete: NoAction, onUpdate: NoAction) + + @@map("Configuration_Audit_Value") + @@schema("dbo") +} + model BillingTerms { billingTermsRecId Int @id @map("Billing_Terms_RecID") description String? @map("Description") @db.NVarChar(50) @@ -799,11 +1205,41 @@ model TaxCode { id String @map("Id") @db.UniqueIdentifier opportunities Opportunity[] + levels TaxCodeLevel[] @@map("Tax_Code") @@schema("dbo") } +model TaxCodeLevel { + taxCodeLevelRecId Int @id @map("Tax_Code_Level_RecID") + taxCodeRecId Int @map("Tax_Code_RecID") + taxRate Decimal @map("Tax_Rate") @db.Decimal(18, 6) + taxRateType String @map("Tax_Rate_Type") @db.Char(1) + taxMax Decimal? @map("Tax_Max") @db.Decimal(18, 6) + taxXref String? @map("Tax_Xref") @db.NVarChar(50) + agencyXref String? @map("Agency_Xref") @db.NVarChar(100) + levelCaption String? @map("Level_Caption") @db.NVarChar(25) + taxServices Boolean @map("Tax_Services") + taxExpenses Boolean @map("Tax_Expenses") + taxProducts Boolean @map("Tax_Products") + singleUnit Int @map("Single_Unit") + singleUnitMinimum Decimal? @map("Single_Unit_Minimum") @db.Decimal(18, 6) + singleUnitMaximum Decimal? @map("Single_Unit_Maximum") @db.Decimal(18, 6) + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + updatedBy String @map("Updated_By") @db.NVarChar(15) + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + coreEntityUpdatedBy String @map("Core_Entity_Updated_By") @db.NVarChar(15) + coreEntityLastUpdateUtc DateTime? @map("Core_Entity_Last_Update_UTC") @db.DateTime2 + coreEntityId String? @map("Core_Entity_ID") @db.UniqueIdentifier + + taxCode TaxCode @relation(fields: [taxCodeRecId], references: [taxCodeRecId], onDelete: NoAction, onUpdate: NoAction) + + @@map("Tax_Code_Level") + @@schema("dbo") +} + model Currency { currencyRecId Int @id @map("Currency_RecID") currencyId String @map("Currency_ID") @db.NVarChar(10) @@ -845,6 +1281,7 @@ model BillingUnit { id String @map("Id") @db.UniqueIdentifier opportunities Opportunity[] + ivProducts IV_Product[] @@map("Billing_Unit") @@schema("dbo") @@ -942,7 +1379,9 @@ model PmProject { id String? @map("Id") @db.UniqueIdentifier billingLogRecId Int? @map("Billing_Log_RecID") - opportunities Opportunity[] + opportunities Opportunity[] + ivProducts IV_Product[] @relation("IVProductProject") + convertedIvProducts IV_Product[] @relation("IVProductConvertedProject") @@map("PM_Project") @@schema("dbo") @@ -1055,108 +1494,124 @@ model AgrType { id String @map("Id") @db.UniqueIdentifier opportunities Opportunity[] + ivProducts IV_Product[] @@map("AGR_Type") @@schema("dbo") } model SrService { - srServiceRecId Int @id @map("SR_Service_RecID") - srTypeRecId Int? @map("SR_Type_RecID") - srLocationRecId Int? @map("SR_Location_RecID") - srStatusRecId Int @map("SR_Status_RecID") - srSourceRecId Int? @map("SR_Source_RecID") - srUrgencyRecId Int @map("SR_Urgency_RecID") - srTeamRecId Int? @map("SR_Team_RecID") - enteredBy String? @map("Entered_By") @db.NVarChar(15) - enteredNotifyFlag Boolean @map("Entered_Notify_Flag") - dateEntered DateTime? @map("Date_Entered") @db.DateTime - dateReq DateTime? @map("Date_Req") @db.Date - companyRecId Int? @map("Company_RecID") - contactRecId Int? @map("Contact_RecID") - assignedByRecId Int? @map("Assigned_By_RecID") - assignedNotifyFlag Boolean @map("Assigned_Notify_Flag") - billingUnitRecId Int? @map("Billing_Unit_RecID") - redFlag Boolean @map("Red_Flag") - updatedBy String? @map("Updated_By") @db.NVarChar(15) - lastUpdate DateTime? @map("Last_Update") @db.DateTime - addressLine1 String? @map("Address_Line1") @db.NVarChar(50) - addressLine2 String? @map("Address_Line2") @db.NVarChar(50) - city String? @map("City") @db.NVarChar(50) - stateId String? @map("State_ID") @db.NVarChar(50) - zip String? @map("Zip") @db.NVarChar(12) - contactName String? @map("Contact_Name") @db.NVarChar(62) - phoneNbr String? @map("PhoneNbr") @db.NVarChar(20) - extension String? @map("Extension") @db.NVarChar(15) - ownerLevelRecId Int @map("Owner_Level_RecID") - emailAddress String? @map("Email_Address") @db.NVarChar(250) - summary String? @map("Summary") @db.NVarChar(100) - publishFlag Boolean @map("Publish_Flag") - closedBy String? @map("Closed_By") @db.NVarChar(15) - dateClosed DateTime? @map("Date_Closed") @db.DateTime - hoursBudget Decimal? @map("Hours_Budget") @db.Decimal(18, 2) - timeBillableFlag Boolean? @map("Time_Billable_Flag") - timeBillableFlagAlt Boolean? @map("TimeBillable_Flag") - expBillableFlag Boolean? @map("ExpBillable_Flag") - activityClassRecId Int? @map("Activity_Class_RecID") - activityTypeRecId Int? @map("Activity_Type_RecID") - poNumber String? @map("PO_Number") @db.NVarChar(50) - reference String? @map("Reference") @db.NVarChar(50) - billCompleteFlag Boolean? @map("BillComplete_Flag") - billUnappFlag Boolean? @map("BillUnapp_Flag") - billMethod String? @map("Bill_Method") @db.Char(1) - billSrFlag Boolean? @map("BillSR_Flag") - billingAmount Decimal? @map("Billing_Amount") @db.Decimal(18, 2) - companyAddressRecId Int? @map("Company_Address_RecID") - timeInvoiceFlag Boolean? @map("TimeInvoice_Flag") - expInvoiceFlag Boolean? @map("ExpInvoice_Flag") - prodInvoiceFlag Boolean? @map("ProdInvoice_Flag") - prodBillableFlag Boolean? @map("ProdBillable_Flag") - agrHeaderRecId Int? @map("AGR_Header_RecID") - overrideFlag Boolean? @map("Override_Flag") - srBillingMethodId String? @map("SR_Billing_Method_ID") @db.Char(1) - agrAmount Decimal? @map("AGR_Amount") @db.Decimal(18, 2) - agrMonth Int? @map("AGR_Month") @db.SmallInt - agrYear Int? @map("AGR_Year") @db.SmallInt - srSeverityRecId Int? @map("SR_Severity_RecID") - srImpactRecId Int? @map("SR_Impact_RecID") - srBoardRecId Int @map("SR_Board_RecID") - billingCompanyRecId Int? @map("Billing_Company_RecID") - billingAddressRecId Int? @map("Billing_Address_RecID") - externalHoursWorked Decimal? @map("External_Hours_Worked") @db.Decimal(18, 0) - opportunityRecId Int? @map("Opportunity_RecID") - mobileGuid String @map("Mobile_Guid") @db.UniqueIdentifier - approvedFlag Boolean @map("Approved_Flag") - externalXref String? @map("External_Xref") @db.NVarChar(100) - siteName String? @map("Site_Name") @db.NVarChar(50) - subDateAccepted DateTime? @map("Sub_Date_Accepted") @db.DateTime - subCompanyName String? @map("Sub_Company_Name") @db.NVarChar(50) - subCompanyRecId Int? @map("Sub_Company_RecID") - subContactName String? @map("Sub_Contact_Name") @db.NVarChar(62) - subContactRecId Int? @map("Sub_Contact_RecID") - subEmailAddress String? @map("Sub_Email_Address") @db.NVarChar(250) - subPhoneNbr String? @map("Sub_PhoneNbr") @db.NVarChar(30) - subExtension String? @map("Sub_Extension") @db.NVarChar(15) - subBillingMethodId String? @map("Sub_Billing_Method_ID") @db.Char(1) - subBillingAmount Decimal? @map("Sub_Billing_Amount") @db.Decimal(18, 2) - subTicketNbr String? @map("Sub_Ticket_Nbr") @db.NVarChar(25) - subOverrideFlag Boolean? @map("Sub_Override_Flag") - rejectedFlag Boolean @map("Rejected_Flag") - dateRejected DateTime? @map("Date_Rejected") @db.DateTime - cnSurveyCompletedFlag Boolean @map("Cn_Survey_Completed_Flag") - pmPhaseRecId Int? @map("PM_Phase_RecID") - wbsCode String? @map("WBS_Code") @db.VarChar(50) - recType String @map("Rec_Type") @db.Char(1) - emailContactFlag Boolean? @map("EmailContact_Flag") - emailResourceFlag Boolean? @map("EmailResource_Flag") - emailCcFlag Boolean? @map("EmailCC_Flag") - emailCc String? @map("EmailCC") @db.NVarChar(4000) - custUpdateFlag String? @map("CustUpdate_Flag") @db.Char(1) - countryRecId Int? @map("Country_RecID") - kbCategoryRecId Int? @map("KB_Category_RecID") - kbSubCategoryRecId Int? @map("KB_SubCategory_RecID") - kbLinkRecId Int? @map("KB_Link_RecID") - kbLinkType String? @map("KB_Link_Type") @db.Char(1) + srServiceRecId Int @id @map("SR_Service_RecID") + srTypeRecId Int? @map("SR_Type_RecID") + srLocationRecId Int? @map("SR_Location_RecID") + srStatusRecId Int @map("SR_Status_RecID") + srSourceRecId Int? @map("SR_Source_RecID") + srUrgencyRecId Int @map("SR_Urgency_RecID") + srTeamRecId Int? @map("SR_Team_RecID") + enteredBy String? @map("Entered_By") @db.NVarChar(15) + enteredNotifyFlag Boolean @map("Entered_Notify_Flag") + dateEntered DateTime? @map("Date_Entered") @db.DateTime + dateReq DateTime? @map("Date_Req") @db.Date + companyRecId Int? @map("Company_RecID") + contactRecId Int? @map("Contact_RecID") + assignedByRecId Int? @map("Assigned_By_RecID") + assignedNotifyFlag Boolean @map("Assigned_Notify_Flag") + billingUnitRecId Int? @map("Billing_Unit_RecID") + redFlag Boolean @map("Red_Flag") + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime? @map("Last_Update") @db.DateTime + addressLine1 String? @map("Address_Line1") @db.NVarChar(50) + addressLine2 String? @map("Address_Line2") @db.NVarChar(50) + city String? @map("City") @db.NVarChar(50) + stateId String? @map("State_ID") @db.NVarChar(50) + zip String? @map("Zip") @db.NVarChar(12) + contactName String? @map("Contact_Name") @db.NVarChar(62) + phoneNbr String? @map("PhoneNbr") @db.NVarChar(20) + extension String? @map("Extension") @db.NVarChar(15) + ownerLevelRecId Int @map("Owner_Level_RecID") + emailAddress String? @map("Email_Address") @db.NVarChar(250) + summary String? @map("Summary") @db.NVarChar(100) + publishFlag Boolean @map("Publish_Flag") + closedBy String? @map("Closed_By") @db.NVarChar(15) + dateClosed DateTime? @map("Date_Closed") @db.DateTime + hoursBudget Decimal? @map("Hours_Budget") @db.Decimal(18, 2) + timeBillableFlag Boolean? @map("Time_Billable_Flag") + timeBillableFlagAlt Boolean? @map("TimeBillable_Flag") + expBillableFlag Boolean? @map("ExpBillable_Flag") + activityClassRecId Int? @map("Activity_Class_RecID") + activityTypeRecId Int? @map("Activity_Type_RecID") + poNumber String? @map("PO_Number") @db.NVarChar(50) + reference String? @map("Reference") @db.NVarChar(50) + + billCompleteFlag Boolean? @map("BillComplete_Flag") + billUnappFlag Boolean? @map("BillUnapp_Flag") + billMethod String? @map("Bill_Method") @db.Char(1) + billSrFlag Boolean? @map("BillSR_Flag") + billingAmount Decimal? @map("Billing_Amount") @db.Decimal(18, 2) + companyAddressRecId Int? @map("Company_Address_RecID") + + timeInvoiceFlag Boolean? @map("TimeInvoice_Flag") + expInvoiceFlag Boolean? @map("ExpInvoice_Flag") + prodInvoiceFlag Boolean? @map("ProdInvoice_Flag") + prodBillableFlag Boolean? @map("ProdBillable_Flag") + + agrHeaderRecId Int? @map("AGR_Header_RecID") + overrideFlag Boolean? @map("Override_Flag") + srBillingMethodId String? @map("SR_Billing_Method_ID") @db.Char(1) + + agrAmount Decimal? @map("AGR_Amount") @db.Decimal(18, 2) + agrMonth Int? @map("AGR_Month") @db.SmallInt + agrYear Int? @map("AGR_Year") @db.SmallInt + + srSeverityRecId Int? @map("SR_Severity_RecID") + srImpactRecId Int? @map("SR_Impact_RecID") + srBoardRecId Int @map("SR_Board_RecID") + + billingCompanyRecId Int? @map("Billing_Company_RecID") + billingAddressRecId Int? @map("Billing_Address_RecID") + + externalHoursWorked Decimal? @map("External_Hours_Worked") @db.Decimal(18, 0) + opportunityRecId Int? @map("Opportunity_RecID") + mobileGuid String @map("Mobile_Guid") @db.UniqueIdentifier + approvedFlag Boolean @map("Approved_Flag") + externalXref String? @map("External_Xref") @db.NVarChar(100) + siteName String? @map("Site_Name") @db.NVarChar(50) + + subDateAccepted DateTime? @map("Sub_Date_Accepted") @db.DateTime + subCompanyName String? @map("Sub_Company_Name") @db.NVarChar(50) + subCompanyRecId Int? @map("Sub_Company_RecID") + subContactName String? @map("Sub_Contact_Name") @db.NVarChar(62) + subContactRecId Int? @map("Sub_Contact_RecID") + subEmailAddress String? @map("Sub_Email_Address") @db.NVarChar(250) + subPhoneNbr String? @map("Sub_PhoneNbr") @db.NVarChar(30) + subExtension String? @map("Sub_Extension") @db.NVarChar(15) + subBillingMethodId String? @map("Sub_Billing_Method_ID") @db.Char(1) + subBillingAmount Decimal? @map("Sub_Billing_Amount") @db.Decimal(18, 2) + subTicketNbr String? @map("Sub_Ticket_Nbr") @db.NVarChar(25) + subOverrideFlag Boolean? @map("Sub_Override_Flag") + + rejectedFlag Boolean @map("Rejected_Flag") + dateRejected DateTime? @map("Date_Rejected") @db.DateTime + + cnSurveyCompletedFlag Boolean @map("Cn_Survey_Completed_Flag") + pmPhaseRecId Int? @map("PM_Phase_RecID") + + wbsCode String? @map("WBS_Code") @db.VarChar(50) + recType String @map("Rec_Type") @db.Char(1) + + emailContactFlag Boolean? @map("EmailContact_Flag") + emailResourceFlag Boolean? @map("EmailResource_Flag") + emailCcFlag Boolean? @map("EmailCC_Flag") + emailCc String? @map("EmailCC") @db.NVarChar(4000) + + custUpdateFlag String? @map("CustUpdate_Flag") @db.Char(1) + countryRecId Int? @map("Country_RecID") + + kbCategoryRecId Int? @map("KB_Category_RecID") + kbSubCategoryRecId Int? @map("KB_SubCategory_RecID") + kbLinkRecId Int? @map("KB_Link_RecID") + kbLinkType String? @map("KB_Link_Type") @db.Char(1) + srSlaRecId Int? @map("SR_SLA_RecID") orderHeaderRecId Int? @map("Order_Header_RecID") expBillableFlagAlt Boolean? @map("Exp_Billable_Flag") @@ -1189,12 +1644,426 @@ model SrService { parentRecId Int? @map("Parent_RecID") latestSentimentScore Decimal? @map("Latest_Sentiment_Score") @db.Decimal(9, 2) + srType SrType? @relation(fields: [srTypeRecId], references: [srTypeRecId], onDelete: NoAction, onUpdate: NoAction) + srLocation SrLocation? @relation(fields: [srLocationRecId], references: [srLocationRecId], onDelete: NoAction, onUpdate: NoAction) + srStatus SrStatus @relation(fields: [srStatusRecId], references: [srStatusRecId], onDelete: NoAction, onUpdate: NoAction) + srSource SrSource? @relation(fields: [srSourceRecId], references: [srSourceRecId], onDelete: NoAction, onUpdate: NoAction) + srUrgency SrUrgency @relation(fields: [srUrgencyRecId], references: [srUrgencyRecId], onDelete: NoAction, onUpdate: NoAction) + srTeam SrTeam? @relation(fields: [srTeamRecId], references: [srTeamRecId], onDelete: NoAction, onUpdate: NoAction) + activityClass ActivityClass? @relation(fields: [activityClassRecId], references: [activityClassRecId], onDelete: NoAction, onUpdate: NoAction) + activityType ActivityType? @relation(fields: [activityTypeRecId], references: [activityTypeRecId], onDelete: NoAction, onUpdate: NoAction) + srSeverity SrSeverity? @relation(fields: [srSeverityRecId], references: [srSeverityRecId], onDelete: NoAction, onUpdate: NoAction) + srImpact SrImpact? @relation(fields: [srImpactRecId], references: [srImpactRecId], onDelete: NoAction, onUpdate: NoAction) + srBoard SrBoard @relation(fields: [srBoardRecId], references: [srBoardRecId], onDelete: NoAction, onUpdate: NoAction) + country Country? @relation(fields: [countryRecId], references: [countryRecId], onDelete: NoAction, onUpdate: NoAction) + opportunities Opportunity[] + ticketNotes TicketNote[] + ivProducts IV_Product[] @@map("SR_Service") @@schema("dbo") } +model TicketNote { + ticketNoteRecId Int @id @map("SR_Detail_RecID") + srServiceRecId Int @map("SR_Service_RecID") + + problemFlag Boolean @map("Problem_Flag") + resolutionFlag Boolean @map("Resolution_Flag") + + internalAnalysisFlag Boolean @map("InternalAnalysis_Flag") + documentFlag Boolean @map("Document_Flag") + + internalMemberFlag Boolean @map("Internal_Member_Flag") + externalSourceFlag Boolean @map("External_Source_Flag") + + dateFormat Int? @map("Date_Format") + dateCreated DateTime @map("Date_Created") @db.DateTime + + createdBy String? @map("Created_By") @db.NVarChar(150) + lastUpdate DateTime @map("Last_Update") @db.DateTime + updatedBy String? @map("Updated_By") @db.NVarChar(150) + + mobileGuid String @map("Mobile_Guid") @db.UniqueIdentifier + memberRecId Int? @map("Member_RecID") + + contactRecId Int? @map("Contact_RecID") + + dbTimestamp Unsupported("timestamp") @map("DB_Timestamp") + dateCreatedUtc DateTime? @map("Date_Created_UTC") @db.DateTime + lastUpdatedUtc DateTime? @map("Last_Update_UTC") @db.DateTime + + mergedFlag Boolean @map("Merged_Flag") + bundledFlag Boolean @map("Bundled_Flag") + + originalAuthor String? @map("Original_Author") @db.NVarChar(150) + issueFlag Boolean @map("Issue_Flag") + + notes String? @map("SR_Detail_Notes") @db.NVarChar(4000) + notesMarkdown String? @map("SR_Detail_Notes_Markdown") @db.NVarChar(Max) + + sentimentScore Decimal? @map("Sentiment_Score") @db.Decimal(9, 2) + createdByParentFlag Boolean @map("Created_By_Parent_Flag") + + srService SrService @relation(fields: [srServiceRecId], references: [srServiceRecId], onDelete: NoAction, onUpdate: NoAction) + member Member? @relation(fields: [memberRecId], references: [memberRecId], onDelete: NoAction, onUpdate: NoAction) + contact Contact? @relation(fields: [contactRecId], references: [contactRecId], onDelete: NoAction, onUpdate: NoAction) + + @@map("SR_Detail") + @@schema("dbo") +} + +model ActivityClass { + activityClassRecId Int @id @map("Activity_Class_RecID") + description String @map("Description") @db.NVarChar(50) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + updatedBy String? @map("Updated_By") @db.NVarChar(15) + inactiveFlag Boolean @map("Inactive_Flag") + xrefWorkCode String? @map("Xref_Work_Code") @db.NVarChar(10) + taxExemptFlag Boolean @map("taxexempt_flag") + hourlyRate Decimal? @map("Hourly_Rate") @db.Decimal(18, 2) + integrationXref String? @map("Integration_Xref") @db.NVarChar(50) + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("Activity_Class") + @@schema("dbo") +} + +model ActivityType { + activityTypeRecId Int @id @map("Activity_Type_RecID") + description String @map("Description") @db.NVarChar(50) + hoursMin Decimal? @map("Hours_Min") @db.Decimal(18, 2) + hoursMax Decimal? @map("Hours_Max") @db.Decimal(18, 2) + defaultFlag Boolean @map("Default_Flag") + multiplierFlag Boolean @map("Multiplier_Flag") + rate Decimal? @map("Rate") @db.Decimal(18, 2) + rateType String? @map("Rate_Type") @db.Char(1) + inactiveFlag Boolean @map("Inactive_Flag") + invoiceFlag Boolean @map("Invoice_Flag") + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + updatedBy String? @map("Updated_By") @db.NVarChar(15) + billableFlag Boolean @map("Billable_Flag") + utilizationFlag Boolean @map("Utilization_Flag") + xrefWorkType String? @map("Xref_Work_Type") @db.NVarChar(10) + costMultiplier Decimal? @map("Cost_Multiplier") @db.Decimal(18, 2) + actDefaultFlag Boolean? @map("Act_Default_Flag") + timeAccrualTypeId String? @map("TimeAccrual_Type_ID") @db.Char(1) + roundBillHrs Decimal? @map("Round_Bill_Hrs") @db.Decimal(18, 2) + integrationXref String? @map("Integration_Xref") @db.NVarChar(50) + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("Activity_Type") + @@schema("dbo") +} + +model SrBoard { + srBoardRecId Int @id @map("SR_Board_RecID") + boardName String? @map("Board_Name") @db.NVarChar(50) + ownerLevelRecId Int @map("Owner_Level_RecID") + billingUnitRecId Int @map("Billing_Unit_RecID") + boardIcon String? @map("Board_Icon") @db.NVarChar(100) + billCompleteFlag Boolean? @map("BillComplete_Flag") + billSrFlag Boolean? @map("BillSR_Flag") + billUnappFlag Boolean? @map("BillUnapp_Flag") + overrideFlag Boolean? @map("Override_Flag") + dispatchRecId Int? @map("Dispatch") + serviceManagerRecId Int? @map("ServiceManager") + dutyManagerRecId Int? @map("DutyManager") + activityClassRecId Int? @map("Activity_Class_RecID") + activityTypeRecId Int? @map("Activity_Type_RecID") + timeBillableFlag Boolean? @map("TimeBillable_Flag") + expBillableFlag Boolean? @map("ExpBillable_Flag") + prodBillableFlag Boolean? @map("ProdBillable_Flag") + timeInvoiceFlag Boolean? @map("TimeInvoice_Flag") + expInvoiceFlag Boolean? @map("ExpInvoice_Flag") + prodInvoiceFlag Boolean? @map("ProdInvoice_Flag") + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + autocloseStatusRecId Int? @map("autoclose_status_recid") + autoAssignNewFlag Boolean? @map("AutoAssignNew_Flag") + autoAssignEcFlag Boolean? @map("AutoAssignEC_Flag") + autoAssignPortalFlag Boolean? @map("AutoAssignPortal_Flag") + srSignoffRecId Int @map("SR_Signoff_RecID") + ecReOpenStatus Int? @map("EC_ReOpen_Status") + projectFlag Boolean @map("Project_Flag") + emailContactFlag Boolean? @map("EmailContact_Flag") + emailResourceFlag Boolean? @map("EmailResource_Flag") + lockDescFlag Boolean? @map("LockDesc_Flag") + srProblemFlag Boolean? @map("SR_Problem_Flag") + additionsLockTimeFlag Boolean? @map("AdditionsLockTime_Flag") + defaultNotifyFrom String? @map("Default_Notify_From") @db.NVarChar(50) + oncallRecId Int? @map("Oncall") + inactiveFlag Boolean? @map("Inactive_Flag") + displayName String? @map("Display_Name") @db.NVarChar(60) + srResolutionFlag Boolean @map("SR_Resolution_Flag") + srInternalAnalysisFlag Boolean @map("SR_InternalAnalysis_Flag") + teProblemFlag Boolean @map("TE_Problem_Flag") + teResolutionFlag Boolean @map("TE_Resolution_Flag") + teInternalAnalysisFlag Boolean @map("TE_InternalAnalysis_Flag") + problemSort String @map("Problem_Sort") @db.Char(1) + resolutionSort String @map("Resolution_Sort") @db.Char(1) + internalAnalysisSort String @map("InternalAnalysis_Sort") @db.Char(1) + emailContactTemplateRecId Int? @map("EmailContact_Template_RecID") + emailResourceTemplateRecId Int? @map("EmailResource_Template_RecID") + ecReOpenResourcesFlag Boolean? @map("EC_ReOpen_Resources_Flag") + ecCompanyMatchFlag Boolean @map("EC_Company_Match_Flag") + ecReOpenDaysFlag Boolean @map("EC_ReOpen_Days_Flag") + ecReOpenDays Int @map("EC_ReOpen_Days") + memberDisplayNameFlag Boolean @map("Member_Display_Name_Flag") + emailCcFlag Boolean @map("EmailCc_Flag") + showDependencies Boolean @map("Show_Dependencies") + showEstimates Boolean @map("Show_Estimates") + autoAssignTicketOwnerFlag Boolean @map("AutoAssignTicketOwner_Flag") + srAllNotesFlag Boolean @map("SR_All_Notes_Flag") + allSort String @map("All_Sort") @db.Char(1) + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + ecAllowReopenClosedFlag Boolean @map("EC_Allow_Reopen_Closed_Flag") + percentageCalculationRecId Int? @map("Percentage_Calculation_RecID") + ecReOpenDaysClosed Int @map("EC_ReOpen_Days_Closed") + markFirstNoteIssueFlag Boolean @map("Mark_First_Note_Issue_Flag") + restrictBoardByDefaultFlag Boolean @map("Restrict_Board_By_Default_Flag") + autoAssignLimitFlag Boolean @map("AutoAssignLimit_Flag") + autoAssignLimitAmount Int @map("AutoAssignLimit_Amount") + sendToBundledFlag Boolean @map("Send_To_Bundled_Flag") + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("SR_Board") + @@schema("dbo") +} + +model SrImpact { + srImpactRecId Int @id @map("SR_Impact_RecID") + impactName String @map("SR_Impact_Name") @db.NVarChar(10) + impactDesc String @map("SR_Impact_Desc") @db.NVarChar(200) + defaultFlag Boolean? @map("Default_Flag") + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + updatedBy String @map("Updated_By") @db.NVarChar(15) + localeKeyRecId Int? @map("Locale_Key_RecID") + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("SR_Impact") + @@schema("dbo") +} + +model SrLocation { + srLocationRecId Int @id @map("SR_Location_RecID") + description String? @map("Description") @db.NVarChar(30) + defaultFlag Boolean @map("Default_Flag") + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + locationId String? @map("Location_ID") @db.Char(1) + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("SR_Location") + @@schema("dbo") +} + +model SrSeverity { + srSeverityRecId Int @id @map("SR_Severity_RecID") + severityName String? @map("SR_Severity_Name") @db.NVarChar(10) + severityDesc String? @map("SR_Severity_Desc") @db.NVarChar(200) + defaultFlag Boolean? @map("Default_Flag") + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + updatedBy String @map("Updated_By") @db.NVarChar(15) + localeKeyRecId Int? @map("Locale_Key_RecID") + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("SR_Severity") + @@schema("dbo") +} + +model SrSource { + srSourceRecId Int @id @map("SR_Source_RecID") + description String? @map("Description") @db.NVarChar(50) + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime? @map("Last_Update") @db.DateTime2 + defaultFlag Boolean @map("Default_Flag") + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("SR_Source") + @@schema("dbo") +} + +model SrStatus { + srStatusRecId Int @id @map("SR_Status_RecID") + ownerLevelRecId Int? @map("Owner_Level_RecID") + billingUnitRecId Int? @map("Billing_Unit_RecID") + description String? @map("Description") @db.NVarChar(50) + defaultFlag Boolean @map("Default_Flag") + sortOrder Int? @map("Sort_Order") @db.SmallInt + boardFlag Boolean @map("Board_Flag") + closedFlag Boolean @map("Closed_Flag") + noTimeFlag Boolean @map("No_Time_Flag") + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + updatedBy String? @map("Updated_By") @db.NVarChar(15) + srNotifyTypeId String? @map("SR_Notify_Type_ID") @db.Char(1) + memberRecId Int? @map("Member_RecID") + workflowStep Int? @map("Workflow_Step") + resolvedFlag Boolean? @map("Resolved_Flag") + noEscalationFlag Boolean? @map("NoEscalation_Flag") + escalationLevel Int? @map("Escalation_Level") @db.SmallInt + srBoardRecId Int? @map("SR_Board_RecID") + customerPortalFlag Boolean? @map("Customer_Portal_Flag") + svSetupHdrRecId Int? @map("SV_SetupHdr_RecID") + inactiveFlag Boolean @map("Inactive_Flag") + longDescription String? @map("Long_Description") @db.NVarChar(500) + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + statusIndicatorRecId Int? @map("Status_Indicator_RecID") + customStatusIndicatorDescription String? @map("Custom_Status_Indicator_Description") @db.NVarChar(30) + roundRobinCatchallFlag Boolean? @map("RoundRobinCatchall_Flag") + saveTimeAsNoteFlag Boolean @map("SaveTimeAsNote_Flag") + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("SR_Status") + @@schema("dbo") +} + +model SrTeam { + srTeamRecId Int @id @map("SR_Team_RecID") + description String? @map("Description") @db.NVarChar(30) + memberRecId Int? @map("Member_RecID") + ownerLevelRecId Int? @map("Owner_Level_RecID") + billingUnitRecId Int? @map("Billing_Unit_RecID") + defaultFlag Boolean @map("Default_Flag") + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + srBoardRecId Int? @map("SR_Board_RecID") + deleteNotifyFlag Boolean? @map("DeleteNotify_Flag") + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + roundRobinFlag Boolean? @map("RoundRobin_Flag") + defaultRoundRobinFlag Boolean? @map("Default_RoundRobin_Flag") + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("SR_Team") + @@schema("dbo") +} + +model SrType { + srTypeRecId Int @id @map("SR_Type_RecID") + ownerLevelRecId Int? @map("Owner_Level_RecID") + billingUnitRecId Int? @map("Billing_Unit_RecID") + description String @map("Description") @db.NVarChar(50) + updatedBy String @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + defaultFlag Boolean? @map("Default_Flag") + activeType String? @map("ActiveType") @db.Char(1) + srBoardRecId Int? @map("SR_Board_RecID") + inactiveFlag Boolean @map("Inactive_Flag") + rfcFlag Boolean? @map("RFC_Flag") + integrationXref String? @map("Integration_Xref") @db.NVarChar(50) + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("SR_Type") + @@schema("dbo") +} + +model SrUrgency { + srUrgencyRecId Int @id @map("SR_Urgency_RecID") + color String? @map("Color") @db.NVarChar(50) + imageLink String? @map("Image_Link") @db.NVarChar(100) + description String? @map("Description") @db.NVarChar(50) + sortOrder Int @map("Sort_Order") + defaultFlag Boolean @map("Default_Flag") + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + responseHours Decimal @map("Response_Hours") @db.Decimal(18, 2) + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + dateCreatedUtc DateTime @map("Date_Created_UTC") @db.DateTime2 + createdBy String? @map("Created_By") @db.NVarChar(15) + localeKeyRecId Int? @map("Locale_Key_RecID") + urgencyLevel String? @map("Urgency_Level") @db.NVarChar(15) + id String @map("Id") @db.UniqueIdentifier + + services SrService[] + + @@map("SR_Urgency") + @@schema("dbo") +} + +model Country { + country String @map("Country") @db.NVarChar(50) + countryRecId Int @id @map("Country_RecID") + currencyId String? @map("Currency_ID") @db.NVarChar(10) + defaultFlag Boolean @map("Default_Flag") + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + updatedBy String? @map("Updated_By") @db.NVarChar(15) + localizationId String? @map("Localization_ID") @db.Char(1) + caption1 String? @map("Caption1") @db.Char(25) + caption2 String? @map("Caption2") @db.Char(25) + caption3 String? @map("Caption3") @db.Char(25) + caption4 String? @map("Caption4") @db.Char(25) + caption5 String? @map("Caption5") @db.Char(25) + value1 String? @map("Value1") @db.Char(50) + value2 String? @map("Value2") @db.Char(50) + value3 String? @map("Value3") @db.Char(50) + value4 String? @map("Value4") @db.Char(50) + value5 String? @map("Value5") @db.Char(50) + cityCaption String? @map("City_Caption") @db.NVarChar(25) + stateCaption String? @map("State_Caption") @db.NVarChar(25) + zipCaption String? @map("Zip_Caption") @db.NVarChar(25) + dialPrefix String? @map("Dial_Prefix") @db.NVarChar(5) + zipMinLength Int @map("Zip_Min_Length") @db.SmallInt + addressFormatRecId Int? @map("Address_Format_RecID") + countryCode String? @map("Country_Code") @db.NVarChar(2) + lastUpdatedUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + countryCodeIsoAlpha2 String? @map("Country_Code_ISO_ALPHA2") @db.NVarChar(2) + id String @map("Id") @db.UniqueIdentifier + disabled Boolean @map("Disabled") + + services SrService[] + + @@map("Country") + @@schema("dbo") +} + // ===================== // OPPORTUNITY RELATED // ===================== @@ -1232,7 +2101,7 @@ model SoActivity { dateTimeStartUtc DateTime? @map("Date_Time_Start_UTC") @db.SmallDateTime dateTimeEndUtc DateTime? @map("Date_Time_End_UTC") @db.SmallDateTime dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime - lastUpdateUtc DateTime @map("Last_Update_UTC") @db.DateTime + lastUpdatedUTC DateTime @map("Last_Update_UTC") @db.DateTime dateClosedUtc DateTime? @map("Date_Closed_UTC") @db.DateTime soActStatusRecId Int @map("so_act_status_recid") currencyRecId Int @map("Currency_RecID") @@ -1255,7 +2124,7 @@ model SoNote { lastUpdate DateTime @map("Last_Update") @db.DateTime2 updatedBy String? @map("Updated_By") @db.NVarChar(15) mobileGuid String @map("Mobile_Guid") @db.UniqueIdentifier - lastUpdateUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + lastUpdatedUTC DateTime @map("Last_Update_UTC") @db.DateTime2 opportunity Opportunity? @relation(fields: [opportunityRecId], references: [opportunityRecId], onDelete: NoAction, onUpdate: NoAction) @@ -1286,14 +2155,30 @@ model SoForecastDtl { recurringFlag Boolean @map("Recurring_Flag") productFlag Boolean @map("Product_Flag") mobileGuid String @map("Mobile_Guid") @db.UniqueIdentifier - lastUpdateUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + lastUpdatedUTC DateTime @map("Last_Update_UTC") @db.DateTime2 - opportunity Opportunity @relation(fields: [opportunityRecId], references: [opportunityRecId], onDelete: NoAction, onUpdate: NoAction) + opportunity Opportunity @relation(fields: [opportunityRecId], references: [opportunityRecId], onDelete: NoAction, onUpdate: NoAction) + recurringRows SoRecurring[] + ivProducts IV_Product[] @@map("SO_Forecast_Dtl") @@schema("dbo") } +model SoRecurring { + soRecurringRecId Int @id @map("SO_Recurring_RecID") + soForecastDtlRecId Int? @map("SO_Forecast_Dtl_RecID") + revenue Decimal? @map("Revenue") @db.Decimal(18, 2) + cost Decimal? @map("Cost") @db.Decimal(18, 2) + periodStart DateTime @map("Period_Start") @db.DateTime + periodEnd DateTime @map("Period_End") @db.DateTime + + forecastItem SoForecastDtl? @relation(fields: [soForecastDtlRecId], references: [soForecastDtlRecId], onDelete: NoAction, onUpdate: NoAction) + + @@map("SO_Recurring") + @@schema("dbo") +} + model SoInfluencer { soInfluencerRecId Int @id @map("SO_Influencer_RecID") opportunityRecId Int? @map("Opportunity_RecID") @@ -1314,3 +2199,202 @@ model SoInfluencer { @@map("SO_Influencers") @@schema("dbo") } + +model OpportunityMember { + opportunityRecId Int @map("Opportunity_RecID") + memberRecId Int @map("Member_RecID") + primarySalesFlag Boolean @map("PrimarySales_Flag") + secondarySalesFlag Boolean @map("SecondarySales_Flag") + individualFlag Boolean @map("Individual_Flag") + teamFlag Boolean @map("Team_Flag") + allowAccessFlag Boolean @map("AllowAccess_Flag") + + opportunity Opportunity @relation("OpportunityMemberToOpportunity", fields: [opportunityRecId], references: [opportunityRecId], onDelete: NoAction, onUpdate: NoAction) + member Member @relation("OpportunityMemberToMember", fields: [memberRecId], references: [memberRecId], onDelete: NoAction, onUpdate: NoAction) + + @@id([opportunityRecId, memberRecId]) + @@map("SO_Opportunity_Member") + @@schema("dbo") +} + +model V_RptOpportunityCustomFields { + customfieldPodId String @map("customfield_podID") @db.NVarChar(50) + opportunityRecId Int @map("Opportunity_RecID") + opportunityName String @map("Opportunity_Name") @db.NVarChar(100) + opportunityCustomfield String @map("opportunity_customfield") @db.NVarChar(25) + opportunityCustomvalue String? @map("opportunity_customvalue") @db.NVarChar(1000) + opportunityFieldtype String? @map("opportunity_fieldtype") @db.NVarChar(20) + updatedBy String @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + timeZone String? @map("Time_Zone") @db.NVarChar(100) + userDefinedFieldRecId Int @map("User_Defined_Field_RecID") + + opportunity Opportunity @relation(fields: [opportunityRecId], references: [opportunityRecId], onDelete: NoAction, onUpdate: NoAction) + + @@id([opportunityRecId, userDefinedFieldRecId]) + @@map("v_rpt_OpportunityCustomFields") + @@schema("dbo") +} + +model V_IV_Product_Custom_Fields { + ivProductRecId Int @id @map("IV_Product_RecID") + procurementNotes String? @map("ProcurementNotes") @db.NVarChar(1000) + productNarrative String? @map("ProductNarrative") @db.NVarChar(1000) + productcatalogMetadata String? @map("productcatalog_metadata") @db.NVarChar(1000) + + ivProduct IV_Product @relation(fields: [ivProductRecId], references: [ivProductRecId], onDelete: NoAction, onUpdate: NoAction) + + @@map("v_IV_Product_Custom_Fields") + @@schema("dbo") +} + +// ===================== +// SCHEDULE +// ===================== + +model ScheduleSpan { + scheduleSpanRecId Int @id @map("Schedule_Span_RecID") @db.SmallInt + scheduleSpanId String? @map("Schedule_Span_ID") @db.NVarChar(1) + spanDesc String? @map("Span_Desc") @db.NVarChar(20) + localeKeyRecId Int? @map("Locale_Key_RecID") + + schedules Schedule[] + + @@map("Schedule_Span") + @@schema("dbo") +} + +model ScheduleStatus { + scheduleStatusRecId Int @id @map("Schedule_Status_RecID") + description String? @map("Description") @db.NVarChar(30) + softFlag Boolean @map("Soft_Flag") + defaultFlag Boolean @map("Default_Flag") + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + color String? @map("Color") @db.NVarChar(25) + lastUpdateUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + id String @map("Id") @db.UniqueIdentifier + + schedules Schedule[] + + @@map("Schedule_Status") + @@schema("dbo") +} + +model ScheduleType { + scheduleTypeRecId Int @id @map("Schedule_Type_RecID") + tableReference String? @map("Table_Reference") @db.NVarChar(50) + description String? @map("Description") @db.NVarChar(50) + displayColor String? @map("Display_Color") @db.NVarChar(30) + moduleId String? @map("Module_ID") @db.Char(2) + systemFlag Boolean @map("System_Flag") + lastUpdate DateTime @map("Last_Update") @db.DateTime2 + updatedBy String? @map("Updated_By") @db.NVarChar(15) + displayFlag Boolean @map("Display_Flag") + xrefMbrTable String? @map("Xref_Mbr_Table") @db.NVarChar(50) + scheduleTypeId String? @map("Schedule_Type_ID") @db.Char(1) + teChargeCodeRecId Int? @map("TE_Charge_Code_RecID") + srLocationRecId Int? @map("SR_Location_RecID") + lastUpdateUtc DateTime @map("Last_Update_UTC") @db.DateTime2 + enteredBy String @map("Entered_By") @db.NVarChar(15) + dateEnteredUtc DateTime @map("Date_Entered_UTC") @db.DateTime2 + id String @map("Id") @db.UniqueIdentifier + + schedules Schedule[] + + @@map("Schedule_Type") + @@schema("dbo") +} + +model Schedule { + scheduleRecId Int @id @map("Schedule_RecID") + recId Int? @map("RecID") + scheduleTypeRecId Int @map("Schedule_Type_RecID") + memberId String? @map("Member_ID") @db.NVarChar(15) + dateTimeStart DateTime? @map("Date_Time_Start") @db.DateTime + dateTimeEnd DateTime? @map("Date_Time_End") @db.DateTime + closeFlag Boolean @map("close_flag") + hoursEstimated Decimal? @map("Hours_Estimated") @db.Decimal(18, 2) + lastUpdate DateTime? @map("Last_Update") @db.DateTime + updatedBy String? @map("Updated_By") @db.NVarChar(15) + syncable Boolean @map("Syncable") + lastSync DateTime? @map("Last_Sync") @db.DateTime + exchangeGuid String? @map("Exchange_GUID") @db.VarChar(4000) + reminderFlag Boolean @map("Reminder_Flag") + reminderMinutes Int? @map("Reminder_Minutes") + allDayFlag Boolean @map("All_Day_Flag") + duration Int? @map("Duration") + enteredByRecId Int? @map("Entered_By_RecID") + xrefMbrRecId Int? @map("Xref_Mbr_RecID") + percentSched Int? @map("Percent_Sched") + hoursSched Decimal? @map("Hours_Sched") @db.Decimal(18, 2) + scheduleStatusRecId Int? @map("Schedule_Status_RecID") + hoursPerDay Decimal? @map("Hours_Per_Day") @db.Decimal(18, 2) + ackFlag Boolean? @map("Ack_Flag") + ackMemberRecId Int? @map("Ack_Member_RecID") + ackDate DateTime? @map("Ack_Date") @db.DateTime + closeMemberRecId Int? @map("Close_Member_RecID") + closeDate DateTime? @map("Close_Date") @db.DateTime + billableFlag Boolean? @map("Billable_Flag") + dateEntered DateTime? @map("Date_Entered") @db.DateTime + mobileGuid String @map("Mobile_Guid") @db.UniqueIdentifier + srLocationRecId Int? @map("SR_Location_RecID") + scheduleSpanRecId Int? @map("Schedule_Span_RecID") + meetingFlag Boolean? @map("Meeting_Flag") + recurringFlag Boolean? @map("Recurring_Flag") + ackDateUtc DateTime? @map("Ack_Date_UTC") @db.DateTime + dateEnteredUtc DateTime? @map("Date_Entered_UTC") @db.DateTime + lastUpdateUtc DateTime? @map("Last_Update_UTC") @db.DateTime + closeDateUtc DateTime? @map("Close_Date_UTC") @db.DateTime + enteredBy String? @map("Entered_By") @db.NVarChar(15) + acknowledgedBy String? @map("Acknowledged_By") @db.NVarChar(15) + closedBy String? @map("Closed_By") @db.NVarChar(15) + dateTimeStartUtc DateTime? @map("Date_Time_Start_UTC") @db.SmallDateTime + dateTimeEndUtc DateTime? @map("Date_Time_End_UTC") @db.SmallDateTime + scheduleDesc String? @map("Schedule_Desc") @db.NVarChar(250) + privateFlag Boolean @map("Private_Flag") + notifyType String? @map("NotifyType") @db.NVarChar(2) + + details ScheduleDetail[] + status ScheduleStatus? @relation(fields: [scheduleStatusRecId], references: [scheduleStatusRecId], onDelete: NoAction, onUpdate: NoAction) + type ScheduleType @relation(fields: [scheduleTypeRecId], references: [scheduleTypeRecId], onDelete: NoAction, onUpdate: NoAction) + span ScheduleSpan? @relation(fields: [scheduleSpanRecId], references: [scheduleSpanRecId], onDelete: NoAction, onUpdate: NoAction) + + @@map("Schedule") + @@schema("dbo") +} + +model ScheduleDetail { + scheduleDetailRecId Int @id @map("Schedule_Detail_RecID") + scheduleRecId Int? @map("Schedule_RecID") + dateScheduled DateTime? @map("Date_Scheduled") @db.DateTime + hoursScheduled Decimal? @map("Hours_Scheduled") @db.Decimal(18, 2) + priority Int? @map("Priority") @db.SmallInt + startTime DateTime? @map("Start_Time") @db.DateTime + endTime DateTime? @map("End_Time") @db.DateTime + memberRecId Int? @map("Member_RecID") + scheduleTypeId String? @map("Schedule_Type_ID") @db.Char(1) + recId Int? @map("RecID") + updatedBy String? @map("Updated_By") @db.NVarChar(15) + lastUpdate DateTime @map("Last_Update") @db.DateTime + timeFlag Boolean? @map("time_flag") + mobileGuid String @map("Mobile_Guid") @db.UniqueIdentifier + exchangeHref String? @map("Exchange_Href") @db.NVarChar(300) + revisionId Int? @map("Revision_ID") + appointmentHash String? @map("Appointment_Hash") @db.NVarChar(100) + dateTimeStartUtc DateTime? @map("Date_Time_Start_UTC") @db.SmallDateTime + dateTimeEndUtc DateTime? @map("Date_Time_End_UTC") @db.SmallDateTime + lastUpdateUtc DateTime @map("Last_Update_UTC") @db.DateTime + privateFlag Boolean @map("Private_Flag") + changeKey String? @map("Change_Key") @db.NVarChar(300) + masterSeriesId String? @map("Master_Series_ID") @db.NVarChar(300) + iCalUid String? @map("ICal_UID") @db.NVarChar(512) + teamsMeetingFlag Boolean @map("Teams_Meeting_Flag") + + schedule Schedule? @relation(fields: [scheduleRecId], references: [scheduleRecId], onDelete: NoAction, onUpdate: NoAction) + + @@map("Schedule_Detail") + @@schema("dbo") +} diff --git a/dalpuri/src/index.ts b/dalpuri/src/index.ts index e69de29..384c122 100644 --- a/dalpuri/src/index.ts +++ b/dalpuri/src/index.ts @@ -0,0 +1,44 @@ +export { companyTranslation } from "./translations/company"; +export { companyAddressTranslation } from "./translations/company-address"; +export { contactTranslation } from "./translations/contact"; +export { opportunityTranslation } from "./translations/opportunity"; +export { opportunityStageTranslation } from "./translations/opportunity-stage"; +export { opportunityTypeTranslation } from "./translations/opportunity-type"; +export { opportunityStatusTranslation } from "./translations/opportunity-status"; +export { catalogItemTranslation } from "./translations/catalog-item"; +export { catalogItemTypeTranslation } from "./translations/catalog-item-type"; +export { catalogCategoryTranslation } from "./translations/catalog-category"; +export { catalogSubcategoryTranslation } from "./translations/catalog-subcategory"; +export { catalogManufacturerTranslation } from "./translations/catalog-manufacturer"; +export { warehouseBinTranslation } from "./translations/warehouse-bin"; +export { productInventoryTranslation } from "./translations/product-inventory"; +export { productDataTranslation } from "./translations/product-data.ts"; +export { corporateLocationTranslation } from "./translations/corporate-location"; +export { internalDepartmentTranslation } from "./translations/internal-department"; +export { cwMemberTranslation } from "./translations/cw-member"; +export { serviceTicketTypeTranslation } from "./translations/service-ticket-type"; +export { serviceTicketBoardTranslation } from "./translations/service-ticket-board"; +export { serviceTicketLocationTranslation } from "./translations/service-ticket-location"; +export { serviceTicketSourceTranslation } from "./translations/service-ticket-source"; +export { serviceTicketImpactTranslation } from "./translations/service-ticket-impact"; +export { serviceTicketSeverityTranslation } from "./translations/service-ticket-severity"; +export { serviceTicketPriorityTranslation } from "./translations/service-ticket-priority"; +export { userTranslation } from "./translations/user"; +export { serviceTicketTranslation } from "./translations/service-ticket"; +export { serviceTicketNoteTranslation } from "./translations/service-ticket-note"; +export { scheduleStatusTranslation } from "./translations/schedule-status"; +export { scheduleTypeTranslation } from "./translations/schedule-type"; +export { scheduleSpanTranslation } from "./translations/schedule-span"; +export { scheduleTranslation } from "./translations/schedule"; +export { taxCodeTranslation } from "./translations/tax-code"; + +// Context type exports +export type { TranslationContext } from "./translations/context"; +export { createTranslationContext } from "./translations/context"; + +// Sync utilities +export { syncTableUpdates, disconnectSyncClients } from "./sync-by-table"; +export { + executeFullDalpuriSync, + executeForcedIncrementalDalpuriSync, +} from "./sync"; diff --git a/dalpuri/src/sync-by-table.ts b/dalpuri/src/sync-by-table.ts new file mode 100644 index 0000000..15d9359 --- /dev/null +++ b/dalpuri/src/sync-by-table.ts @@ -0,0 +1,967 @@ +import { PrismaMssql } from "@prisma/adapter-mssql"; +import { PrismaPg } from "@prisma/adapter-pg"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +import { PrismaClient as CwPrismaClient } from "../generated/prisma/client"; +import { PrismaClient as ApiPrismaClient } from "../../api/generated/prisma/client"; + +import { + catalogCategoryTranslation, + catalogItemTranslation, + catalogItemTypeTranslation, + catalogManufacturerTranslation, + catalogSubcategoryTranslation, + companyAddressTranslation, + companyTranslation, + contactTranslation, + corporateLocationTranslation, + createTranslationContext, + cwMemberTranslation, + internalDepartmentTranslation, + opportunityStatusTranslation, + opportunityTranslation, + opportunityTypeTranslation, + productDataTranslation, + productInventoryTranslation, + serviceTicketBoardTranslation, + serviceTicketImpactTranslation, + serviceTicketLocationTranslation, + serviceTicketNoteTranslation, + serviceTicketPriorityTranslation, + serviceTicketSeverityTranslation, + serviceTicketSourceTranslation, + serviceTicketTranslation, + serviceTicketTypeTranslation, + scheduleStatusTranslation, + scheduleTypeTranslation, + scheduleSpanTranslation, + scheduleTranslation, + userTranslation, + warehouseBinTranslation, + type TranslationContext, +} from "./index"; +import { Translation, SkipRowError } from "./translations/types"; + +type Row = Record; +type AnyTranslation = Translation; + +type SyncTableConfig = { + sourceModel: string; + targetModel: string; + translation: AnyTranslation; + uniqueField: string; + lastUpdatedField: string; + sourceArgs?: Record; +}; + +type SyncResult = { + insertedOrUpdated: number; + skipped: number; + failed: number; +}; + +const parseEnvFile = (path: string): Record => { + const envData = readFileSync(path, "utf8"); + const out: Record = {}; + + for (const rawLine of envData.split(/\r?\n/)) { + const line = rawLine.trim(); + if (!line || line.startsWith("#")) continue; + + const index = line.indexOf("="); + if (index <= 0) continue; + + const key = line.slice(0, index).trim(); + let value = line.slice(index + 1).trim(); + + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + + out[key] = value; + } + + return out; +}; + +const resolveApiDatabaseUrl = (): string => { + if (process.env.API_DATABASE_URL) return process.env.API_DATABASE_URL; + if (process.env.OPTIMA_API_DATABASE_URL) + return process.env.OPTIMA_API_DATABASE_URL; + + const candidates = [ + resolve(import.meta.dir, "../../api/.env"), + resolve(process.cwd(), "../api/.env"), + ]; + + for (const apiEnvPath of candidates) { + try { + const apiEnv = parseEnvFile(apiEnvPath); + if (apiEnv.DATABASE_URL) { + return apiEnv.DATABASE_URL; + } + } catch { + // Try next path candidate. + } + } + + return ""; +}; + +const parseJsonMapFromEnv = (envKey: string): Map => { + const raw = process.env[envKey]; + if (!raw) return new Map(); + + try { + const parsed = JSON.parse(raw) as Record; + const entries = Object.entries(parsed) + .map(([key, value]) => [Number.parseInt(key, 10), value] as const) + .filter( + ([key, value]) => Number.isInteger(key) && typeof value === "string" + ); + + return new Map(entries); + } catch (error) { + console.warn(`Could not parse ${envKey} as JSON map. Ignoring.`, error); + return new Map(); + } +}; + +const normalizeData = (data: Row): Row => { + const normalized: Row = {}; + for (const [key, value] of Object.entries(data)) { + if (value !== undefined) { + normalized[key] = value; + } + } + return normalized; +}; + +const sanitizeUserForeignKeys = ( + data: Row, + context: TranslationContext, + targetModel: string +): Row => { + const sanitized = { ...data }; + + if (targetModel === "serviceTicket" && typeof sanitized.userId === "string") { + const id = context.usersByIdentifier.get(sanitized.userId as string); + sanitized.userId = id || null; + } + + if (targetModel === "opportunity" && typeof sanitized.ownerId === "string") { + const id = context.usersByIdentifier.get(sanitized.ownerId as string); + sanitized.ownerId = id || null; + } + + return sanitized; +}; + +const translateRow = ( + row: Row, + translation: AnyTranslation, + context: TranslationContext +): Row => { + const out: Row = {}; + + for (const entry of translation.values) { + const fromKey = entry.from as string; + const toKey = entry.to as string; + const input = row[fromKey]; + + if (input === undefined) { + continue; + } + + const output = entry.process + ? entry.process(input as never, context, row) + : input; + out[toKey] = output as unknown; + } + + return normalizeData(out); +}; + +const refreshContextFromApi = async ( + apiPrisma: ApiPrismaClient, + context: TranslationContext +): Promise => { + context.userIds.clear(); + context.serviceTicketIds.clear(); + context.opportunityIds.clear(); + context.catalogItemIds.clear(); + context.corporateLocationIds.clear(); + context.usersByMemberRecId.clear(); + context.userIdentifiersByMemberRecId.clear(); + context.usersByIdentifier.clear(); + context.serviceTicketBoardUidsById.clear(); + context.opportunityStatusIds.clear(); + context.scheduleStatusIds.clear(); + context.scheduleTypeIds.clear(); + context.scheduleSpanIds.clear(); + + const [ + users, + boards, + serviceTickets, + opportunities, + catalogItems, + locations, + opportunityStatuses, + scheduleStatuses, + scheduleTypes, + scheduleSpans, + ] = await Promise.all([ + apiPrisma.user.findMany({ + select: { + id: true, + cwMemberId: true, + cwIdentifier: true, + }, + }), + apiPrisma.serviceTicketBoard.findMany({ + select: { + id: true, + uid: true, + }, + }), + apiPrisma.serviceTicket.findMany({ + select: { + id: true, + }, + }), + apiPrisma.opportunity.findMany({ + select: { + id: true, + }, + }), + apiPrisma.catalogItem.findMany({ + select: { + id: true, + }, + }), + apiPrisma.corporateLocation.findMany({ + select: { + id: true, + }, + }), + apiPrisma.opportunityStatus.findMany({ + select: { + id: true, + }, + }), + apiPrisma.scheduleStatus.findMany({ + select: { + id: true, + }, + }), + apiPrisma.scheduleType.findMany({ + select: { + id: true, + }, + }), + apiPrisma.scheduleSpan.findMany({ + select: { + id: true, + }, + }), + ]); + + const defaultOpportunityType = await apiPrisma.opportunityType.findFirst({ + orderBy: { id: "asc" }, + select: { id: true }, + }); + + for (const user of users) { + context.userIds.add(user.id); + + if (user.cwMemberId != null) { + context.usersByMemberRecId.set(user.cwMemberId, user.id); + if (user.cwIdentifier) { + context.userIdentifiersByMemberRecId.set( + user.cwMemberId, + user.cwIdentifier + ); + } + } + + if (user.cwIdentifier) { + context.usersByIdentifier.set(user.cwIdentifier, user.id); + } + } + + for (const board of boards) { + context.serviceTicketBoardUidsById.set(board.id, board.uid); + } + + for (const serviceTicket of serviceTickets) { + context.serviceTicketIds.add(serviceTicket.id); + } + + for (const opportunity of opportunities) { + context.opportunityIds.add(opportunity.id); + } + + for (const catalogItem of catalogItems) { + context.catalogItemIds.add(catalogItem.id); + } + + for (const location of locations) { + context.corporateLocationIds.add(location.id); + } + + for (const status of opportunityStatuses) { + context.opportunityStatusIds.add(status.id); + } + + for (const status of scheduleStatuses) { + context.scheduleStatusIds.add(status.id); + } + + for (const type of scheduleTypes) { + context.scheduleTypeIds.add(type.id); + } + + for (const span of scheduleSpans) { + context.scheduleSpanIds.add(span.id); + } + + context.defaultOpportunityTypeId = defaultOpportunityType?.id ?? null; + + // Optional context fed from env-provided maps. + context.billingTypeByTicketId = parseJsonMapFromEnv( + "BILLING_TYPE_CONTEXT_JSON" + ); + context.billingInstructionsByTicketId = parseJsonMapFromEnv( + "BILLING_INSTRUCTIONS_CONTEXT_JSON" + ); +}; + +const refreshCustomFieldContextFromCw = async ( + cwPrisma: CwPrismaClient, + context: TranslationContext +): Promise => { + context.opportunityNarrativeByOpportunityId.clear(); + context.productCustomByIvProductId.clear(); + + const [productCustomRows, opportunityCustomRows] = await Promise.all([ + cwPrisma.$queryRawUnsafe>>(` + SELECT iv.RecID, iv.ConfigUIDF as fieldName, iv.ConfigValue as fieldValue + FROM dbo.iV_Product iv + `), + cwPrisma.$queryRawUnsafe>>(` + SELECT so.RecID, so.SalesPipelineUIDF as fieldName, so.OpportunitySalesNotes as fieldValue + FROM dbo.opportunity so + `), + ]); + + for (const row of productCustomRows) { + const productId = row.RecID; + const fieldName = row.fieldName; + const fieldValue = row.fieldValue; + if ( + typeof productId === "number" && + typeof fieldName === "string" && + typeof fieldValue === "string" + ) { + if (!context.productCustomByIvProductId.has(productId)) { + context.productCustomByIvProductId.set(productId, new Map()); + } + context.productCustomByIvProductId + .get(productId) + ?.set(fieldName.toLowerCase().trim(), fieldValue.trim()); + } + } + + for (const row of opportunityCustomRows) { + const opportunityId = row.RecID; + const fieldName = row.fieldName; + const fieldValue = row.fieldValue; + if ( + typeof opportunityId === "number" && + typeof fieldName === "string" && + typeof fieldValue === "string" + ) { + if (fieldName.toLowerCase().includes("narrative")) { + context.opportunityNarrativeByOpportunityId.set( + opportunityId, + fieldValue + ); + } + } + } +}; + +const sanitizeModelData = ( + data: Row, + targetModel: string, + context: TranslationContext +): Row => { + const sanitized = { ...data }; + + if (targetModel === "warehouseBin") { + if (sanitized.minQuantity == null) sanitized.minQuantity = 0; + if (sanitized.maxQuantity == null) sanitized.maxQuantity = 0; + } + + if (targetModel === "serviceTicket") { + const boardId = sanitized.serviceTicketBoardId; + if (typeof boardId === "string") { + const parsed = Number.parseInt(boardId, 10); + sanitized.serviceTicketBoardId = Number.isNaN(parsed) ? null : parsed; + } + } + + if (targetModel === "opportunity") { + if (sanitized.typeId == null && context.defaultOpportunityTypeId != null) { + sanitized.typeId = context.defaultOpportunityTypeId; + } + if ( + sanitized.statusId != null && + !context.opportunityStatusIds.has(sanitized.statusId as number) + ) { + sanitized.statusId = null; + } + } + + if (targetModel === "schedule") { + // Nullify statusId if the referenced ScheduleStatus hasn't synced yet + if ( + sanitized.statusId != null && + !context.scheduleStatusIds.has(sanitized.statusId as number) + ) { + sanitized.statusId = null; + } + // Nullify typeId if the referenced ScheduleType hasn't synced yet + if ( + sanitized.typeId != null && + !context.scheduleTypeIds.has(sanitized.typeId as number) + ) { + sanitized.typeId = null; + } + // Nullify scheduleSpanId if the referenced ScheduleSpan hasn't synced yet + if ( + sanitized.scheduleSpanId != null && + !context.scheduleSpanIds.has(sanitized.scheduleSpanId as number) + ) { + sanitized.scheduleSpanId = null; + } + } + + return sanitized; +}; + +const asRecord = (value: unknown): Record | null => { + if (typeof value !== "object" || value == null) { + return null; + } + + return value as Record; +}; + +const parseFieldsFromErrorMessage = (message: string): string[] => { + const fields = new Set(); + + // Handles patterns like (`"cwIdentifier"`) and (`login`) + for (const match of message.matchAll(/`\"([^\"]+)\"`|`([^`]+)`/g)) { + const candidate = (match[1] ?? match[2] ?? "").trim(); + if (!candidate) continue; + + // Skip common non-field tokens that can appear in error messages. + if (candidate === "targetDelegate.upsert()" || candidate === "invocation") { + continue; + } + + fields.add(candidate); + } + + return [...fields]; +}; + +const getUniqueConstraintFields = (error: unknown): string[] => { + const errorRecord = asRecord(error); + if (!errorRecord || errorRecord.code !== "P2002") { + return []; + } + + const meta = asRecord(errorRecord.meta); + if (!meta) { + return []; + } + + const target = meta.target; + if (Array.isArray(target)) { + return target.filter((field): field is string => typeof field === "string"); + } + + if (typeof target === "string") { + return [target]; + } + + if (typeof errorRecord.message === "string") { + return parseFieldsFromErrorMessage(errorRecord.message); + } + + return []; +}; + +const formatValue = (value: unknown): string => { + if (value instanceof Date) { + return value.toISOString(); + } + + if (typeof value === "string") { + return JSON.stringify(value); + } + + if ( + typeof value === "number" || + typeof value === "boolean" || + value == null + ) { + return String(value); + } + + try { + return JSON.stringify(value); + } catch { + return "[unserializable]"; + } +}; + +const formatUniqueConstraintError = ( + error: unknown, + data: Row | null +): string => { + const fields = getUniqueConstraintFields(error); + + if (fields.length === 0) { + return "Unique constraint failed."; + } + + const fieldValues = fields.map((field) => { + const value = data ? data[field] : undefined; + return `${field}=${formatValue(value)}`; + }); + + return `Unique constraint failed on field(s): ${fields.join( + ", " + )}. Values: ${fieldValues.join(", ")}`; +}; + +const getConfigForTable = (table: string): SyncTableConfig | null => { + const configMap: Record = { + member: { + sourceModel: "member", + targetModel: "user", + translation: userTranslation as unknown as AnyTranslation, + uniqueField: "cwMemberId", + lastUpdatedField: "lastUpdatedUtc", + }, + cwMember: { + sourceModel: "member", + targetModel: "cwMember", + translation: cwMemberTranslation as unknown as AnyTranslation, + uniqueField: "cwMemberId", + lastUpdatedField: "lastUpdatedUtc", + }, + company: { + sourceModel: "company", + targetModel: "company", + translation: companyTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdate", + }, + companyAddress: { + sourceModel: "companyAddress", + targetModel: "companyAddress", + translation: companyAddressTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + contact: { + sourceModel: "contact", + targetModel: "contact", + translation: contactTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + ownerLevel: { + sourceModel: "ownerLevel", + targetModel: "corporateLocation", + translation: corporateLocationTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + department: { + sourceModel: "department", + targetModel: "internalDepartment", + translation: internalDepartmentTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUTC", + }, + productType: { + sourceModel: "productType", + targetModel: "catalogItemType", + translation: catalogItemTypeTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + productCategory: { + sourceModel: "productCategory", + targetModel: "catalogCategory", + translation: catalogCategoryTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + productSubcategory: { + sourceModel: "productSubcategory", + targetModel: "catalogSubcategory", + translation: catalogSubcategoryTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + manufacturer: { + sourceModel: "manufacturer", + targetModel: "catalogManufacturer", + translation: catalogManufacturerTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + warehouseBin: { + sourceModel: "warehouseBin", + targetModel: "warehouseBin", + translation: warehouseBinTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + productCatalog: { + sourceModel: "productCatalog", + targetModel: "catalogItem", + translation: catalogItemTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + productInventory: { + sourceModel: "productInventory", + targetModel: "productInventory", + translation: productInventoryTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + srType: { + sourceModel: "srType", + targetModel: "serviceTicketType", + translation: serviceTicketTypeTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + srLocation: { + sourceModel: "srLocation", + targetModel: "serviceTicketLocation", + translation: + serviceTicketLocationTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + srSource: { + sourceModel: "srSource", + targetModel: "serviceTicketSource", + translation: serviceTicketSourceTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + srImpact: { + sourceModel: "srImpact", + targetModel: "serviceTicketImpact", + translation: serviceTicketImpactTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + srSeverity: { + sourceModel: "srSeverity", + targetModel: "serviceTicketSeverity", + translation: + serviceTicketSeverityTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + srUrgency: { + sourceModel: "srUrgency", + targetModel: "serviceTicketPriority", + translation: + serviceTicketPriorityTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + srBoard: { + sourceModel: "srBoard", + targetModel: "serviceTicketBoard", + translation: serviceTicketBoardTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + soType: { + sourceModel: "soType", + targetModel: "opportunityType", + translation: opportunityTypeTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + soOppStatus: { + sourceModel: "soOppStatus", + targetModel: "opportunityStatus", + translation: opportunityStatusTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + opportunity: { + sourceModel: "opportunity", + targetModel: "opportunity", + translation: opportunityTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + sourceArgs: { + include: { + members: { + select: { + memberRecId: true, + primarySalesFlag: true, + secondarySalesFlag: true, + }, + }, + }, + }, + }, + srService: { + sourceModel: "srService", + targetModel: "serviceTicket", + translation: serviceTicketTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + iV_Product: { + sourceModel: "iV_Product", + targetModel: "productData", + translation: productDataTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUTC", + }, + ticketNote: { + sourceModel: "ticketNote", + targetModel: "serviceTicketNote", + translation: serviceTicketNoteTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdatedUtc", + }, + scheduleStatus: { + sourceModel: "scheduleStatus", + targetModel: "scheduleStatus", + translation: scheduleStatusTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdateUtc", + }, + scheduleType: { + sourceModel: "scheduleType", + targetModel: "scheduleType", + translation: scheduleTypeTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdateUtc", + }, + scheduleSpan: { + sourceModel: "scheduleSpan", + targetModel: "scheduleSpan", + translation: scheduleSpanTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "scheduleSpanRecId", + }, + schedule: { + sourceModel: "schedule", + targetModel: "schedule", + translation: scheduleTranslation as unknown as AnyTranslation, + uniqueField: "id", + lastUpdatedField: "lastUpdateUtc", + }, + }; + + return configMap[table] ?? null; +}; + +let _cwPrisma: CwPrismaClient | null = null; +let _apiPrisma: ApiPrismaClient | null = null; + +const getClients = (): { + cwPrisma: CwPrismaClient; + apiPrisma: ApiPrismaClient; +} => { + if (!_cwPrisma || !_apiPrisma) { + const cwDatabaseUrl = + process.env.CW_DATABASE_URL || process.env.DATABASE_URL; + const apiDatabaseUrl = resolveApiDatabaseUrl(); + + if (!cwDatabaseUrl) { + throw new Error( + "Missing CW DB connection string. Set CW_DATABASE_URL or DATABASE_URL." + ); + } + + if (!apiDatabaseUrl) { + throw new Error( + "Missing API DB connection string. Set API_DATABASE_URL/OPTIMA_API_DATABASE_URL or ensure ../api/.env has DATABASE_URL." + ); + } + + _cwPrisma = new CwPrismaClient({ adapter: new PrismaMssql(cwDatabaseUrl) }); + _apiPrisma = new ApiPrismaClient({ + adapter: new PrismaPg({ connectionString: apiDatabaseUrl }), + }); + } + + return { cwPrisma: _cwPrisma, apiPrisma: _apiPrisma }; +}; + +export async function disconnectSyncClients(): Promise { + await Promise.all([_cwPrisma?.$disconnect(), _apiPrisma?.$disconnect()]); + _cwPrisma = null; + _apiPrisma = null; +} + +/** + * Syncs a specific number of records from a CW table to the API database. + * Records are pulled ordered by lastUpdatedUTC in descending order (most recent first). + * + * @param table - The name of the source table (e.g., 'opportunity', 'srService', 'company') + * @param numRec - The number of records to pull and sync + * @returns SyncResult containing counts of inserted/updated, skipped, and failed records + */ +export async function syncTableUpdates( + table: string, + numRec: number +): Promise { + const config = getConfigForTable(table); + if (!config) { + throw new Error( + `Unsupported table: ${table}. No sync configuration found.` + ); + } + + const { cwPrisma, apiPrisma } = getClients(); + const context = createTranslationContext(); + + // Refresh context needed for translations + await refreshContextFromApi(apiPrisma, context); + + if ( + config.targetModel === "opportunity" || + config.targetModel === "productData" + ) { + await refreshCustomFieldContextFromCw(cwPrisma, context); + } + + // Build the source args with lastUpdatedField ordering and take limit + const sourceArgs = { + ...config.sourceArgs, + take: numRec, + orderBy: { + [config.lastUpdatedField]: "desc", + }, + }; + + const sourceDelegate = ( + cwPrisma as unknown as Record + )[config.sourceModel]; + const targetDelegate = ( + apiPrisma as unknown as Record + )[config.targetModel]; + + if (!sourceDelegate) { + throw new Error(`CW delegate not found: ${config.sourceModel}`); + } + + if (!targetDelegate) { + throw new Error(`API delegate not found: ${config.targetModel}`); + } + + // Fetch the records + const rows = (await sourceDelegate.findMany(sourceArgs)) as Row[]; + + let insertedOrUpdated = 0; + let skipped = 0; + let failed = 0; + let sampleErrorsPrinted = 0; + + // Process each row + for (const row of rows) { + let translatedData: Row | null = null; + + try { + const translated = translateRow(row, config.translation, context); + const userSanitized = sanitizeUserForeignKeys( + translated, + context, + config.targetModel + ); + const data = sanitizeModelData( + userSanitized, + config.targetModel, + context + ); + translatedData = data; + const uniqueValue = data[config.uniqueField]; + + if ( + uniqueValue === undefined || + uniqueValue === null || + uniqueValue === "" + ) { + skipped += 1; + continue; + } + + await targetDelegate.upsert({ + where: { + [config.uniqueField]: uniqueValue, + }, + create: data, + update: data, + }); + + insertedOrUpdated += 1; + } catch (error) { + if (error instanceof SkipRowError) { + skipped += 1; + continue; + } + + failed += 1; + if (sampleErrorsPrinted < 5) { + sampleErrorsPrinted += 1; + const uniqueFields = getUniqueConstraintFields(error); + const message = + uniqueFields.length > 0 + ? formatUniqueConstraintError(error, translatedData) + : error instanceof Error + ? error.message + : "Unknown row sync error"; + console.error( + `Failed row in ${config.sourceModel} -> ${config.targetModel}:`, + message + ); + } + } + } + + if (failed > sampleErrorsPrinted) { + console.error( + `${config.sourceModel}: suppressed ${ + failed - sampleErrorsPrinted + } additional row errors` + ); + } + + return { insertedOrUpdated, skipped, failed }; +} diff --git a/dalpuri/src/sync-table-updates.ts b/dalpuri/src/sync-table-updates.ts new file mode 100644 index 0000000..8ce933d --- /dev/null +++ b/dalpuri/src/sync-table-updates.ts @@ -0,0 +1,69 @@ +import { syncTableUpdates } from "./index"; + +/** + * Example usage of syncTableUpdates + * + * Usage from CLI: + * bun run src/sync-table-updates.ts + * + * Example: + * bun run src/sync-table-updates.ts opportunity 100 + * bun run src/sync-table-updates.ts srService 50 + * bun run src/sync-table-updates.ts company 10 + */ + +const main = async () => { + const args = process.argv.slice(2); + + if (args.length < 2) { + console.error("Usage: bun run src/sync-table-updates.ts "); + console.error("\nSupported tables:"); + console.error( + " - member, cwMember, company, companyAddress, contact, ownerLevel" + ); + console.error( + " - department, productType, productCategory, productSubcategory" + ); + console.error( + " - manufacturer, warehouseBin, productCatalog, productInventory" + ); + console.error( + " - srType, srLocation, srSource, srImpact, srSeverity, srUrgency" + ); + console.error( + " - srBoard, soType, soOppStatus, opportunity, srService, iV_Product" + ); + console.error(" - ticketNote"); + console.error("\nExample: bun run src/sync-table-updates.ts opportunity 100"); + process.exit(1); + } + + const table = args[0]; + const numRec = Number.parseInt(args[1], 10); + + if (Number.isNaN(numRec) || numRec <= 0) { + console.error("Error: numRec must be a positive integer"); + process.exit(1); + } + + try { + console.log(`Syncing ${numRec} records from table: ${table}`); + console.log("Pulling records ordered by lastUpdatedUTC (most recent first)"); + console.log(""); + + const result = await syncTableUpdates(table, numRec); + + console.log("\nSync completed!"); + console.log(`Inserted/Updated: ${result.insertedOrUpdated}`); + console.log(`Skipped: ${result.skipped}`); + console.log(`Failed: ${result.failed}`); + console.log( + `Total: ${result.insertedOrUpdated + result.skipped + result.failed}` + ); + } catch (error) { + console.error("Sync failed:", error); + process.exit(1); + } +}; + +main(); diff --git a/dalpuri/src/sync.ts b/dalpuri/src/sync.ts index dd8ec6c..cb89f8f 100644 --- a/dalpuri/src/sync.ts +++ b/dalpuri/src/sync.ts @@ -19,6 +19,7 @@ import { createTranslationContext, cwMemberTranslation, internalDepartmentTranslation, + opportunityStageTranslation, opportunityStatusTranslation, opportunityTranslation, opportunityTypeTranslation, @@ -29,15 +30,20 @@ import { serviceTicketLocationTranslation, serviceTicketNoteTranslation, serviceTicketPriorityTranslation, - serviceTicketServerityTranslation, + serviceTicketSeverityTranslation, serviceTicketSourceTranslation, serviceTicketTranslation, serviceTicketTypeTranslation, + scheduleStatusTranslation, + scheduleTypeTranslation, + scheduleSpanTranslation, + scheduleTranslation, + taxCodeTranslation, userTranslation, warehouseBinTranslation, type TranslationContext, } from "./index"; -import { Translation } from "./translations/types"; +import { Translation, SkipRowError } from "./translations/types"; type Row = Record; type AnyTranslation = Translation; @@ -51,6 +57,9 @@ type Step = { sourceIdField: string; sourceUpdatedField: string; sourceArgs?: Record; + targetDeleteWhere?: Record; + /** API-side field used for timestamp comparison in smart-sync. Defaults to "updatedAt". Set to null for tables without timestamps. */ + targetUpdatedField?: string | null; }; type StepResult = { @@ -59,6 +68,13 @@ type StepResult = { failed: number; }; +type DeleteResult = { + deleted: number; + failed: number; +}; + +let incrementalDeleteStepIndex = 0; + const parseEnvFile = (path: string): Record => { const envData = readFileSync(path, "utf8"); const out: Record = {}; @@ -173,10 +189,19 @@ const refreshContextFromApi = async ( context.opportunityIds.clear(); context.catalogItemIds.clear(); context.corporateLocationIds.clear(); + context.companyIds.clear(); + context.companyAddressIds.clear(); + context.contactIds.clear(); + context.opportunityStageIds.clear(); context.usersByMemberRecId.clear(); context.userIdentifiersByMemberRecId.clear(); context.usersByIdentifier.clear(); context.serviceTicketBoardUidsById.clear(); + context.opportunityStatusIds.clear(); + context.scheduleStatusIds.clear(); + context.scheduleTypeIds.clear(); + context.scheduleSpanIds.clear(); + context.taxCodeIds.clear(); const [ users, @@ -185,6 +210,15 @@ const refreshContextFromApi = async ( opportunities, catalogItems, locations, + opportunityStatuses, + companies, + companyAddresses, + contacts, + opportunityStages, + scheduleStatuses, + scheduleTypes, + scheduleSpans, + taxCodes, ] = await Promise.all([ apiPrisma.user.findMany({ select: { @@ -219,6 +253,51 @@ const refreshContextFromApi = async ( id: true, }, }), + apiPrisma.opportunityStatus.findMany({ + select: { + id: true, + }, + }), + apiPrisma.company.findMany({ + select: { + id: true, + }, + }), + apiPrisma.companyAddress.findMany({ + select: { + id: true, + }, + }), + apiPrisma.contact.findMany({ + select: { + id: true, + }, + }), + apiPrisma.opportunityStage.findMany({ + select: { + id: true, + }, + }), + apiPrisma.scheduleStatus.findMany({ + select: { + id: true, + }, + }), + apiPrisma.scheduleType.findMany({ + select: { + id: true, + }, + }), + apiPrisma.scheduleSpan.findMany({ + select: { + id: true, + }, + }), + apiPrisma.taxCode.findMany({ + select: { + id: true, + }, + }), ]); const defaultOpportunityType = await apiPrisma.opportunityType.findFirst({ @@ -264,6 +343,42 @@ const refreshContextFromApi = async ( context.corporateLocationIds.add(location.id); } + for (const status of opportunityStatuses) { + context.opportunityStatusIds.add(status.id); + } + + for (const company of companies) { + context.companyIds.add(company.id); + } + + for (const address of companyAddresses) { + context.companyAddressIds.add(address.id); + } + + for (const contact of contacts) { + context.contactIds.add(contact.id); + } + + for (const stage of opportunityStages) { + context.opportunityStageIds.add(stage.id); + } + + for (const status of scheduleStatuses) { + context.scheduleStatusIds.add(status.id); + } + + for (const type of scheduleTypes) { + context.scheduleTypeIds.add(type.id); + } + + for (const span of scheduleSpans) { + context.scheduleSpanIds.add(span.id); + } + + for (const taxCode of taxCodes) { + context.taxCodeIds.add(taxCode.id); + } + context.defaultOpportunityTypeId = defaultOpportunityType?.id ?? null; // Optional context fed from env-provided maps. @@ -411,18 +526,147 @@ const sanitizeModelData = ( if (sanitized.maxQuantity == null) sanitized.maxQuantity = 0; } + if (step.targetModel === "contact") { + // Nullify companyAddressId if the address doesn't exist in the API + if ( + sanitized.companyAddressId != null && + !context.companyAddressIds.has(sanitized.companyAddressId as number) + ) { + sanitized.companyAddressId = null; + } + // Nullify companyId if the company doesn't exist in the API + if ( + sanitized.companyId != null && + !context.companyIds.has(sanitized.companyId as number) + ) { + sanitized.companyId = null; + } + } + + if (step.targetModel === "companyAddress") { + // companyId is required — skip the row if the parent company hasn't synced yet + if ( + sanitized.companyId == null || + !context.companyIds.has(sanitized.companyId as number) + ) { + throw new SkipRowError( + `CompanyAddress companyId=${sanitized.companyId} not found in API — skipping` + ); + } + } + if (step.targetModel === "serviceTicket") { const boardId = sanitized.serviceTicketBoardId; if (typeof boardId === "string") { const parsed = Number.parseInt(boardId, 10); sanitized.serviceTicketBoardId = Number.isNaN(parsed) ? null : parsed; } + // Nullify contactId if the contact doesn't exist in the API + if ( + sanitized.contactId != null && + !context.contactIds.has(sanitized.contactId as number) + ) { + sanitized.contactId = null; + } + // Nullify companyAddressId if the address doesn't exist + if ( + sanitized.companyAddressId != null && + !context.companyAddressIds.has(sanitized.companyAddressId as number) + ) { + sanitized.companyAddressId = null; + } + // Nullify companyId if the company doesn't exist + if ( + sanitized.companyId != null && + !context.companyIds.has(sanitized.companyId as number) + ) { + sanitized.companyId = null; + } + // Nullify billingCompanyId if the company doesn't exist + if ( + sanitized.billingCompanyId != null && + !context.companyIds.has(sanitized.billingCompanyId as number) + ) { + sanitized.billingCompanyId = null; + } + // Nullify billingAddressId if the address doesn't exist + if ( + sanitized.billingAddressId != null && + !context.companyAddressIds.has(sanitized.billingAddressId as number) + ) { + sanitized.billingAddressId = null; + } } if (step.targetModel === "opportunity") { if (sanitized.typeId == null && context.defaultOpportunityTypeId != null) { sanitized.typeId = context.defaultOpportunityTypeId; } + if ( + sanitized.statusId != null && + !context.opportunityStatusIds.has(sanitized.statusId as number) + ) { + sanitized.statusId = null; + } + // Nullify companyId if the company doesn't exist + if ( + sanitized.companyId != null && + !context.companyIds.has(sanitized.companyId as number) + ) { + sanitized.companyId = null; + } + // Nullify contactId if the contact doesn't exist + if ( + sanitized.contactId != null && + !context.contactIds.has(sanitized.contactId as number) + ) { + sanitized.contactId = null; + } + // Nullify siteId (companyAddressId) if the address doesn't exist + if ( + sanitized.siteId != null && + !context.companyAddressIds.has(sanitized.siteId as number) + ) { + sanitized.siteId = null; + } + // Nullify stageId if the stage doesn't exist + if ( + sanitized.stageId != null && + !context.opportunityStageIds.has(sanitized.stageId as number) + ) { + sanitized.stageId = null; + } + // Nullify taxCodeId if the tax code hasn't synced yet + if ( + sanitized.taxCodeId != null && + !context.taxCodeIds.has(sanitized.taxCodeId as number) + ) { + sanitized.taxCodeId = null; + } + } + + if (step.targetModel === "schedule") { + // Nullify statusId if the referenced ScheduleStatus hasn't synced yet + if ( + sanitized.statusId != null && + !context.scheduleStatusIds.has(sanitized.statusId as number) + ) { + sanitized.statusId = null; + } + // Nullify typeId if the referenced ScheduleType hasn't synced yet + if ( + sanitized.typeId != null && + !context.scheduleTypeIds.has(sanitized.typeId as number) + ) { + sanitized.typeId = null; + } + // Nullify scheduleSpanId if the referenced ScheduleSpan hasn't synced yet + if ( + sanitized.scheduleSpanId != null && + !context.scheduleSpanIds.has(sanitized.scheduleSpanId as number) + ) { + sanitized.scheduleSpanId = null; + } } return sanitized; @@ -526,14 +770,212 @@ const formatUniqueConstraintError = ( )}. Values: ${fieldValues.join(", ")}`; }; +const toComparableKey = (value: unknown): string | null => { + if (value == null) { + return null; + } + + if (typeof value === "number") { + if (Number.isNaN(value)) { + return null; + } + return `number:${value}`; + } + + if (typeof value === "string") { + return `string:${value}`; + } + + if (typeof value === "bigint") { + return `bigint:${value.toString()}`; + } + + if (typeof value === "boolean") { + return `boolean:${value}`; + } + + return null; +}; + +const reconcileStepDeletes = async ( + cwPrisma: CwPrismaClient, + apiPrisma: ApiPrismaClient, + step: Step +): Promise => { + const sourceDelegate = ( + cwPrisma as unknown as Record + )[step.sourceModel]; + const targetDelegate = ( + apiPrisma as unknown as Record< + string, + { findMany: Function; deleteMany: Function; delete: Function } + > + )[step.targetModel]; + + if (!sourceDelegate) { + throw new Error(`CW delegate not found: ${step.sourceModel}`); + } + + if (!targetDelegate) { + throw new Error(`API delegate not found: ${step.targetModel}`); + } + + const sourceWhere = + (step.sourceArgs as Record | undefined)?.where ?? + undefined; + + const sourceRows = (await sourceDelegate.findMany({ + ...(sourceWhere ? { where: sourceWhere } : {}), + select: { + [step.sourceIdField]: true, + }, + })) as Row[]; + + const sourceKeys = new Set(); + for (const sourceRow of sourceRows) { + const sourceKey = toComparableKey(sourceRow[step.sourceIdField]); + if (sourceKey) { + sourceKeys.add(sourceKey); + } + } + + const targetRows = (await targetDelegate.findMany({ + ...(step.targetDeleteWhere ? { where: step.targetDeleteWhere } : {}), + select: { + [step.uniqueField]: true, + }, + })) as Row[]; + + const staleValues: Array = []; + + for (const targetRow of targetRows) { + const value = targetRow[step.uniqueField]; + const targetKey = toComparableKey(value); + + if (!targetKey) { + continue; + } + + if (!sourceKeys.has(targetKey)) { + staleValues.push(value as string | number | bigint | boolean); + } + } + + if (staleValues.length === 0) { + return { deleted: 0, failed: 0 }; + } + + const chunkSize = 500; + let deleted = 0; + let failed = 0; + let sampleErrorsPrinted = 0; + + for (let index = 0; index < staleValues.length; index += chunkSize) { + const chunk = staleValues.slice(index, index + chunkSize); + + try { + const deleteManyResult = (await targetDelegate.deleteMany({ + where: { + ...(step.targetDeleteWhere ?? {}), + [step.uniqueField]: { in: chunk }, + }, + })) as unknown; + + const deletedCount = asRecord(deleteManyResult)?.count as + | number + | undefined; + deleted += typeof deletedCount === "number" ? deletedCount : chunk.length; + continue; + } catch { + // Fall back to row-by-row deletes so one FK violation does not block all deletions. + } + + for (const value of chunk) { + try { + await targetDelegate.delete({ + where: { + [step.uniqueField]: value, + }, + }); + + deleted += 1; + } catch (error) { + failed += 1; + if (sampleErrorsPrinted < 5) { + sampleErrorsPrinted += 1; + const message = + error instanceof Error ? error.message : "Unknown delete error"; + console.error( + `Delete reconcile failed in ${step.name} for ${ + step.uniqueField + }=${formatValue(value)}:`, + message + ); + } + } + } + } + + if (failed > sampleErrorsPrinted) { + console.error( + `${step.name}: suppressed ${ + failed - sampleErrorsPrinted + } additional delete errors` + ); + } + + return { deleted, failed }; +}; + type SmartSyncDecision = - | { mode: "full" } - | { mode: "incremental"; sourceIds: number[] }; + | { mode: "full"; differences: SmartSyncDifference[] } + | { + mode: "incremental"; + sourceIds: number[]; + differences: SmartSyncDifference[]; + }; + +type SmartSyncDifference = { + sourceId: number; + reason: "missing-in-api" | "cw-newer"; + cwUpdatedAt: Date; + apiUpdatedAt: Date | null; +}; + +const logAllSmartSyncDifferences = ( + step: Step, + differences: SmartSyncDifference[] +): void => { + if (differences.length === 0) { + console.log( + ` [smart-sync][details] ${step.name}: no differences in sampled records` + ); + return; + } + + console.log( + ` [smart-sync][details] ${step.name}: logging all ${differences.length} differences` + ); + + for (const diff of differences) { + const apiUpdated = diff.apiUpdatedAt + ? diff.apiUpdatedAt.toISOString() + : "null"; + console.log( + ` [diff] sourceModel=${step.sourceModel} targetModel=${ + step.targetModel + } id=${diff.sourceId} reason=${ + diff.reason + } cwUpdatedAt=${diff.cwUpdatedAt.toISOString()} apiUpdatedAt=${apiUpdated}` + ); + } +}; const computeSmartSyncDecision = async ( cwPrisma: CwPrismaClient, apiPrisma: ApiPrismaClient, - step: Step + step: Step, + forceIncremental = false ): Promise => { const cwDelegate = ( cwPrisma as unknown as Record @@ -543,12 +985,32 @@ const computeSmartSyncDecision = async ( )[step.targetModel]; if (!cwDelegate || !apiDelegate) { - return { mode: "full" }; + return { mode: "full", differences: [] }; } const existingWhere = (step.sourceArgs as Record | undefined)?.where ?? {}; + // Count-based check: force a full sync when the two databases have diverged + // significantly in either direction. + // CW >> API → rows are missing from API (old records never in recency window) + // API >> CW → stale rows exist in API (need delete reconciliation via full sync) + if (!forceIncremental) { + const [cwCount, apiCount] = await Promise.all([ + (cwDelegate as unknown as { count: Function }).count({ where: existingWhere }), + (apiDelegate as unknown as { count: Function }).count(), + ]); + const gap = cwCount - apiCount; // positive = API missing rows, negative = API has extra rows + const threshold = Math.max(50, Math.floor(Math.max(cwCount, apiCount) * 0.05)); + if (Math.abs(gap) > threshold) { + const direction = gap > 0 ? "api-missing-rows" : "api-has-stale-rows"; + console.log( + ` [smart-sync] ${step.name}: count gap detected (cw=${cwCount}, api=${apiCount}, gap=${gap}, direction=${direction}) — forcing full sync` + ); + return { mode: "full", differences: [] }; + } + } + // Fetch top 1000 CW records: only id + updatedAt const cwSample = (await cwDelegate.findMany({ select: { @@ -561,16 +1023,22 @@ const computeSmartSyncDecision = async ( })) as Row[]; if (cwSample.length === 0) { - return { mode: "incremental", sourceIds: [] }; + return { mode: "incremental", sourceIds: [], differences: [] }; } const cwIds = cwSample.map((r) => r[step.sourceIdField] as number); + // If the API model has no timestamp field, skip incremental comparison and do a full sync. + const apiUpdatedField = step.targetUpdatedField === undefined ? "updatedAt" : step.targetUpdatedField; + if (apiUpdatedField === null) { + return { mode: "full", differences: [] }; + } + // Fetch the corresponding API records for comparison const apiSample = (await apiDelegate.findMany({ select: { [step.uniqueField]: true, - updatedAt: true, + [apiUpdatedField]: true, }, where: { [step.uniqueField]: { in: cwIds } }, })) as Row[]; @@ -578,10 +1046,11 @@ const computeSmartSyncDecision = async ( const apiMap = new Map(); for (const row of apiSample) { const id = row[step.uniqueField] as number; - apiMap.set(id, (row.updatedAt as Date | null) ?? new Date(0)); + apiMap.set(id, (row[apiUpdatedField] as Date | null) ?? new Date(0)); } let differences = 0; + const differenceRows: SmartSyncDifference[] = []; for (const cwRow of cwSample) { const cwId = cwRow[step.sourceIdField] as number; const cwUpdated = @@ -589,10 +1058,22 @@ const computeSmartSyncDecision = async ( if (!apiMap.has(cwId)) { differences++; + differenceRows.push({ + sourceId: cwId, + reason: "missing-in-api", + cwUpdatedAt: cwUpdated, + apiUpdatedAt: null, + }); } else { const apiUpdated = apiMap.get(cwId)!; if (cwUpdated.getTime() > apiUpdated.getTime()) { differences++; + differenceRows.push({ + sourceId: cwId, + reason: "cw-newer", + cwUpdatedAt: cwUpdated, + apiUpdatedAt: apiUpdated, + }); } } } @@ -601,11 +1082,23 @@ const computeSmartSyncDecision = async ( ` [smart-sync] ${step.name}: ${differences} differences in top 1000` ); - if (differences > 100) { - return { mode: "full" }; + if (forceIncremental) { + return { + mode: "incremental", + sourceIds: differenceRows.map((d) => d.sourceId), + differences: differenceRows, + }; } - return { mode: "incremental", sourceIds: cwIds.slice(0, 100) }; + if (differences > 100) { + return { mode: "full", differences: differenceRows }; + } + + return { + mode: "incremental", + sourceIds: differenceRows.map((d) => d.sourceId), + differences: differenceRows, + }; }; const syncStep = async ( @@ -634,27 +1127,14 @@ const syncStep = async ( return { insertedOrUpdated: 0, skipped: 0, failed: 0 }; } - let findManyArgs: Record = step.sourceArgs ?? {}; - if (sourceIdsFilter !== undefined) { - const existingWhere = - (findManyArgs as Record).where ?? {}; - findManyArgs = { - ...findManyArgs, - where: { - ...(existingWhere as Record), - [step.sourceIdField]: { in: sourceIdsFilter }, - }, - }; - } - - const rows = (await sourceDelegate.findMany(findManyArgs)) as Row[]; + const baseArgs: Record = step.sourceArgs ?? {}; let insertedOrUpdated = 0; let skipped = 0; let failed = 0; let sampleErrorsPrinted = 0; - for (const row of rows) { + const processRow = async (row: Row) => { let translatedData: Row | null = null; try { @@ -674,7 +1154,7 @@ const syncStep = async ( uniqueValue === "" ) { skipped += 1; - continue; + return; } await targetDelegate.upsert({ @@ -687,9 +1167,9 @@ const syncStep = async ( insertedOrUpdated += 1; } catch (error) { - if (error instanceof Error && error.message.startsWith("SKIP_ROW:")) { + if (error instanceof SkipRowError) { skipped += 1; - continue; + return; } failed += 1; @@ -708,6 +1188,40 @@ const syncStep = async ( ); } } + }; + + if (sourceIdsFilter !== undefined) { + // Incremental sync: fetch only the specific IDs + const existingWhere = (baseArgs as Record).where ?? {}; + const findManyArgs = { + ...baseArgs, + where: { + ...(existingWhere as Record), + [step.sourceIdField]: { in: sourceIdsFilter }, + }, + }; + const rows = (await sourceDelegate.findMany(findManyArgs)) as Row[]; + for (const row of rows) { + await processRow(row); + } + } else { + // Full sync: paginate through all records to avoid loading 100K+ rows at once + const FULL_SYNC_PAGE_SIZE = 5000; + let skip = 0; + while (true) { + const pageArgs: Record = { + ...baseArgs, + orderBy: { [step.sourceIdField]: "asc" as const }, + take: FULL_SYNC_PAGE_SIZE, + skip, + }; + const pageRows = (await sourceDelegate.findMany(pageArgs)) as Row[]; + for (const row of pageRows) { + await processRow(row); + } + if (pageRows.length < FULL_SYNC_PAGE_SIZE) break; + skip += FULL_SYNC_PAGE_SIZE; + } } if (failed > sampleErrorsPrinted) { @@ -721,7 +1235,18 @@ const syncStep = async ( return { insertedOrUpdated, skipped, failed }; }; -export const executeFullDalpuriSync = async (): Promise => { +export const executeFullDalpuriSync = async (options?: { + forceIncremental?: boolean; + logAllDifferences?: boolean; + reconcileDeletes?: boolean; + jobRunId?: string; + timeoutMs?: number; +}): Promise => { + const forceIncremental = options?.forceIncremental === true; + const logAllDifferences = options?.logAllDifferences ?? !forceIncremental; + const reconcileDeletes = options?.reconcileDeletes ?? true; + const jobRunId = options?.jobRunId; + const timeoutMs = options?.timeoutMs ?? 30 * 60 * 1000; // 30 minutes default const cwDatabaseUrl = process.env.CW_DATABASE_URL || process.env.DATABASE_URL; const apiDatabaseUrl = resolveApiDatabaseUrl(); @@ -745,6 +1270,63 @@ export const executeFullDalpuriSync = async (): Promise => { const context = createTranslationContext(); + // Helper to update job status in the database + const updateJobStatus = async ( + status: "RUNNING" | "COMPLETED" | "FAILED" | "TIMED_OUT", + errorSummary?: string + ) => { + if (!jobRunId) return; + try { + await apiPrisma.syncJobRun.update({ + where: { id: jobRunId }, + data: { + status, + ...(status === "RUNNING" ? { startedAt: new Date() } : {}), + ...(status === "COMPLETED" || status === "FAILED" || status === "TIMED_OUT" + ? { completedAt: new Date() } + : {}), + ...(errorSummary ? { errorSummary } : {}), + }, + }); + } catch (err) { + console.error("[sync] Failed to update job status:", err); + } + }; + + // Helper to write a step log + const writeStepLog = async ( + tableName: string, + syncMode: string, + result: StepResult, + deleteResult: DeleteResult, + durationMs: number, + sampleErrors: string[] = [] + ) => { + if (!jobRunId) return; + try { + await apiPrisma.syncStepLog.create({ + data: { + syncJobRunId: jobRunId, + tableName, + syncMode, + recordsProcessed: result.insertedOrUpdated + result.skipped + result.failed, + recordsInserted: result.insertedOrUpdated, + recordsSkipped: result.skipped, + recordsFailed: result.failed, + recordsDeleted: deleteResult.deleted, + sampleErrors: sampleErrors, + durationMs, + }, + }); + } catch (err) { + console.error("[sync] Failed to write step log:", err); + } + }; + + // Timeout tracking + const syncStartTime = Date.now(); + const isTimedOut = () => Date.now() - syncStartTime > timeoutMs; + const steps: Step[] = [ { name: "Users", @@ -754,10 +1336,9 @@ export const executeFullDalpuriSync = async (): Promise => { uniqueField: "cwMemberId", sourceIdField: "memberRecId", sourceUpdatedField: "lastUpdatedUtc", - sourceArgs: { - where: { - memberClass: "F", - NOT: [{ emailAddress: null }, { emailAddress: "" }], + targetDeleteWhere: { + cwMemberId: { + not: null, }, }, }, @@ -769,12 +1350,6 @@ export const executeFullDalpuriSync = async (): Promise => { uniqueField: "cwMemberId", sourceIdField: "memberRecId", sourceUpdatedField: "lastUpdatedUtc", - sourceArgs: { - where: { - memberClass: "F", - NOT: [{ emailAddress: null }, { emailAddress: "" }], - }, - }, }, { name: "Companies", @@ -783,7 +1358,7 @@ export const executeFullDalpuriSync = async (): Promise => { translation: companyTranslation as unknown as AnyTranslation, uniqueField: "id", sourceIdField: "companyRecId", - sourceUpdatedField: "lastUpdatedUtc", + sourceUpdatedField: "lastUpdate", }, { name: "Company Addresses", @@ -924,9 +1499,9 @@ export const executeFullDalpuriSync = async (): Promise => { { name: "Service Ticket Severities", sourceModel: "srSeverity", - targetModel: "serviceTicketServerity", + targetModel: "serviceTicketSeverity", translation: - serviceTicketServerityTranslation as unknown as AnyTranslation, + serviceTicketSeverityTranslation as unknown as AnyTranslation, uniqueField: "id", sourceIdField: "srSeverityRecId", sourceUpdatedField: "lastUpdatedUtc", @@ -950,6 +1525,31 @@ export const executeFullDalpuriSync = async (): Promise => { sourceIdField: "srBoardRecId", sourceUpdatedField: "lastUpdatedUtc", }, + { + name: "Tax Codes", + sourceModel: "taxCode", + targetModel: "taxCode", + translation: taxCodeTranslation as unknown as AnyTranslation, + uniqueField: "id", + sourceIdField: "taxCodeRecId", + sourceUpdatedField: "lastUpdatedUtc", + sourceArgs: { + include: { + levels: { + where: { taxXref: { not: null } }, + }, + }, + }, + }, + { + name: "Opportunity Stages", + sourceModel: "soPipeline", + targetModel: "opportunityStage", + translation: opportunityStageTranslation as unknown as AnyTranslation, + uniqueField: "id", + sourceIdField: "soPipelineRecId", + sourceUpdatedField: "lastUpdatedUtc", + }, { name: "Opportunity Types", sourceModel: "soType", @@ -1020,40 +1620,129 @@ export const executeFullDalpuriSync = async (): Promise => { }, }, }, + { + name: "Schedule Statuses", + sourceModel: "scheduleStatus", + targetModel: "scheduleStatus", + translation: scheduleStatusTranslation as unknown as AnyTranslation, + uniqueField: "id", + sourceIdField: "scheduleStatusRecId", + sourceUpdatedField: "lastUpdateUtc", + }, + { + name: "Schedule Types", + sourceModel: "scheduleType", + targetModel: "scheduleType", + translation: scheduleTypeTranslation as unknown as AnyTranslation, + uniqueField: "id", + sourceIdField: "scheduleTypeRecId", + sourceUpdatedField: "lastUpdateUtc", + }, + { + name: "Schedule Spans", + sourceModel: "scheduleSpan", + targetModel: "scheduleSpan", + translation: scheduleSpanTranslation as unknown as AnyTranslation, + uniqueField: "id", + sourceIdField: "scheduleSpanRecId", + sourceUpdatedField: "scheduleSpanRecId", + targetUpdatedField: null, + }, + { + name: "Schedules", + sourceModel: "schedule", + targetModel: "schedule", + translation: scheduleTranslation as unknown as AnyTranslation, + uniqueField: "id", + sourceIdField: "scheduleRecId", + sourceUpdatedField: "lastUpdateUtc", + }, ]; try { - await refreshContextFromApi(apiPrisma, context); - await refreshCustomFieldContextFromCw(cwPrisma, context); + await updateJobStatus("RUNNING"); - const summary: Array<{ step: string; result: StepResult }> = []; + try { + await refreshContextFromApi(apiPrisma, context); + await refreshCustomFieldContextFromCw(cwPrisma, context); + } catch (contextError) { + const errorMsg = contextError instanceof Error ? contextError.message : String(contextError); + console.error("[sync] Context refresh failed — aborting sync:", errorMsg); + await updateJobStatus("FAILED", `Context refresh failed: ${errorMsg}`); + return; + } + + const summary: Array<{ + step: string; + result: StepResult; + deleteResult: DeleteResult; + }> = []; + const summaryByStep = new Map< + string, + { + step: string; + result: StepResult; + deleteResult: DeleteResult; + } + >(); for (const step of steps) { + // Check timeout between steps + if (isTimedOut()) { + console.error(`[sync] Timeout exceeded (${timeoutMs}ms) — aborting at step ${step.name}`); + await updateJobStatus("TIMED_OUT", `Timed out at step: ${step.name}`); + return; + } + // Refresh context before tables that depend heavily on cross-table lookups. if ( step.targetModel === "user" || + step.targetModel === "contact" || step.targetModel === "serviceTicketBoard" || step.targetModel === "serviceTicket" || step.targetModel === "opportunity" || step.targetModel === "productData" || - step.targetModel === "serviceTicketNote" + step.targetModel === "serviceTicketNote" || + step.targetModel === "schedule" ) { - await refreshContextFromApi(apiPrisma, context); - if ( - step.targetModel === "opportunity" || - step.targetModel === "productData" - ) { - await refreshCustomFieldContextFromCw(cwPrisma, context); + try { + await refreshContextFromApi(apiPrisma, context); + if ( + step.targetModel === "opportunity" || + step.targetModel === "productData" + ) { + await refreshCustomFieldContextFromCw(cwPrisma, context); + } + } catch (contextError) { + const errorMsg = contextError instanceof Error ? contextError.message : String(contextError); + console.error(`[sync] Context refresh failed before ${step.name} — aborting:`, errorMsg); + await updateJobStatus("FAILED", `Context refresh failed before ${step.name}: ${errorMsg}`); + return; } } + const stepStart = Date.now(); console.log(`Starting ${step.name}...`); - const decision = await computeSmartSyncDecision(cwPrisma, apiPrisma, step); + const decision = await computeSmartSyncDecision( + cwPrisma, + apiPrisma, + step, + forceIncremental + ); const sourceIdsFilter = decision.mode === "incremental" ? decision.sourceIds : undefined; console.log( - ` [smart-sync] mode=${decision.mode}${decision.mode === "incremental" ? ` (${decision.sourceIds.length} ids)` : ""}` + ` [smart-sync]${forceIncremental ? "[forced]" : ""} mode=${ + decision.mode + }${ + decision.mode === "incremental" + ? ` (${decision.sourceIds.length} ids)` + : "" + }` ); + if (logAllDifferences) { + logAllSmartSyncDifferences(step, decision.differences); + } const result = await syncStep( cwPrisma, apiPrisma, @@ -1061,24 +1750,115 @@ export const executeFullDalpuriSync = async (): Promise => { step, sourceIdsFilter ); - summary.push({ step: step.name, result }); + const summaryItem = { + step: step.name, + result, + deleteResult: { deleted: 0, failed: 0 }, + }; + summary.push(summaryItem); + summaryByStep.set(step.name, summaryItem); console.log( `${step.name}: upserted=${result.insertedOrUpdated} skipped=${result.skipped} failed=${result.failed}` ); + + await writeStepLog( + step.name, + decision.mode, + result, + { deleted: 0, failed: 0 }, + Date.now() - stepStart + ); + } + + if (reconcileDeletes) { + // Check timeout before delete reconciliation + if (isTimedOut()) { + console.error(`[sync] Timeout exceeded — skipping delete reconciliation`); + await updateJobStatus("TIMED_OUT", "Timed out before delete reconciliation"); + return; + } + + const deleteSteps = forceIncremental + ? [steps[incrementalDeleteStepIndex % steps.length]] + : [...steps].reverse(); + + if (forceIncremental) { + const selected = deleteSteps[0]; + console.log( + `[delete-reconcile] incremental sweep: ${selected.name} (${ + incrementalDeleteStepIndex + 1 + }/${steps.length})` + ); + incrementalDeleteStepIndex = + (incrementalDeleteStepIndex + 1) % steps.length; + } + + for (const step of deleteSteps) { + if (isTimedOut()) { + console.error(`[sync] Timeout exceeded during delete reconciliation at ${step.name}`); + await updateJobStatus("TIMED_OUT", `Timed out during delete reconciliation: ${step.name}`); + return; + } + + console.log(`Reconciling deletes for ${step.name}...`); + const deleteResult = await reconcileStepDeletes( + cwPrisma, + apiPrisma, + step + ); + const summaryItem = summaryByStep.get(step.name); + if (summaryItem) { + summaryItem.deleteResult = deleteResult; + } + console.log( + `${step.name}: deleted=${deleteResult.deleted} deleteFailed=${deleteResult.failed}` + ); + } } console.log("\nSync complete.\n"); for (const item of summary) { console.log( - `${item.step}: upserted=${item.result.insertedOrUpdated}, skipped=${item.result.skipped}, failed=${item.result.failed}` + `${item.step}: upserted=${item.result.insertedOrUpdated}, skipped=${item.result.skipped}, failed=${item.result.failed}, deleted=${item.deleteResult.deleted}, deleteFailed=${item.deleteResult.failed}` ); } + + await updateJobStatus("COMPLETED"); + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error); + console.error("[sync] Unhandled sync error:", errorMsg); + // Try to write the error to the job run + if (jobRunId) { + try { + await apiPrisma.syncJobRun.update({ + where: { id: jobRunId }, + data: { + status: "FAILED", + completedAt: new Date(), + errorSummary: errorMsg.slice(0, 2000), + }, + }); + } catch { + // Best-effort status update + } + } + throw error; } finally { await cwPrisma.$disconnect(); await apiPrisma.$disconnect(); } }; +export const executeForcedIncrementalDalpuriSync = async (options?: { + jobRunId?: string; +}): Promise => { + return executeFullDalpuriSync({ + forceIncremental: true, + logAllDifferences: false, + jobRunId: options?.jobRunId, + }); +}; + if (import.meta.main) { executeFullDalpuriSync().catch((error) => { console.error("CW -> API sync failed:", error); diff --git a/dalpuri/src/translations/__tests__/translators.test.ts b/dalpuri/src/translations/__tests__/translators.test.ts new file mode 100644 index 0000000..abd63d5 --- /dev/null +++ b/dalpuri/src/translations/__tests__/translators.test.ts @@ -0,0 +1,609 @@ +import { describe, expect, test } from "bun:test"; +import { SkipRowError } from "../types"; +import { createTranslationContext, type TranslationContext } from "../context"; + +// ─── Test‑only translator helper ────────────────────────────────────────── +// We cannot import Translation generically, but every translator +// exposes a `.values` array. We walk the entries exactly the way the real +// `translateRow` function does. + +type AnyTranslation = { + values: Array<{ + from: string; + to: string; + process?: (value: any, context: any, row: any) => any; + }>; +}; + +function translateRow( + row: Record, + translation: AnyTranslation, + context: TranslationContext +): Record { + const out: Record = {}; + for (const entry of translation.values) { + const input = row[entry.from]; + if (input === undefined) continue; + out[entry.to] = entry.process + ? entry.process(input, context, row) + : input; + } + return out; +} + +function expectSkipRow(fn: () => void): string { + try { + fn(); + throw new Error("Expected SkipRowError but nothing was thrown"); + } catch (err) { + expect(err).toBeInstanceOf(SkipRowError); + return (err as SkipRowError).message; + } +} + +// ─── TranslationContext factory ─────────────────────────────────────────── +function makeContext(overrides?: Partial): TranslationContext { + const ctx = createTranslationContext(); + if (overrides) Object.assign(ctx, overrides); + return ctx; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// User Translator +// ═══════════════════════════════════════════════════════════════════════════ +describe("userTranslation", () => { + // Lazy‑load to sidestep top‑level Prisma import issues in test env. + let userTranslation: AnyTranslation; + + test("module loads", async () => { + const mod = await import("../user"); + userTranslation = mod.userTranslation as unknown as AnyTranslation; + expect(userTranslation).toBeDefined(); + }); + + test("valid email is normalised", async () => { + const mod = await import("../user"); + userTranslation = mod.userTranslation as unknown as AnyTranslation; + const ctx = makeContext(); + const row = { + emailAddress: " John@Example.COM ", + firstName: "John", + lastName: "Doe", + memberRecId: 42, + memberId: "jdoe", + memberClass: "F", + inactiveFlag: false, + dateHire: new Date("2024-01-01"), + lastUpdatedUtc: new Date("2024-06-01"), + }; + const result = translateRow(row, userTranslation, ctx); + expect(result.login).toBe("jdoe@cw.local"); + expect(result.email).toBe("john@example.com"); + expect(result.firstName).toBe("John"); + expect(result.active).toBe(true); + expect(result.cwMemberId).toBe(42); + expect(result.cwIdentifier).toBe("jdoe"); + }); + + test("missing email generates placeholder", async () => { + const mod = await import("../user"); + userTranslation = mod.userTranslation as unknown as AnyTranslation; + const ctx = makeContext(); + const result = translateRow( + { emailAddress: null, firstName: "A", lastName: "B", memberRecId: 1, memberId: "a", memberClass: "F", inactiveFlag: false, lastUpdatedUtc: new Date(), dateHire: null }, + userTranslation, + ctx + ); + expect(result.email).toBe("a@cw.local"); + }); + + test("email without @ generates placeholder", async () => { + const mod = await import("../user"); + userTranslation = mod.userTranslation as unknown as AnyTranslation; + const ctx = makeContext(); + const result = translateRow( + { emailAddress: "nodomain", firstName: "A", lastName: "B", memberRecId: 1, memberId: "a", memberClass: "F", inactiveFlag: false, lastUpdatedUtc: new Date(), dateHire: null }, + userTranslation, + ctx + ); + expect(result.email).toBe("a@cw.local"); + }); + + test("empty firstName falls back to Unknown", async () => { + const mod = await import("../user"); + userTranslation = mod.userTranslation as unknown as AnyTranslation; + const ctx = makeContext(); + const result = translateRow( + { emailAddress: "x@y.com", firstName: "", lastName: "", memberRecId: 1, memberId: "x", inactiveFlag: false, lastUpdatedUtc: new Date(), dateHire: null }, + userTranslation, + ctx + ); + expect(result.firstName).toBe("Unknown"); + expect(result.lastName).toBe("Unknown"); + }); + + test("inactiveFlag=true maps to active=false", async () => { + const mod = await import("../user"); + userTranslation = mod.userTranslation as unknown as AnyTranslation; + const ctx = makeContext(); + const result = translateRow( + { emailAddress: "x@y.com", firstName: "X", lastName: "Y", memberRecId: 1, memberId: "x", inactiveFlag: true, lastUpdatedUtc: new Date(), dateHire: null }, + userTranslation, + ctx + ); + expect(result.active).toBe(false); + }); + + test("createdAt falls back to lastUpdatedUtc when dateHire is null", async () => { + const mod = await import("../user"); + userTranslation = mod.userTranslation as unknown as AnyTranslation; + const ctx = makeContext(); + const updated = new Date("2025-03-01"); + const result = translateRow( + { emailAddress: "x@y.com", firstName: "X", lastName: "Y", memberRecId: 1, memberId: "x", inactiveFlag: false, lastUpdatedUtc: updated, dateHire: null }, + userTranslation, + ctx + ); + expect(result.createdAt).toEqual(updated); + }); +}); + +// ═══════════════════════════════════════════════════════════════════════════ +// Opportunity Translator +// ═══════════════════════════════════════════════════════════════════════════ +describe("opportunityTranslation", () => { + let opportunityTranslation: AnyTranslation; + + test("module loads", async () => { + const mod = await import("../opportunity"); + opportunityTranslation = mod.opportunityTranslation as unknown as AnyTranslation; + expect(opportunityTranslation).toBeDefined(); + }); + + test("interest maps 1->COLD, 2->WARM, 3->HOT", async () => { + const mod = await import("../opportunity"); + opportunityTranslation = mod.opportunityTranslation as unknown as AnyTranslation; + const ctx = makeContext({ defaultOpportunityTypeId: 1 }); + + for (const [input, expected] of [ + [1, "COLD"], + [2, "WARM"], + [3, "HOT"], + ] as const) { + const result = translateRow( + { opportunityRecId: 100, soInterestRecId: input, soTypeRecId: 1, opportunityName: "T", notes: null, soPipelineRecId: null, soOppStatusRecId: null, probabilityToClose: 50, source: null, companyRecId: 1, contactRecId: 1, companyAddressRecId: null, poNumber: null, dateCloseExpected: null, datePipelineChange: null, dateBecameLead: null, dateClosed: null, oldCloseFlag: false, closedBy: null, updatedBy: "admin", enteredBy: "admin", dateBecameLeadUtc: new Date(), lastUpdatedUtc: new Date(), members: [] }, + opportunityTranslation, + ctx + ); + expect(result.interest).toBe(expected); + } + }); + + test("interest value 0 maps to COLD", async () => { + const mod = await import("../opportunity"); + opportunityTranslation = mod.opportunityTranslation as unknown as AnyTranslation; + const ctx = makeContext({ defaultOpportunityTypeId: 1 }); + const result = translateRow( + { opportunityRecId: 100, soInterestRecId: 0, soTypeRecId: 1, opportunityName: "T", notes: null, soPipelineRecId: null, soOppStatusRecId: null, probabilityToClose: 0, source: null, companyRecId: 1, contactRecId: 1, companyAddressRecId: null, poNumber: null, dateCloseExpected: null, datePipelineChange: null, dateBecameLead: null, dateClosed: null, oldCloseFlag: false, closedBy: null, updatedBy: "admin", enteredBy: "admin", dateBecameLeadUtc: new Date(), lastUpdatedUtc: new Date(), members: [] }, + opportunityTranslation, + ctx + ); + expect(result.interest).toBe("COLD"); + }); + + test("interest value 4+ maps to HOT", async () => { + const mod = await import("../opportunity"); + opportunityTranslation = mod.opportunityTranslation as unknown as AnyTranslation; + const ctx = makeContext({ defaultOpportunityTypeId: 1 }); + const result = translateRow( + { opportunityRecId: 100, soInterestRecId: 4, soTypeRecId: 1, opportunityName: "T", notes: null, soPipelineRecId: null, soOppStatusRecId: null, probabilityToClose: 0, source: null, companyRecId: 1, contactRecId: 1, companyAddressRecId: null, poNumber: null, dateCloseExpected: null, datePipelineChange: null, dateBecameLead: null, dateClosed: null, oldCloseFlag: false, closedBy: null, updatedBy: "admin", enteredBy: "admin", dateBecameLeadUtc: new Date(), lastUpdatedUtc: new Date(), members: [] }, + opportunityTranslation, + ctx + ); + expect(result.interest).toBe("HOT"); + }); + + test("null interest returns null", async () => { + const mod = await import("../opportunity"); + opportunityTranslation = mod.opportunityTranslation as unknown as AnyTranslation; + const ctx = makeContext({ defaultOpportunityTypeId: 1 }); + const result = translateRow( + { opportunityRecId: 100, soInterestRecId: null, soTypeRecId: 1, opportunityName: "T", notes: null, soPipelineRecId: null, soOppStatusRecId: null, probabilityToClose: 0, source: null, companyRecId: 1, contactRecId: 1, companyAddressRecId: null, poNumber: null, dateCloseExpected: null, datePipelineChange: null, dateBecameLead: null, dateClosed: null, oldCloseFlag: false, closedBy: null, updatedBy: "admin", enteredBy: "admin", dateBecameLeadUtc: new Date(), lastUpdatedUtc: new Date(), members: [] }, + opportunityTranslation, + ctx + ); + expect(result.interest).toBeNull(); + }); + + test("missing typeId with default fallback succeeds", async () => { + const mod = await import("../opportunity"); + opportunityTranslation = mod.opportunityTranslation as unknown as AnyTranslation; + const ctx = makeContext({ defaultOpportunityTypeId: 99 }); + const result = translateRow( + { opportunityRecId: 100, soInterestRecId: null, soTypeRecId: null, opportunityName: "T", notes: null, soPipelineRecId: null, soOppStatusRecId: null, probabilityToClose: 0, source: null, companyRecId: 1, contactRecId: 1, companyAddressRecId: null, poNumber: null, dateCloseExpected: null, datePipelineChange: null, dateBecameLead: null, dateClosed: null, oldCloseFlag: false, closedBy: null, updatedBy: "admin", enteredBy: "admin", dateBecameLeadUtc: new Date(), lastUpdatedUtc: new Date(), members: [] }, + opportunityTranslation, + ctx + ); + expect(result.typeId).toBe(99); + }); + + test("missing typeId with no default triggers skipRow", async () => { + const mod = await import("../opportunity"); + opportunityTranslation = mod.opportunityTranslation as unknown as AnyTranslation; + const ctx = makeContext({ defaultOpportunityTypeId: null }); + expectSkipRow(() => { + translateRow( + { opportunityRecId: 100, soInterestRecId: null, soTypeRecId: null, opportunityName: "T", notes: null, soPipelineRecId: null, soOppStatusRecId: null, probabilityToClose: 0, source: null, companyRecId: 1, contactRecId: 1, companyAddressRecId: null, poNumber: null, dateCloseExpected: null, datePipelineChange: null, dateBecameLead: null, dateClosed: null, oldCloseFlag: false, closedBy: null, updatedBy: "admin", enteredBy: "admin", dateBecameLeadUtc: new Date(), lastUpdatedUtc: new Date(), members: [] }, + opportunityTranslation, + ctx + ); + }); + }); + + test("primary/secondary sales reps resolved from members", async () => { + const mod = await import("../opportunity"); + opportunityTranslation = mod.opportunityTranslation as unknown as AnyTranslation; + const identMap = new Map([[10, "jdoe"], [20, "jsmith"]]); + const ctx = makeContext({ + defaultOpportunityTypeId: 1, + userIdentifiersByMemberRecId: identMap, + }); + const result = translateRow( + { opportunityRecId: 100, soInterestRecId: null, soTypeRecId: 1, opportunityName: "T", notes: null, soPipelineRecId: null, soOppStatusRecId: null, probabilityToClose: 0, source: null, companyRecId: 1, contactRecId: 1, companyAddressRecId: null, poNumber: null, dateCloseExpected: null, datePipelineChange: null, dateBecameLead: null, dateClosed: null, oldCloseFlag: false, closedBy: null, updatedBy: "admin", enteredBy: "admin", dateBecameLeadUtc: new Date(), lastUpdatedUtc: new Date(), members: [{ memberRecId: 10, primarySalesFlag: true, secondarySalesFlag: false }, { memberRecId: 20, primarySalesFlag: false, secondarySalesFlag: true }] }, + opportunityTranslation, + ctx + ); + expect(result.primarySalesRepId).toBe("jdoe"); + expect(result.secondarySalesRepId).toBe("jsmith"); + }); +}); + +// ═══════════════════════════════════════════════════════════════════════════ +// Service Ticket Translator +// ═══════════════════════════════════════════════════════════════════════════ +describe("serviceTicketTranslation", () => { + let serviceTicketTranslation: AnyTranslation; + + test("module loads", async () => { + const mod = await import("../service-ticket"); + serviceTicketTranslation = mod.serviceTicketTranslation as unknown as AnyTranslation; + expect(serviceTicketTranslation).toBeDefined(); + }); + + test("valid row translates correctly", async () => { + const mod = await import("../service-ticket"); + serviceTicketTranslation = mod.serviceTicketTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + usersByMemberRecId: new Map([[5, "user-abc"]]), + usersByIdentifier: new Map([["admin", "user-admin"]]), + billingTypeByTicketId: new Map([[42, "Standard"]]), + billingInstructionsByTicketId: new Map([[42, "Bill monthly"]]), + }); + const result = translateRow( + { + srServiceRecId: 42, summary: "Server is down", srLocationRecId: 1, srSourceRecId: 2, + srUrgencyRecId: 3, srSeverityRecId: 4, srImpactRecId: 5, srBoardRecId: 10, + companyRecId: 100, contactRecId: 200, companyAddressRecId: 300, + billingCompanyRecId: null, billingAddressRecId: null, + ticketOwnerRecId: 5, enteredBy: "admin", updatedBy: "admin", closedBy: null, + dateEnteredUtc: new Date("2025-01-01"), lastUpdatedUtc: new Date("2025-06-01"), + dateClosedUtc: null, isClosedFlag: false, billMethod: "A", + poNumber: "PO123", billingAmount: 500, rejectedFlag: false, publishFlag: true, redFlag: false, + }, + serviceTicketTranslation, + ctx + ); + expect(result.id).toBe(42); + expect(result.summary).toBe("Server is down"); + expect(result.severityId).toBe(4); + expect(result.ticketOwnerId).toBe("user-abc"); + expect(result.createdById).toBe("user-admin"); + expect(result.billingMethod).toBe("ACTUAL_RATES"); + expect(result.billingType).toBe("STANDARD"); + expect(result.billingInstructions).toBe("Bill monthly"); + }); + + test("empty summary triggers skipRow", async () => { + const mod = await import("../service-ticket"); + serviceTicketTranslation = mod.serviceTicketTranslation as unknown as AnyTranslation; + const ctx = makeContext(); + expectSkipRow(() => { + translateRow( + { srServiceRecId: 1, summary: "", srLocationRecId: 1, srSourceRecId: 1, srSeverityRecId: 1, srImpactRecId: 1, srBoardRecId: null, companyRecId: null, contactRecId: null, companyAddressRecId: null, billingCompanyRecId: null, billingAddressRecId: null, ticketOwnerRecId: null, enteredBy: null, updatedBy: null, closedBy: null, dateEnteredUtc: null, lastUpdatedUtc: null, dateClosedUtc: null, isClosedFlag: false, billMethod: null, srUrgencyRecId: null, poNumber: null, billingAmount: null, rejectedFlag: false, publishFlag: false, redFlag: false }, + serviceTicketTranslation, + ctx + ); + }); + }); + + test("null locationId triggers skipRow", async () => { + const mod = await import("../service-ticket"); + serviceTicketTranslation = mod.serviceTicketTranslation as unknown as AnyTranslation; + const ctx = makeContext(); + expectSkipRow(() => { + translateRow( + { srServiceRecId: 1, summary: "problem", srLocationRecId: null, srSourceRecId: 1, srSeverityRecId: 1, srImpactRecId: 1, srBoardRecId: null, companyRecId: null, contactRecId: null, companyAddressRecId: null, billingCompanyRecId: null, billingAddressRecId: null, ticketOwnerRecId: null, enteredBy: null, updatedBy: null, closedBy: null, dateEnteredUtc: null, lastUpdatedUtc: null, dateClosedUtc: null, isClosedFlag: false, billMethod: null, srUrgencyRecId: null, poNumber: null, billingAmount: null, rejectedFlag: false, publishFlag: false, redFlag: false }, + serviceTicketTranslation, + ctx + ); + }); + }); + + test("null dates return null instead of epoch", async () => { + const mod = await import("../service-ticket"); + serviceTicketTranslation = mod.serviceTicketTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + billingTypeByTicketId: new Map(), + billingInstructionsByTicketId: new Map(), + }); + const result = translateRow( + { + srServiceRecId: 1, summary: "test", srLocationRecId: 1, srSourceRecId: 1, + srSeverityRecId: 1, srImpactRecId: 1, srBoardRecId: null, + companyRecId: null, contactRecId: null, companyAddressRecId: null, + billingCompanyRecId: null, billingAddressRecId: null, + ticketOwnerRecId: null, enteredBy: null, updatedBy: null, closedBy: null, + dateEnteredUtc: null, lastUpdatedUtc: null, dateClosedUtc: null, + isClosedFlag: false, billMethod: null, srUrgencyRecId: null, + poNumber: null, billingAmount: null, rejectedFlag: false, publishFlag: false, redFlag: false, + }, + serviceTicketTranslation, + ctx + ); + expect(result.createdAt).toBeNull(); + expect(result.updatedAt).toBeNull(); + }); + + test("billing method maps correctly", async () => { + const mod = await import("../service-ticket"); + serviceTicketTranslation = mod.serviceTicketTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + billingTypeByTicketId: new Map(), + billingInstructionsByTicketId: new Map(), + }); + const base = { + srServiceRecId: 1, summary: "test", srLocationRecId: 1, srSourceRecId: 1, + srSeverityRecId: 1, srImpactRecId: 1, srBoardRecId: null, + companyRecId: null, contactRecId: null, companyAddressRecId: null, + billingCompanyRecId: null, billingAddressRecId: null, + ticketOwnerRecId: null, enteredBy: null, updatedBy: null, closedBy: null, + dateEnteredUtc: new Date(), lastUpdatedUtc: new Date(), dateClosedUtc: null, + isClosedFlag: false, srUrgencyRecId: null, + poNumber: null, billingAmount: 0, rejectedFlag: false, publishFlag: false, redFlag: false, + }; + + for (const [input, expected] of [ + ["A", "ACTUAL_RATES"], + ["F", "FIXED_FEE"], + ["N", "NOT_TO_EXCEED"], + ["O", "OVERRIDE_RATE"], + [null, "ACTUAL_RATES"], + ["UNKNOWN", "ACTUAL_RATES"], + ] as const) { + const result = translateRow({ ...base, billMethod: input }, serviceTicketTranslation, ctx); + expect(result.billingMethod).toBe(expected); + } + }); + + test("missing billingType defaults to STANDARD", async () => { + const mod = await import("../service-ticket"); + serviceTicketTranslation = mod.serviceTicketTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + billingTypeByTicketId: new Map(), // No entry for ticket 999 + billingInstructionsByTicketId: new Map(), + }); + const result = translateRow( + { + srServiceRecId: 999, summary: "test", srLocationRecId: 1, srSourceRecId: 1, + srSeverityRecId: 1, srImpactRecId: 1, srBoardRecId: null, + companyRecId: null, contactRecId: null, companyAddressRecId: null, + billingCompanyRecId: null, billingAddressRecId: null, + ticketOwnerRecId: null, enteredBy: null, updatedBy: null, closedBy: null, + dateEnteredUtc: new Date(), lastUpdatedUtc: new Date(), dateClosedUtc: null, + isClosedFlag: false, billMethod: null, srUrgencyRecId: null, + poNumber: null, billingAmount: 0, rejectedFlag: false, publishFlag: false, redFlag: false, + }, + serviceTicketTranslation, + ctx + ); + expect(result.billingType).toBe("STANDARD"); + }); +}); + +// ═══════════════════════════════════════════════════════════════════════════ +// Service Ticket Note Translator +// ═══════════════════════════════════════════════════════════════════════════ +describe("serviceTicketNoteTranslation", () => { + let serviceTicketNoteTranslation: AnyTranslation; + + test("module loads", async () => { + const mod = await import("../service-ticket-note"); + serviceTicketNoteTranslation = mod.serviceTicketNoteTranslation as unknown as AnyTranslation; + expect(serviceTicketNoteTranslation).toBeDefined(); + }); + + test("valid note translates correctly", async () => { + const mod = await import("../service-ticket-note"); + serviceTicketNoteTranslation = mod.serviceTicketNoteTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + serviceTicketIds: new Set([42]), + usersByMemberRecId: new Map([[10, "user-abc"]]), + }); + const result = translateRow( + { ticketNoteRecId: 1, srServiceRecId: 42, notesMarkdown: "# Title", notes: "Title", memberRecId: 10, problemFlag: true, resolutionFlag: false, internalAnalysisFlag: false, internalMemberFlag: false, mergedFlag: false, bundledFlag: false, createdByParentFlag: false, dateCreatedUtc: new Date("2025-01-01"), lastUpdatedUtc: new Date("2025-06-01") }, + serviceTicketNoteTranslation, + ctx + ); + expect(result.serviceTicketId).toBe(42); + expect(result.authorId).toBe("user-abc"); + expect(result.notesMd).toBe("# Title"); + }); + + test("missing parent ticket triggers skipRow", async () => { + const mod = await import("../service-ticket-note"); + serviceTicketNoteTranslation = mod.serviceTicketNoteTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + serviceTicketIds: new Set([99]), // 42 NOT in set + usersByMemberRecId: new Map([[10, "user-abc"]]), + }); + expectSkipRow(() => { + translateRow( + { ticketNoteRecId: 1, srServiceRecId: 42, notesMarkdown: "", notes: "", memberRecId: 10, problemFlag: false, resolutionFlag: false, internalAnalysisFlag: false, internalMemberFlag: false, mergedFlag: false, bundledFlag: false, createdByParentFlag: false, dateCreatedUtc: null, lastUpdatedUtc: null }, + serviceTicketNoteTranslation, + ctx + ); + }); + }); + + test("unknown memberRecId triggers skipRow", async () => { + const mod = await import("../service-ticket-note"); + serviceTicketNoteTranslation = mod.serviceTicketNoteTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + serviceTicketIds: new Set([42]), + usersByMemberRecId: new Map(), // empty + }); + expectSkipRow(() => { + translateRow( + { ticketNoteRecId: 1, srServiceRecId: 42, notesMarkdown: "", notes: "", memberRecId: 999, problemFlag: false, resolutionFlag: false, internalAnalysisFlag: false, internalMemberFlag: false, mergedFlag: false, bundledFlag: false, createdByParentFlag: false, dateCreatedUtc: null, lastUpdatedUtc: null }, + serviceTicketNoteTranslation, + ctx + ); + }); + }); + + test("null dates return null", async () => { + const mod = await import("../service-ticket-note"); + serviceTicketNoteTranslation = mod.serviceTicketNoteTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + serviceTicketIds: new Set([42]), + usersByMemberRecId: new Map([[10, "user-abc"]]), + }); + const result = translateRow( + { ticketNoteRecId: 1, srServiceRecId: 42, notesMarkdown: "", notes: "", memberRecId: 10, problemFlag: false, resolutionFlag: false, internalAnalysisFlag: false, internalMemberFlag: false, mergedFlag: false, bundledFlag: false, createdByParentFlag: false, dateCreatedUtc: null, lastUpdatedUtc: null }, + serviceTicketNoteTranslation, + ctx + ); + expect(result.createdAt).toBeNull(); + expect(result.updatedAt).toBeNull(); + }); +}); + +// ═══════════════════════════════════════════════════════════════════════════ +// Product Data Translator +// ═══════════════════════════════════════════════════════════════════════════ +describe("productDataTranslation", () => { + let productDataTranslation: AnyTranslation; + + test("module loads", async () => { + const mod = await import("../product-data"); + productDataTranslation = mod.productDataTranslation as unknown as AnyTranslation; + expect(productDataTranslation).toBeDefined(); + }); + + test("missing catalog item triggers skipRow", async () => { + const mod = await import("../product-data"); + productDataTranslation = mod.productDataTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + catalogItemIds: new Set([1]), // 999 NOT in set + corporateLocationIds: new Set([1]), + }); + expectSkipRow(() => { + translateRow( + { ivProductRecId: 1, quantity: 1, catalogRecId: 999, ownerLevelRecId: 1, internalNote: null, shortDescription: null, sequenceNumber: null, unitPrice: 10, unitCost: 5, listPrice: 15, discountAmount: 0, recurringRevenue: 0, recurringCost: 0, qtyPicked: 0, qtyShipped: 0, cancelReason: null, quantityCancelled: null, billableFlag: true, taxableFlag: false, invoiceFlag: false, recurringFlag: false, poApprovedFlag: false, calcPriceFlag: false, calcCostFlag: false, cancelFlag: false, srServiceRecId: null, opportunityRecId: null, updatedBy: null, enteredBy: null, closedBy: null, cancelBy: null, dateClosedUtc: null, cancelDateUtc: null, lastUpdateUtc: new Date(), dateEnteredUtc: new Date() }, + productDataTranslation, + ctx + ); + }); + }); + + test("missing corporate location triggers skipRow", async () => { + const mod = await import("../product-data"); + productDataTranslation = mod.productDataTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + catalogItemIds: new Set([1]), + corporateLocationIds: new Set(), // empty + }); + expectSkipRow(() => { + translateRow( + { ivProductRecId: 1, quantity: 1, catalogRecId: 1, ownerLevelRecId: 999, internalNote: null, shortDescription: null, sequenceNumber: null, unitPrice: 10, unitCost: 5, listPrice: 15, discountAmount: 0, recurringRevenue: 0, recurringCost: 0, qtyPicked: 0, qtyShipped: 0, cancelReason: null, quantityCancelled: null, billableFlag: true, taxableFlag: false, invoiceFlag: false, recurringFlag: false, poApprovedFlag: false, calcPriceFlag: false, calcCostFlag: false, cancelFlag: false, srServiceRecId: null, opportunityRecId: null, updatedBy: null, enteredBy: null, closedBy: null, cancelBy: null, dateClosedUtc: null, cancelDateUtc: null, lastUpdateUtc: new Date(), dateEnteredUtc: new Date() }, + productDataTranslation, + ctx + ); + }); + }); + + test("numeric conversions handle null and invalid values", async () => { + const mod = await import("../product-data"); + productDataTranslation = mod.productDataTranslation as unknown as AnyTranslation; + const ctx = makeContext({ + catalogItemIds: new Set([1]), + corporateLocationIds: new Set([1]), + serviceTicketIds: new Set(), + opportunityIds: new Set(), + productCustomByIvProductId: new Map(), + }); + const result = translateRow( + { ivProductRecId: 1, quantity: null, catalogRecId: 1, ownerLevelRecId: 1, internalNote: null, shortDescription: null, sequenceNumber: null, unitPrice: null, unitCost: null, listPrice: null, discountAmount: null, recurringRevenue: null, recurringCost: null, qtyPicked: null, qtyShipped: null, cancelReason: null, quantityCancelled: null, billableFlag: true, taxableFlag: false, invoiceFlag: false, recurringFlag: false, poApprovedFlag: false, calcPriceFlag: false, calcCostFlag: false, cancelFlag: false, srServiceRecId: null, opportunityRecId: null, updatedBy: null, enteredBy: null, closedBy: null, cancelBy: null, dateClosedUtc: null, cancelDateUtc: null, lastUpdateUtc: new Date(), dateEnteredUtc: new Date() }, + productDataTranslation, + ctx + ); + expect(result.qty).toBe(1); // null quantity defaults to 1 + expect(result.unitPrice).toBe(0); + expect(result.unitCost).toBe(0); + expect(result.listPrice).toBe(0); + expect(result.discount).toBe(0); + }); +}); + +// ═══════════════════════════════════════════════════════════════════════════ +// Service Ticket Priority Translator (Boolean fix) +// ═══════════════════════════════════════════════════════════════════════════ +describe("serviceTicketPriorityTranslation", () => { + test("defaultFlag is coerced to boolean", async () => { + const mod = await import("../service-ticket-priority"); + const translation = mod.serviceTicketPriorityTranslation as unknown as AnyTranslation; + const ctx = makeContext(); + + // Truthy value + const result1 = translateRow( + { srUrgencyRecId: 1, description: "High", color: "#FF0000", defaultFlag: 1, createdBy: null, updatedBy: null, dateCreatedUtc: new Date(), lastUpdatedUtc: new Date() }, + translation, ctx + ); + expect(result1.defaultFlag).toBe(true); + + // Falsy value + const result2 = translateRow( + { srUrgencyRecId: 2, description: "Low", color: null, defaultFlag: 0, createdBy: null, updatedBy: null, dateCreatedUtc: new Date(), lastUpdatedUtc: new Date() }, + translation, ctx + ); + expect(result2.defaultFlag).toBe(false); + + // Null value + const result3 = translateRow( + { srUrgencyRecId: 3, description: "Med", color: null, defaultFlag: null, createdBy: null, updatedBy: null, dateCreatedUtc: new Date(), lastUpdatedUtc: new Date() }, + translation, ctx + ); + expect(result3.defaultFlag).toBe(false); + }); +}); + +// ═══════════════════════════════════════════════════════════════════════════ +// SkipRowError type +// ═══════════════════════════════════════════════════════════════════════════ +describe("SkipRowError", () => { + test("is distinct from generic Error", () => { + const err = new SkipRowError("test reason"); + expect(err).toBeInstanceOf(SkipRowError); + expect(err).toBeInstanceOf(Error); + expect(err.name).toBe("SkipRowError"); + expect(err.message).toBe("test reason"); + expect(err.__brand).toBe("SkipRowError"); + }); + + test("does not match SKIP_ROW: prefix check", () => { + const err = new SkipRowError("Something wrong"); + // Old code would check: error.message.startsWith("SKIP_ROW:") + // New errors don't have that prefix + expect(err.message.startsWith("SKIP_ROW:")).toBe(false); + }); +}); diff --git a/dalpuri/src/translations/catalog-category.ts b/dalpuri/src/translations/catalog-category.ts new file mode 100644 index 0000000..989b2f3 --- /dev/null +++ b/dalpuri/src/translations/catalog-category.ts @@ -0,0 +1,23 @@ +import { ProductCategory as CwProductCategory } from "../../generated/prisma/client"; +import { CatalogCategory as ApiCatalogCategory } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const catalogCategoryTranslation: Translation< + CwProductCategory, + ApiCatalogCategory +> = { + values: [ + { from: "categoryRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Category"), + }, + { from: "description", to: "description" }, + { from: "inactiveFlag", to: "inactiveFlag" }, + { from: "updatedBy", to: "updatedById" }, + { from: "enteredBy", to: "createdById" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + { from: "dateEnteredUtc", to: "createdAt" }, + ], +}; diff --git a/dalpuri/src/translations/catalog-item-type.ts b/dalpuri/src/translations/catalog-item-type.ts new file mode 100644 index 0000000..dd22cff --- /dev/null +++ b/dalpuri/src/translations/catalog-item-type.ts @@ -0,0 +1,24 @@ +import { ProductType as CwProductType } from "../../generated/prisma/client"; +import { CatalogItemType as ApiCatalogItemType } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const catalogItemTypeTranslation: Translation< + CwProductType, + ApiCatalogItemType +> = { + values: [ + { from: "typeRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Type"), + }, + { from: "description", to: "description" }, + { from: "inactiveFlag", to: "inactiveFlag" }, + { from: "defaultFlag", to: "defaultFlag" }, + { from: "updatedBy", to: "updatedById" }, + { from: "enteredBy", to: "createdById" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + { from: "dateEnteredUtc", to: "createdAt" }, + ], +}; diff --git a/dalpuri/src/translations/catalog-item.ts b/dalpuri/src/translations/catalog-item.ts new file mode 100644 index 0000000..d6173d1 --- /dev/null +++ b/dalpuri/src/translations/catalog-item.ts @@ -0,0 +1,58 @@ +import { ProductCatalog as CwProductCatalog } from "../../generated/prisma/client"; +import { CatalogItem as ApiCatalogItem } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const catalogItemTranslation: Translation< + CwProductCatalog, + ApiCatalogItem +> = { + values: [ + { from: "catalogRecId", to: "id" }, + { from: "itemId", to: "identifier" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unnamed Item"), + }, + { from: "longDescription", to: "description" }, + { from: "longDescription", to: "customerDescription" }, + { from: "notes", to: "internalNotes" }, + { + from: "subcategoryRecId", + to: "subcategoryId", + process: (value) => { + if (value == null) { + throw new Error( + "CatalogItem subcategoryId is required but subcategoryRecId is null" + ); + } + return value; + }, + }, + { from: "manufacturerRecId", to: "manufacturerId" }, + { from: "manufacturerPartNum", to: "partNumber" }, + { from: "vendorSku", to: "vendorSku" }, + { from: "vendorRecId", to: "vendorCwId" }, + { + from: "listPrice", + to: "price", + process: (value) => Number(value), + }, + { + from: "currentCost", + to: "cost", + process: (value) => (value == null ? 0 : Number(value)), + }, + { from: "inactiveFlag", to: "inactive" }, + { from: "taxableFlag", to: "salesTaxable" }, + { + from: "minimumStock", + to: "onHand", + process: (value) => (value == null ? 0 : value), + }, + { from: "classId", to: "classId" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "cwLastUpdated" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/catalog-manufacturer.ts b/dalpuri/src/translations/catalog-manufacturer.ts new file mode 100644 index 0000000..71d0fd3 --- /dev/null +++ b/dalpuri/src/translations/catalog-manufacturer.ts @@ -0,0 +1,23 @@ +import { Manufacturer as CwManufacturer } from "../../generated/prisma/client"; +import { CatalogManufacturer as ApiCatalogManufacturer } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const catalogManufacturerTranslation: Translation< + CwManufacturer, + ApiCatalogManufacturer +> = { + values: [ + { from: "manufacturerRecId", to: "id" }, + { from: "manufacturerName", to: "name" }, + { from: "manufacturerName", to: "description" }, + { + from: "inactiveFlag", + to: "inactiveFlag", + process: (value) => Boolean(value), + }, + { from: "updatedBy", to: "updatedById" }, + { from: "enteredBy", to: "createdById" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + { from: "dateEnteredUtc", to: "createdAt" }, + ], +}; diff --git a/dalpuri/src/translations/catalog-subcategory.ts b/dalpuri/src/translations/catalog-subcategory.ts new file mode 100644 index 0000000..5bdee4e --- /dev/null +++ b/dalpuri/src/translations/catalog-subcategory.ts @@ -0,0 +1,24 @@ +import { ProductSubcategory as CwProductSubcategory } from "../../generated/prisma/client"; +import { CatalogSubcategory as ApiCatalogSubcategory } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const catalogSubcategoryTranslation: Translation< + CwProductSubcategory, + ApiCatalogSubcategory +> = { + values: [ + { from: "subcategoryRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Subcategory"), + }, + { from: "description", to: "description" }, + { from: "categoryRecId", to: "categoryId" }, + { from: "inactiveFlag", to: "inactiveFlag" }, + { from: "updatedBy", to: "updatedById" }, + { from: "enteredBy", to: "createdById" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + { from: "dateEnteredUtc", to: "createdAt" }, + ], +}; diff --git a/dalpuri/src/translations/company-address.ts b/dalpuri/src/translations/company-address.ts new file mode 100644 index 0000000..e78e2cd --- /dev/null +++ b/dalpuri/src/translations/company-address.ts @@ -0,0 +1,53 @@ +import { CompanyAddress as CwCompanyAddress } from "../../generated/prisma/client"; +import { + CompanyAddress as ApiCompanyAddress, + Country, + USState, +} from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +const toUSState = (value: string | null): USState | null => { + if (!value) return null; + const normalized = value.trim().toUpperCase(); + if (normalized in USState) { + return normalized as USState; + } + return null; +}; + +const toCountry = (value: number | null): Country | null => { + if (value == null) return null; + return Country.US; +}; + +export const companyAddressTranslation: Translation< + CwCompanyAddress, + ApiCompanyAddress +> = { + values: [ + { from: "companyAddressRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Primary Site"), + }, + { from: "description", to: "description" }, + { from: "addressLine1", to: "addressLine1" }, + { from: "addressLine2", to: "addressLine2" }, + { from: "city", to: "city" }, + { from: "stateId", to: "state", process: toUSState }, + { from: "zip", to: "zipCode" }, + { from: "countryRecId", to: "country", process: toCountry }, + { from: "phoneNbr", to: "phone" }, + { from: "phoneNbrFax", to: "fax" }, + { from: "inactiveFlag", to: "inactiveFlag" }, + { from: "defaultFlag", to: "defaultFlag" }, + { from: "defaultMailFlag", to: "defaultMailFlag" }, + { from: "defaultBillFlag", to: "defaultBillFlag" }, + { from: "defaultShipFlag", to: "defaultShipFlag" }, + { from: "updatedBy", to: "updatedById" }, + { from: "companyRecId", to: "companyId" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/company.ts b/dalpuri/src/translations/company.ts index 3d690dd..76699ed 100644 --- a/dalpuri/src/translations/company.ts +++ b/dalpuri/src/translations/company.ts @@ -2,6 +2,22 @@ import { Company as CwCompany } from "../../generated/prisma/client"; import { Company as ApiCompany } from "../../../api/generated/prisma/client"; import { Translation } from "./types"; -const companyTranslation: Translation = { - values: [{ from: "" }], +export const companyTranslation: Translation = { + values: [ + { from: "companyRecId", to: "id" }, + { + from: "companyName", + to: "name", + process: (value) => (value ? value : "Unnamed Company"), + }, + { from: "phoneNbr", to: "phone" }, + { from: "websiteUrl", to: "website" }, + { from: "deleteFlag", to: "deleteFlag" }, + { from: "dateDeleted", to: "dateDeleted" }, + { from: "taxId", to: "taxId" }, + { from: "deletedBy", to: "deletedById" }, + { from: "enteredBy", to: "enteredById" }, + { from: "dateEntered", to: "createdAt" }, + { from: "lastUpdate", to: "updatedAt" }, + ], }; diff --git a/dalpuri/src/translations/contact.ts b/dalpuri/src/translations/contact.ts new file mode 100644 index 0000000..ea03dda --- /dev/null +++ b/dalpuri/src/translations/contact.ts @@ -0,0 +1,63 @@ +import { Contact as CwContact } from "../../generated/prisma/client"; +import { + Contact as ApiContact, + GenderType, + PhoneType, +} from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +const toGender = (value: string | null): GenderType | null => { + if (!value) return null; + const normalized = value.trim().toUpperCase(); + if (normalized === "M") return GenderType.MALE; + if (normalized === "F") return GenderType.FEMALE; + return null; +}; + +const toPhoneType = (value: string | null): PhoneType | null => { + if (!value) return null; + const normalized = value.trim().toUpperCase(); + if (normalized === "DIRECT") return PhoneType.DIRECT; + if (normalized === "MOBILE") return PhoneType.MOBILE; + if (normalized === "HOME") return PhoneType.HOME; + if (normalized === "COMPANY") return PhoneType.COMPANY; + if (normalized === "SITE") return PhoneType.SITE; + return null; +}; + +export const contactTranslation: Translation = { + values: [ + { from: "contactRecId", to: "id" }, + { + from: "inactiveFlag", + to: "active", + process: (value) => !value, + }, + { + from: "defaultFlag", + to: "default", + process: (value) => Boolean(value), + }, + { + from: "firstName", + to: "firstName", + process: (value) => (value ? value : "Unknown"), + }, + { + from: "lastName", + to: "lastName", + process: (value) => (value ? value : "Contact"), + }, + { from: "nickName", to: "nickname" }, + { from: "title", to: "title" }, + { from: "gender", to: "gender", process: toGender }, + { from: "dateBirth", to: "birthday" }, + { from: "defaultPhoneNbr", to: "phone" }, + { from: "defaultPhoneExtension", to: "phoneExtension" }, + { from: "defaultPhoneType", to: "phoneType", process: toPhoneType }, + { from: "companyAddressRecId", to: "companyAddressId" }, + { from: "companyRecId", to: "companyId" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/context.ts b/dalpuri/src/translations/context.ts new file mode 100644 index 0000000..52e14c7 --- /dev/null +++ b/dalpuri/src/translations/context.ts @@ -0,0 +1,107 @@ +/** + * Global context object passed to all translation process functions + * Contains lookup tables and computed values from external sources + */ +export interface TranslationContext { + // Set of API User.id values for FK validation + userIds: Set; + + // Set of API ServiceTicket.id values for FK validation + serviceTicketIds: Set; + + // Set of API Opportunity.id values for FK validation + opportunityIds: Set; + + // Set of API CatalogItem.id values for FK validation + catalogItemIds: Set; + + // Set of API CorporateLocation.id values for FK validation + corporateLocationIds: Set; + + // Set of API Company.id values for FK validation + companyIds: Set; + + // Set of API CompanyAddress.id values for FK validation + companyAddressIds: Set; + + // Set of API Contact.id values for FK validation + contactIds: Set; + + // Set of API OpportunityStage.id values for FK validation + opportunityStageIds: Set; + + // User lookups: CW memberRecId -> API User.id + usersByMemberRecId: Map; + + // User lookups: CW memberRecId -> API User.cwIdentifier + userIdentifiersByMemberRecId: Map; + + // User lookups: CW member identifier string -> API User.id + usersByIdentifier: Map; + + // Service ticket board lookups: API ServiceTicketBoard.id -> API ServiceTicketBoard.uid + serviceTicketBoardUidsById: Map; + + // Optional custom-field derived lookups keyed by CW SR_Service_RecID + billingTypeByTicketId: Map; + billingInstructionsByTicketId: Map; + + // Opportunity/Product custom-field lookups loaded from CW reporting views. + opportunityNarrativeByOpportunityId: Map; + productCustomByIvProductId: Map< + number, + { + procurementNotes: string | null; + productNarrative: string | null; + } + >; + + // Required FK fallback for Opportunity.typeId + defaultOpportunityTypeId: number | null; + + // Set of valid OpportunityStatus.id values for FK validation + opportunityStatusIds: Set; + + // Set of valid ScheduleStatus.id values for FK validation + scheduleStatusIds: Set; + + // Set of valid ScheduleType.id values for FK validation + scheduleTypeIds: Set; + + // Set of valid ScheduleSpan.id values for FK validation + scheduleSpanIds: Set; + + // Set of API TaxCode.id values for FK validation + taxCodeIds: Set; +} + +/** + * Create an empty context object with initialized maps + */ +export function createTranslationContext(): TranslationContext { + return { + userIds: new Set(), + serviceTicketIds: new Set(), + opportunityIds: new Set(), + catalogItemIds: new Set(), + corporateLocationIds: new Set(), + companyIds: new Set(), + companyAddressIds: new Set(), + contactIds: new Set(), + opportunityStageIds: new Set(), + usersByMemberRecId: new Map(), + userIdentifiersByMemberRecId: new Map(), + usersByIdentifier: new Map(), + serviceTicketBoardUidsById: new Map(), + billingTypeByTicketId: new Map(), + billingInstructionsByTicketId: new Map(), + opportunityNarrativeByOpportunityId: new Map(), + productCustomByIvProductId: new Map(), + defaultOpportunityTypeId: null, + opportunityStatusIds: new Set(), + scheduleStatusIds: new Set(), + scheduleTypeIds: new Set(), + scheduleSpanIds: new Set(), + taxCodeIds: new Set(), + }; +} diff --git a/dalpuri/src/translations/corporate-location.ts b/dalpuri/src/translations/corporate-location.ts new file mode 100644 index 0000000..7564f62 --- /dev/null +++ b/dalpuri/src/translations/corporate-location.ts @@ -0,0 +1,45 @@ +import { OwnerLevel as CwOwnerLevel } from "../../generated/prisma/client"; +import { + CorporateLocation as ApiCorporateLocation, + Country, + USState, +} from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +const toUSState = (value: string | null): USState | null => { + if (!value) return null; + const normalized = value.trim().toUpperCase(); + if (normalized in USState) { + return normalized as USState; + } + return null; +}; + +const toCountry = (value: number | null): Country | null => { + if (value == null) return null; + return Country.US; +}; + +export const corporateLocationTranslation: Translation< + CwOwnerLevel, + ApiCorporateLocation +> = { + values: [ + { from: "ownerLevelRecId", to: "id" }, + { + from: "ownerLevelName", + to: "name", + process: (value) => (value ? value : "Unknown Location"), + }, + { from: "description", to: "description" }, + { from: "updatedBy", to: "updatedById" }, + { from: "olAddressLine1", to: "addressLine1" }, + { from: "olAddressLine2", to: "addressLine2" }, + { from: "olCity", to: "city" }, + { from: "olStateId", to: "state", process: toUSState }, + { from: "olZip", to: "zipCode" }, + { from: "olCountryRecId", to: "country", process: toCountry }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/cw-member.ts b/dalpuri/src/translations/cw-member.ts new file mode 100644 index 0000000..eb5c090 --- /dev/null +++ b/dalpuri/src/translations/cw-member.ts @@ -0,0 +1,29 @@ +import { Member as CwMember } from "../../generated/prisma/client"; +import { CwMember as ApiCwMember } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const cwMemberTranslation: Translation = { + values: [ + { from: "memberRecId", to: "cwMemberId" }, + { from: "memberId", to: "identifier" }, + { + from: "firstName", + to: "firstName", + process: (value) => (value ? value : "Unknown"), + }, + { + from: "lastName", + to: "lastName", + process: (value) => (value ? value : "Member"), + }, + { from: "emailAddress", to: "officeEmail" }, + { from: "inactiveFlag", to: "inactiveFlag" }, + { + from: "dateHire", + to: "createdAt", + process: (value, _context, row) => value ?? row.lastUpdatedUtc, + }, + { from: "lastUpdatedUtc", to: "cwLastUpdated" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/internal-department.ts b/dalpuri/src/translations/internal-department.ts new file mode 100644 index 0000000..a086e2b --- /dev/null +++ b/dalpuri/src/translations/internal-department.ts @@ -0,0 +1,20 @@ +import { Department as CwDepartment } from "../../generated/prisma/client"; +import { InternalDepartment as ApiInternalDepartment } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const internalDepartmentTranslation: Translation< + CwDepartment, + ApiInternalDepartment +> = { + values: [ + { from: "departmentRecId", to: "id" }, + { + from: "departmentName", + to: "name", + process: (value) => (value ? value : "Unknown Department"), + }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "updatedBy", to: "updatedById" }, + { from: "lastUpdateUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/opportunity-stage.ts b/dalpuri/src/translations/opportunity-stage.ts new file mode 100644 index 0000000..766743e --- /dev/null +++ b/dalpuri/src/translations/opportunity-stage.ts @@ -0,0 +1,32 @@ +import { SoPipeline as CwSoPipeline } from "../../generated/prisma/client"; +import { OpportunityStage as ApiOpportunityStage } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +type ApiOpportunityStageRecord = { + id: number; + name: string; + seqNbr?: number | null; + funnelColor?: string | null; + updatedById?: string | null; + createdAt: Date; + updatedAt: Date; +}; + +export const opportunityStageTranslation: Translation< + CwSoPipeline, + ApiOpportunityStageRecord +> = { + values: [ + { from: "soPipelineRecId", to: "id" }, + { + from: "description", + to: "name", + process: (v: string | null) => v ?? "Unknown Stage", + }, + { from: "seqNbr", to: "seqNbr" }, + { from: "funnelColor", to: "funnelColor" }, + { from: "updatedBy", to: "updatedById" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/opportunity-status.ts b/dalpuri/src/translations/opportunity-status.ts new file mode 100644 index 0000000..d029d49 --- /dev/null +++ b/dalpuri/src/translations/opportunity-status.ts @@ -0,0 +1,26 @@ +import { SoOppStatus as CwOpportunityStatus } from "../../generated/prisma/client"; +import { OpportunityStatus as ApiOpportunityStatus } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const opportunityStatusTranslation: Translation< + CwOpportunityStatus, + ApiOpportunityStatus +> = { + values: [ + { from: "soOppStatusRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Status"), + }, + { from: "description", to: "description" }, + { from: "inactiveFlag", to: "inactiveFlag" }, + { from: "defaultFlag", to: "defaultFlag" }, + { from: "wonFlag", to: "wonFlag" }, + { from: "lostFlag", to: "lostFlag" }, + { from: "closedFlag", to: "closeFlag" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "updatedBy", to: "updatedById" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/opportunity-type.ts b/dalpuri/src/translations/opportunity-type.ts new file mode 100644 index 0000000..2ebea54 --- /dev/null +++ b/dalpuri/src/translations/opportunity-type.ts @@ -0,0 +1,22 @@ +import { SoType as CwOpportunityType } from "../../generated/prisma/client"; +import { OpportunityType as ApiOpportunityType } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const opportunityTypeTranslation: Translation< + CwOpportunityType, + ApiOpportunityType +> = { + values: [ + { from: "soTypeRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Type"), + }, + { from: "description", to: "description" }, + { from: "inactiveFlag", to: "inactiveFlag" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "updatedBy", to: "updatedById" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/opportunity.ts b/dalpuri/src/translations/opportunity.ts new file mode 100644 index 0000000..3623219 --- /dev/null +++ b/dalpuri/src/translations/opportunity.ts @@ -0,0 +1,143 @@ +import { + Opportunity as CwOpportunity, + OpportunityMember as CwOpportunityMember, +} from "../../generated/prisma/client"; +import { OpportunityInterest } from "../../../api/generated/prisma/client"; +import { Translation, skipRow } from "./types"; +import { TranslationContext } from "./context"; + +type ApiOpportunityRecord = { + id: number; + name: string; + notes?: string | null; + oppNarrative?: string | null; + typeId: number; + stageCwId?: number | null; + stageId?: number | null; + statusId?: number | null; + taxCodeId?: number | null; + interest?: OpportunityInterest | null; + probability: number; + source?: string | null; + primarySalesRepId?: string | null; + secondarySalesRepId?: string | null; + companyId?: number | null; + contactId?: number | null; + siteId?: number | null; + customerPO?: string | null; + expectedCloseDate?: Date | null; + pipelineChangeDate?: Date | null; + dateBecameLead?: Date | null; + closedDate?: Date | null; + closedFlag: boolean; + closedById?: string | null; + updatedBy: string; + eneteredBy: string; + createdAt: Date; + updatedAt: Date; +}; + +type CwOpportunityWithMembers = CwOpportunity & { + members?: Pick< + CwOpportunityMember, + "memberRecId" | "primarySalesFlag" | "secondarySalesFlag" + >[]; +}; + +const toInterest = (value: number | null): OpportunityInterest | null => { + if (value == null) return null; + if (value <= 1) return OpportunityInterest.COLD; + if (value === 2) return OpportunityInterest.WARM; + return OpportunityInterest.HOT; +}; + +export const opportunityTranslation: Translation< + CwOpportunityWithMembers, + ApiOpportunityRecord, + TranslationContext +> = { + values: [ + { from: "opportunityRecId", to: "id" }, + { from: "opportunityName", to: "name" }, + { from: "notes", to: "notes" }, + { + from: "opportunityRecId", + to: "oppNarrative", + process: (value, context) => { + if (value == null) return null; + return context.opportunityNarrativeByOpportunityId.get(value) ?? null; + }, + }, + { + from: "soTypeRecId", + to: "typeId", + process: (value, context) => { + if (value != null) return value; + if (context.defaultOpportunityTypeId != null) + return context.defaultOpportunityTypeId; + skipRow("Opportunity missing type and no default type available"); + }, + }, + { from: "soPipelineRecId", to: "stageId" }, + { from: "soOppStatusRecId", to: "statusId" }, + { from: "taxCodeRecId", to: "taxCodeId" }, + { + from: "soInterestRecId", + to: "interest", + process: toInterest, + }, + { + from: "probabilityToClose", + to: "probability", + process: (value) => (value == null ? 0 : value), + }, + { from: "source", to: "source" }, + { + from: "opportunityRecId", + to: "primarySalesRepId", + process: (_value, context, row) => { + const primary = row.members?.find((member) => member.primarySalesFlag); + if (!primary) return null; + return ( + context.userIdentifiersByMemberRecId.get(primary.memberRecId) ?? null + ); + }, + }, + { + from: "opportunityRecId", + to: "secondarySalesRepId", + process: (_value, context, row) => { + const secondary = row.members?.find( + (member) => member.secondarySalesFlag + ); + if (!secondary) return null; + return ( + context.userIdentifiersByMemberRecId.get(secondary.memberRecId) ?? + null + ); + }, + }, + { from: "companyRecId", to: "companyId" }, + { from: "contactRecId", to: "contactId" }, + { from: "companyAddressRecId", to: "siteId" }, + { from: "poNumber", to: "customerPO" }, + { from: "dateCloseExpected", to: "expectedCloseDate" }, + { from: "datePipelineChange", to: "pipelineChangeDate" }, + { from: "dateBecameLead", to: "dateBecameLead" }, + { from: "dateClosed", to: "closedDate" }, + { from: "oldCloseFlag", to: "closedFlag" }, + { from: "closedBy", to: "closedById" }, + { + from: "updatedBy", + to: "updatedBy", + process: (value) => (value ? value : "system"), + }, + { + from: "enteredBy", + to: "eneteredBy", + process: (value) => (value ? value : "system"), + }, + { from: "dateBecameLeadUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/product-data.ts b/dalpuri/src/translations/product-data.ts new file mode 100644 index 0000000..d12f49c --- /dev/null +++ b/dalpuri/src/translations/product-data.ts @@ -0,0 +1,218 @@ +import { IV_Product as CwIVProduct } from "../../generated/prisma/client"; +import { Translation, skipRow } from "./types"; +import { TranslationContext } from "./context"; + +type ApiProductDataRecord = { + id: number; + qty: number; + internalNote?: string | null; + shortDescription?: string | null; + description?: string | null; + sequenceNumber?: number | null; + procurementNotes?: string | null; + productNarrative?: string | null; + unitPrice: number; + unitCost: number; + listPrice: number; + discount: number; + recurringRevenue: number; + recurringCost: number; + qtyPicked: number; + qtyShipped: number; + cancelReason?: string | null; + cancelQty?: number | null; + billableFlag: boolean; + taxableFlag: boolean; + invoiceFlag: boolean; + recurringFlag: boolean; + poApprovedFlag: boolean; + calcPriceFlag: boolean; + calcCostFlag: boolean; + cancelFlag: boolean; + catalogItemId: number; + corporateLocationId: number; + serviceTicketId?: number | null; + opportunityId?: number | null; + updatedById?: string | null; + createdById?: string | null; + closedById?: string | null; + cancelById: string; + closedAt?: Date | null; + cancelledAt?: Date | null; + createdAt: Date; + updatedAt: Date; +}; + +const toInt = (value: unknown, fallback = 0): number => { + if (value == null) return fallback; + const numeric = Number(value); + if (!Number.isFinite(numeric)) return fallback; + return Math.trunc(numeric); +}; + +const toNumber = (value: unknown, fallback = 0): number => { + if (value == null) return fallback; + const numeric = Number(value); + return Number.isFinite(numeric) ? numeric : fallback; +}; + +export const productDataTranslation: Translation< + CwIVProduct, + ApiProductDataRecord, + TranslationContext +> = { + values: [ + { from: "ivProductRecId", to: "id" }, + { + from: "quantity", + to: "qty", + process: (value) => toNumber(value, 1), + }, + { from: "internalNote", to: "internalNote" }, + { from: "shortDescription", to: "shortDescription" }, + { from: "description", to: "description" }, + { + from: "ivProductRecId", + to: "procurementNotes", + process: (value, context) => { + const custom = context.productCustomByIvProductId.get(value); + return custom?.procurementNotes ?? null; + }, + }, + { + from: "ivProductRecId", + to: "productNarrative", + process: (value, context) => { + const custom = context.productCustomByIvProductId.get(value); + return custom?.productNarrative ?? null; + }, + }, + { + from: "sequenceNumber", + to: "sequenceNumber", + process: (value) => (value == null ? null : toInt(value, 0)), + }, + { + from: "unitPrice", + to: "unitPrice", + process: (value) => toNumber(value, 0), + }, + { + from: "unitCost", + to: "unitCost", + process: (value) => toNumber(value, 0), + }, + { + from: "listPrice", + to: "listPrice", + process: (value) => toNumber(value, 0), + }, + { + from: "discountAmount", + to: "discount", + process: (value) => toNumber(value, 0), + }, + { + from: "recurringRevenue", + to: "recurringRevenue", + process: (value) => toNumber(value, 0), + }, + { + from: "recurringCost", + to: "recurringCost", + process: (value) => toNumber(value, 0), + }, + { + from: "qtyPicked", + to: "qtyPicked", + process: (value) => toInt(value, 0), + }, + { + from: "qtyShipped", + to: "qtyShipped", + process: (value) => toInt(value, 0), + }, + { from: "cancelReason", to: "cancelReason" }, + { + from: "quantityCancelled", + to: "cancelQty", + process: (value) => (value == null ? null : toNumber(value, 0)), + }, + { from: "billableFlag", to: "billableFlag" }, + { from: "taxableFlag", to: "taxableFlag" }, + { from: "invoiceFlag", to: "invoiceFlag" }, + { from: "recurringFlag", to: "recurringFlag" }, + { from: "poApprovedFlag", to: "poApprovedFlag" }, + { from: "calcPriceFlag", to: "calcPriceFlag" }, + { from: "calcCostFlag", to: "calcCostFlag" }, + { from: "cancelFlag", to: "cancelFlag" }, + { + from: "catalogRecId", + to: "catalogItemId", + process: (value, context) => { + if (!context.catalogItemIds.has(value)) { + skipRow( + `ProductData catalog item missing for catalogRecId ${value}` + ); + } + return value; + }, + }, + { + from: "ownerLevelRecId", + to: "corporateLocationId", + process: (value, context) => { + if (!context.corporateLocationIds.has(value)) { + skipRow( + `ProductData corporate location missing for ownerLevelRecId ${value}` + ); + } + return value; + }, + }, + { + from: "srServiceRecId", + to: "serviceTicketId", + process: (value, context) => { + if (value == null) return null; + return context.serviceTicketIds.has(value) ? value : null; + }, + }, + { + from: "opportunityRecId", + to: "opportunityId", + process: (value, context) => { + if (value == null) return null; + return context.opportunityIds.has(value) ? value : null; + }, + }, + { from: "updatedBy", to: "updatedById" }, + { from: "enteredBy", to: "createdById" }, + { from: "closedBy", to: "closedById" }, + { + from: "cancelBy", + to: "cancelById", + process: (value, context) => { + if (value == null) return "system"; + return context.usersByMemberRecId.get(value) ?? `cw-member:${value}`; + }, + }, + { from: "dateClosedUtc", to: "closedAt" }, + { from: "cancelDateUtc", to: "cancelledAt" }, + { + from: "dateEntered", + to: "createdAt", + process: (value) => value ?? new Date(0), + }, + { + from: "dateEnteredUtc", + to: "createdAt", + process: (value) => value ?? new Date(0), + }, + { + from: "lastUpdatedUTC", + to: "updatedAt", + process: (value) => value ?? new Date(0), + }, + ], +}; diff --git a/dalpuri/src/translations/product-inventory.ts b/dalpuri/src/translations/product-inventory.ts new file mode 100644 index 0000000..f3f42e7 --- /dev/null +++ b/dalpuri/src/translations/product-inventory.ts @@ -0,0 +1,30 @@ +import { ProductInventory as CwProductInventory } from "../../generated/prisma/client"; +import { ProductInventory as ApiProductInventory } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const productInventoryTranslation: Translation< + CwProductInventory, + ApiProductInventory +> = { + values: [ + { from: "inventoryRecId", to: "id" }, + { + from: "qtyOnHand", + to: "qtyOnHand", + process: (value) => (value == null ? 0 : Number(value)), + }, + { + from: "lastUpdate", + to: "createdAt", + process: (value) => (value ? value : new Date(0)), + }, + { from: "warehouseBinRecId", to: "warehouseBinId" }, + { from: "catalogRecId", to: "itemId" }, + { from: "updatedBy", to: "updatedById" }, + { + from: "lastUpdate", + to: "updatedAt", + process: (value) => (value ? value : new Date(0)), + }, + ], +}; diff --git a/dalpuri/src/translations/schedule-span.ts b/dalpuri/src/translations/schedule-span.ts new file mode 100644 index 0000000..2665173 --- /dev/null +++ b/dalpuri/src/translations/schedule-span.ts @@ -0,0 +1,14 @@ +import { ScheduleSpan as CwScheduleSpan } from "../../generated/prisma/client"; +import { ScheduleSpan as ApiScheduleSpan } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const scheduleSpanTranslation: Translation< + CwScheduleSpan, + ApiScheduleSpan +> = { + values: [ + { from: "scheduleSpanRecId", to: "id" }, + { from: "scheduleSpanId", to: "scheduleSpanId" }, + { from: "spanDesc", to: "spanDesc" }, + ], +}; diff --git a/dalpuri/src/translations/schedule-status.ts b/dalpuri/src/translations/schedule-status.ts new file mode 100644 index 0000000..cb14864 --- /dev/null +++ b/dalpuri/src/translations/schedule-status.ts @@ -0,0 +1,33 @@ +import { ScheduleStatus as CwScheduleStatus } from "../../generated/prisma/client"; +import { ScheduleStatus as ApiScheduleStatus } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const scheduleStatusTranslation: Translation< + CwScheduleStatus, + ApiScheduleStatus +> = { + values: [ + { from: "scheduleStatusRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Status"), + }, + { from: "description", to: "description" }, + { from: "color", to: "color" }, + { + from: "softFlag", + to: "softFlag", + process: (value) => Boolean(value), + }, + { + from: "defaultFlag", + to: "defaultFlag", + process: (value) => Boolean(value), + }, + { from: "enteredBy", to: "createdById" }, + { from: "updatedBy", to: "updatedById" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdateUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/schedule-type.ts b/dalpuri/src/translations/schedule-type.ts new file mode 100644 index 0000000..40d7244 --- /dev/null +++ b/dalpuri/src/translations/schedule-type.ts @@ -0,0 +1,36 @@ +import { ScheduleType as CwScheduleType } from "../../generated/prisma/client"; +import { ScheduleType as ApiScheduleType } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const scheduleTypeTranslation: Translation< + CwScheduleType, + ApiScheduleType +> = { + values: [ + { from: "scheduleTypeRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Type"), + }, + { from: "description", to: "description" }, + { from: "displayColor", to: "displayColor" }, + { from: "tableReference", to: "tableReference" }, + { from: "moduleId", to: "moduleId" }, + { from: "scheduleTypeId", to: "scheduleTypeId" }, + { + from: "systemFlag", + to: "systemFlag", + process: (value) => Boolean(value), + }, + { + from: "displayFlag", + to: "displayFlag", + process: (value) => Boolean(value), + }, + { from: "enteredBy", to: "createdById" }, + { from: "updatedBy", to: "updatedById" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdateUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/schedule.ts b/dalpuri/src/translations/schedule.ts new file mode 100644 index 0000000..5cdc8a9 --- /dev/null +++ b/dalpuri/src/translations/schedule.ts @@ -0,0 +1,114 @@ +import { Schedule as CwSchedule } from "../../generated/prisma/client"; +import { Translation } from "./types"; + +type ApiScheduleRecord = { + id: number; + name: string; + description: string | null; + closedFlag: boolean; + reminderFlag: boolean; + allDayFlag: boolean; + acknowledgementFlag: boolean; + meetingFlag: boolean; + recurringFlag: boolean; + billableFlag: boolean; + acknowledgedById: string | null; + acknowledgedAt: Date | null; + startDate: Date | null; + endDate: Date | null; + hoursScheduled: number | null; + duration: number | null; + hoursPerDay: number | null; + reminderMinutes: number | null; + statusId: number | null; + typeId: number | null; + scheduleSpanId: number | null; + memberId: string | null; + updatedById: string | null; + createdById: string | null; + closedById: string | null; + closedAt: Date | null; + createdAt: Date; + updatedAt: Date; +}; + +export const scheduleTranslation: Translation = { + values: [ + { from: "scheduleRecId", to: "id" }, + { + from: "scheduleDesc", + to: "name", + process: (value) => (value ? value : ""), + }, + { from: "scheduleDesc", to: "description" }, + { + from: "closeFlag", + to: "closedFlag", + process: (value) => Boolean(value), + }, + { + from: "reminderFlag", + to: "reminderFlag", + process: (value) => Boolean(value), + }, + { + from: "allDayFlag", + to: "allDayFlag", + process: (value) => Boolean(value), + }, + { + from: "ackFlag", + to: "acknowledgementFlag", + process: (value) => Boolean(value), + }, + { + from: "meetingFlag", + to: "meetingFlag", + process: (value) => Boolean(value), + }, + { + from: "recurringFlag", + to: "recurringFlag", + process: (value) => Boolean(value), + }, + { + from: "billableFlag", + to: "billableFlag", + process: (value) => Boolean(value), + }, + { from: "acknowledgedBy", to: "acknowledgedById" }, + { from: "ackDateUtc", to: "acknowledgedAt" }, + { from: "dateTimeStartUtc", to: "startDate" }, + { from: "dateTimeEndUtc", to: "endDate" }, + { + from: "hoursSched", + to: "hoursScheduled", + process: (value) => (value != null ? Number(value) : null), + }, + { from: "duration", to: "duration" }, + { + from: "hoursPerDay", + to: "hoursPerDay", + process: (value) => (value != null ? Number(value) : null), + }, + { from: "reminderMinutes", to: "reminderMinutes" }, + { from: "scheduleStatusRecId", to: "statusId" }, + { from: "scheduleTypeRecId", to: "typeId" }, + { from: "scheduleSpanRecId", to: "scheduleSpanId" }, + { from: "memberId", to: "memberId" }, + { from: "updatedBy", to: "updatedById" }, + { from: "enteredBy", to: "createdById" }, + { from: "closedBy", to: "closedById" }, + { from: "closeDateUtc", to: "closedAt" }, + { + from: "dateEnteredUtc", + to: "createdAt", + process: (value) => (value as Date | null) ?? new Date(0), + }, + { + from: "lastUpdateUtc", + to: "updatedAt", + process: (value) => (value as Date | null) ?? new Date(0), + }, + ], +}; diff --git a/dalpuri/src/translations/service-ticket-board.ts b/dalpuri/src/translations/service-ticket-board.ts new file mode 100644 index 0000000..5296b43 --- /dev/null +++ b/dalpuri/src/translations/service-ticket-board.ts @@ -0,0 +1,85 @@ +import { SrBoard as CwServiceTicketBoard } from "../../generated/prisma/client"; +import { ServiceTicketBoard as ApiServiceTicketBoard } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const serviceTicketBoardTranslation: Translation< + CwServiceTicketBoard, + ApiServiceTicketBoard +> = { + values: [ + { from: "srBoardRecId", to: "id" }, + { + from: "boardName", + to: "name", + process: (value) => (value ? value : "Unknown Board"), + }, + { + from: "timeBillableFlag", + to: "timeBillableFlag", + process: (value) => Boolean(value), + }, + { + from: "expBillableFlag", + to: "expenseBillableFlag", + process: (value) => Boolean(value), + }, + { + from: "prodBillableFlag", + to: "productBillableFlag", + process: (value) => Boolean(value), + }, + { + from: "timeInvoiceFlag", + to: "timeInvoiceableFlag", + process: (value) => Boolean(value), + }, + { + from: "expInvoiceFlag", + to: "expenseInvoiceableFlag", + process: (value) => Boolean(value), + }, + { + from: "prodInvoiceFlag", + to: "productInvoiceableFlag", + process: (value) => Boolean(value), + }, + { + from: "autoAssignNewFlag", + to: "autoAssignNewFlag", + process: (value) => Boolean(value), + }, + { + from: "autoAssignEcFlag", + to: "autoAssignEmailCreatedFlag", + process: (value) => Boolean(value), + }, + { + from: "autoAssignPortalFlag", + to: "autoAssignPortalCreatedFlag", + process: (value) => Boolean(value), + }, + { from: "projectFlag", to: "projectFlag" }, + { + from: "lockDescFlag", + to: "lockDescriptionFlag", + process: (value) => Boolean(value), + }, + { + from: "emailContactFlag", + to: "emailContactFlag", + process: (value) => Boolean(value), + }, + { + from: "emailResourceFlag", + to: "emailResourceFlag", + process: (value) => Boolean(value), + }, + { from: "resolutionSort", to: "resolutionSortOrder" }, + { from: "internalAnalysisSort", to: "internalAnalysisSortOrder" }, + { from: "ownerLevelRecId", to: "locationId" }, + { from: "enteredBy", to: "createdById" }, + { from: "updatedBy", to: "updatedById" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/service-ticket-impact.ts b/dalpuri/src/translations/service-ticket-impact.ts new file mode 100644 index 0000000..d4f871b --- /dev/null +++ b/dalpuri/src/translations/service-ticket-impact.ts @@ -0,0 +1,27 @@ +import { SrImpact as CwServiceTicketImpact } from "../../generated/prisma/client"; +import { ServiceTicketImpact as ApiServiceTicketImpact } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const serviceTicketImpactTranslation: Translation< + CwServiceTicketImpact, + ApiServiceTicketImpact +> = { + values: [ + { from: "srImpactRecId", to: "id" }, + { + from: "impactName", + to: "name", + process: (value) => (value ? value : "Unknown Impact"), + }, + { from: "impactDesc", to: "description" }, + { + from: "defaultFlag", + to: "defaultFlag", + process: (value) => Boolean(value), + }, + { from: "enteredBy", to: "createdById" }, + { from: "updatedBy", to: "updatedById" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/service-ticket-location.ts b/dalpuri/src/translations/service-ticket-location.ts new file mode 100644 index 0000000..4b51d99 --- /dev/null +++ b/dalpuri/src/translations/service-ticket-location.ts @@ -0,0 +1,23 @@ +import { SrLocation as CwServiceTicketLocation } from "../../generated/prisma/client"; +import { ServiceTicketLocation as ApiServiceTicketLocation } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const serviceTicketLocationTranslation: Translation< + CwServiceTicketLocation, + ApiServiceTicketLocation +> = { + values: [ + { from: "srLocationRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Location"), + }, + { from: "description", to: "description" }, + { from: "defaultFlag", to: "defaultFlag" }, + { from: "enteredBy", to: "createdById" }, + { from: "updatedBy", to: "updatedById" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/service-ticket-note.ts b/dalpuri/src/translations/service-ticket-note.ts new file mode 100644 index 0000000..e0dc50a --- /dev/null +++ b/dalpuri/src/translations/service-ticket-note.ts @@ -0,0 +1,74 @@ +import { TicketNote as CwTicketNote } from "../../generated/prisma/client"; +import { ServiceTicketNote as ApiServiceTicketNote } from "../../../api/generated/prisma/client"; +import { Translation, skipRow } from "./types"; +import { TranslationContext } from "./context"; + +export const serviceTicketNoteTranslation: Translation< + CwTicketNote, + ApiServiceTicketNote, + TranslationContext +> = { + values: [ + { from: "ticketNoteRecId", to: "id" }, + { + from: "srServiceRecId", + to: "serviceTicketId", + process: (value: number | null, context: TranslationContext) => { + if (!value) { + skipRow("ServiceTicketNote missing srServiceRecId"); + } + + if (!context.serviceTicketIds.has(value)) { + skipRow( + `ServiceTicketNote parent ticket missing: ${value}` + ); + } + + return value; + }, + }, + { + from: "notesMarkdown", + to: "notesMd", + process: (value: string | null) => value || "", + }, + { + from: "notes", + to: "notes", + process: (value: string | null) => value || "", + }, + { + from: "memberRecId", + to: "authorId", + process: (value: number | null, context: TranslationContext) => { + if (!value) { + skipRow("ServiceTicketNote missing memberRecId"); + } + const userId = context.usersByMemberRecId.get(value); + if (!userId) { + skipRow( + `Cannot find user mapping for memberRecId: ${value}` + ); + } + return userId; + }, + }, + { from: "problemFlag", to: "problemFlag" }, + { from: "resolutionFlag", to: "resolutionFlag" }, + { from: "internalAnalysisFlag", to: "internalAnalysisFlag" }, + { from: "internalMemberFlag", to: "internalMemberFlag" }, + { from: "mergedFlag", to: "mergedFlag" }, + { from: "bundledFlag", to: "bundledFlag" }, + { from: "createdByParentFlag", to: "createdByParentFlag" }, + { + from: "dateCreatedUtc", + to: "createdAt", + process: (value: Date | null) => value || null, + }, + { + from: "lastUpdatedUtc", + to: "updatedAt", + process: (value: Date | null) => value || null, + }, + ], +}; diff --git a/dalpuri/src/translations/service-ticket-priority.ts b/dalpuri/src/translations/service-ticket-priority.ts new file mode 100644 index 0000000..3c4d755 --- /dev/null +++ b/dalpuri/src/translations/service-ticket-priority.ts @@ -0,0 +1,28 @@ +import { SrUrgency as CwServiceTicketPriority } from "../../generated/prisma/client"; +import { ServiceTicketPriority as ApiServiceTicketPriority } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const serviceTicketPriorityTranslation: Translation< + CwServiceTicketPriority, + ApiServiceTicketPriority +> = { + values: [ + { from: "srUrgencyRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Priority"), + }, + { from: "description", to: "description" }, + { from: "color", to: "color" }, + { + from: "defaultFlag", + to: "defaultFlag", + process: (value) => Boolean(value), + }, + { from: "createdBy", to: "createdById" }, + { from: "updatedBy", to: "updatedById" }, + { from: "dateCreatedUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/service-ticket-severity.ts b/dalpuri/src/translations/service-ticket-severity.ts new file mode 100644 index 0000000..1c72ef5 --- /dev/null +++ b/dalpuri/src/translations/service-ticket-severity.ts @@ -0,0 +1,27 @@ +import { SrSeverity as CwServiceTicketSeverity } from "../../generated/prisma/client"; +import { ServiceTicketSeverity as ApiServiceTicketSeverity } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const serviceTicketSeverityTranslation: Translation< + CwServiceTicketSeverity, + ApiServiceTicketSeverity +> = { + values: [ + { from: "srSeverityRecId", to: "id" }, + { + from: "severityName", + to: "name", + process: (value) => (value ? value : "Unknown Severity"), + }, + { from: "severityDesc", to: "description" }, + { + from: "defaultFlag", + to: "defaultFlag", + process: (value) => Boolean(value), + }, + { from: "enteredBy", to: "createdById" }, + { from: "updatedBy", to: "updatedById" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/service-ticket-source.ts b/dalpuri/src/translations/service-ticket-source.ts new file mode 100644 index 0000000..9f4c55e --- /dev/null +++ b/dalpuri/src/translations/service-ticket-source.ts @@ -0,0 +1,23 @@ +import { SrSource as CwServiceTicketSource } from "../../generated/prisma/client"; +import { ServiceTicketSource as ApiServiceTicketSource } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const serviceTicketSourceTranslation: Translation< + CwServiceTicketSource, + ApiServiceTicketSource +> = { + values: [ + { from: "srSourceRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Source"), + }, + { from: "description", to: "description" }, + { from: "defaultFlag", to: "defaultFlag" }, + { from: "enteredBy", to: "createdById" }, + { from: "updatedBy", to: "updatedById" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/service-ticket-type.ts b/dalpuri/src/translations/service-ticket-type.ts new file mode 100644 index 0000000..c211de1 --- /dev/null +++ b/dalpuri/src/translations/service-ticket-type.ts @@ -0,0 +1,23 @@ +import { SrType as CwServiceTicketType } from "../../generated/prisma/client"; +import { ServiceTicketType as ApiServiceTicketType } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const serviceTicketTypeTranslation: Translation< + CwServiceTicketType, + ApiServiceTicketType +> = { + values: [ + { from: "srTypeRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unknown Type"), + }, + { from: "description", to: "description" }, + { from: "inactiveFlag", to: "inactiveFlag" }, + { from: "enteredBy", to: "createdById" }, + { from: "updatedBy", to: "updatedById" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/service-ticket.ts b/dalpuri/src/translations/service-ticket.ts new file mode 100644 index 0000000..a9adc2a --- /dev/null +++ b/dalpuri/src/translations/service-ticket.ts @@ -0,0 +1,197 @@ +import { SrService as CwSrService } from "../../generated/prisma/client"; +import { ServiceTicket as ApiServiceTicket } from "../../../api/generated/prisma/client"; +import { Translation, skipRow } from "./types"; +import { TranslationContext } from "./context"; + +const toBillingMethod = ( + value: string | null | undefined +): "ACTUAL_RATES" | "FIXED_FEE" | "NOT_TO_EXCEED" | "OVERRIDE_RATE" => { + if (!value) return "ACTUAL_RATES"; + + const normalized = value.trim().toUpperCase(); + if (normalized === "A") return "ACTUAL_RATES"; + if (normalized === "F") return "FIXED_FEE"; + if (normalized === "N") return "NOT_TO_EXCEED"; + if (normalized === "O") return "OVERRIDE_RATE"; + + // Keyword fallback for unexpected legacy values. + if (normalized.includes("FIX")) return "FIXED_FEE"; + if (normalized.includes("EXCEED")) return "NOT_TO_EXCEED"; + if (normalized.includes("OVERRIDE")) return "OVERRIDE_RATE"; + + return "ACTUAL_RATES"; +}; + +const toBillingType = (value: string | undefined): "STANDARD" | "PROJECT" => { + if (!value) return "STANDARD"; + const normalized = value.trim().toUpperCase(); + if (normalized.includes("PROJECT")) return "PROJECT"; + return "STANDARD"; +}; + +const resolveUserId = ( + value: string | null | undefined, + context: TranslationContext +): string | null => { + if (!value) return null; + + const byIdentifier = context.usersByIdentifier.get(value); + if (byIdentifier) return byIdentifier; + + const asRecId = Number.parseInt(value, 10); + if (!Number.isNaN(asRecId)) { + return context.usersByMemberRecId.get(asRecId) ?? null; + } + + return null; +}; + +export const serviceTicketTranslation: Translation< + CwSrService, + ApiServiceTicket, + TranslationContext +> = { + values: [ + { from: "srServiceRecId", to: "id" }, + { + from: "summary", + to: "summary", + process: (value) => { + if (!value || value.trim() === "") { + skipRow( + "ServiceTicket summary is required and cannot be empty" + ); + } + return value; + }, + }, + { + from: "srLocationRecId", + to: "locationId", + process: (value) => { + if (value == null) { + skipRow("ServiceTicket locationId missing"); + } + return value; + }, + }, + { + from: "srSourceRecId", + to: "sourceId", + process: (value) => { + if (value == null) { + skipRow("ServiceTicket sourceId missing"); + } + return value; + }, + }, + { + from: "srUrgencyRecId", + to: "priorityId", + }, + { + from: "srSeverityRecId", + to: "severityId", + process: (value) => { + if (value == null) { + skipRow("ServiceTicket severityId missing"); + } + return value; + }, + }, + { + from: "srImpactRecId", + to: "impactId", + process: (value) => { + if (value == null) { + skipRow("ServiceTicket impactId missing"); + } + return value; + }, + }, + { + from: "srBoardRecId", + to: "serviceTicketBoardId", + process: (value) => (value == null ? null : value), + }, + { from: "companyRecId", to: "companyId" }, + { from: "contactRecId", to: "contactId" }, + { from: "companyAddressRecId", to: "companyAddressId" }, + { from: "billingCompanyRecId", to: "billingCompanyId" }, + { from: "billingAddressRecId", to: "billingAddressId" }, + { + from: "ticketOwnerRecId", + to: "ticketOwnerId", + process: (value, context) => { + if (!value) return null; + return context.usersByMemberRecId.get(value) || null; + }, + }, + { + from: "enteredBy", + to: "createdById", + process: (value, context) => { + return resolveUserId(value, context); + }, + }, + { + from: "updatedBy", + to: "updatedById", + process: (value, context) => { + return resolveUserId(value, context); + }, + }, + { + from: "closedBy", + to: "closedById", + process: (value, context) => { + return resolveUserId(value, context); + }, + }, + { + from: "dateEnteredUtc", + to: "createdAt", + process: (value) => value || null, + }, + { + from: "lastUpdatedUtc", + to: "updatedAt", + process: (value) => value || null, + }, + { from: "dateClosedUtc", to: "closedAt" }, + { + from: "isClosedFlag", + to: "closedFlag", + }, + { + from: "billMethod", + to: "billingMethod", + process: (value, context) => { + return toBillingMethod(value); + }, + }, + { + from: "srServiceRecId", + to: "billingType", + process: (value, context) => { + return toBillingType(context.billingTypeByTicketId.get(value) ?? undefined); + }, + }, + { + from: "srServiceRecId", + to: "billingInstructions", + process: (value, context) => { + return context.billingInstructionsByTicketId.get(value) ?? null; + }, + }, + { from: "poNumber", to: "poNumber" }, + { + from: "billingAmount", + to: "billingAmount", + process: (value) => (value == null ? 0 : Number(value)), + }, + { from: "rejectedFlag", to: "rejectedFlag" }, + { from: "publishFlag", to: "publishFlag" }, + { from: "redFlag", to: "redFlag" }, + ], +}; diff --git a/dalpuri/src/translations/tax-code.ts b/dalpuri/src/translations/tax-code.ts new file mode 100644 index 0000000..7534837 --- /dev/null +++ b/dalpuri/src/translations/tax-code.ts @@ -0,0 +1,47 @@ +import { + TaxCode as CwTaxCode, + TaxCodeLevel as CwTaxCodeLevel, +} from "../../generated/prisma/client"; +import { TaxCode as ApiTaxCode } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; +import { skipRow } from "./types"; + +type CwTaxCodeWithLevels = CwTaxCode & { + levels: CwTaxCodeLevel[]; +}; + +export const taxCodeTranslation: Translation< + CwTaxCodeWithLevels, + ApiTaxCode +> = { + values: [ + { from: "taxCodeRecId", to: "id" }, + { + from: "taxCodeId", + to: "code", + process: (value) => value ?? skipRow("taxCodeId is null"), + }, + { + from: "codeCaption", + to: "codeCaption", + process: (value, _, row) => + value ?? (row.taxCodeId as string | null) ?? "", + }, + { from: "description", to: "description" }, + { + from: "taxCodeRecId", + to: "rate", + process: (_, __, row) => { + const level = (row as unknown as CwTaxCodeWithLevels).levels.find( + (l) => l.taxXref !== null + ); + return level ? Number(level.taxRate) : null; + }, + }, + { from: "defaultFlag", to: "defaultFlag" }, + { from: "enteredBy", to: "createdBy" }, + { from: "updatedBy", to: "updatedBy" }, + { from: "dateEnteredUtc", to: "createdAt" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/types.ts b/dalpuri/src/translations/types.ts index fc7cb88..5a14b83 100644 --- a/dalpuri/src/translations/types.ts +++ b/dalpuri/src/translations/types.ts @@ -1,18 +1,56 @@ /** * kF - keys From * kT - keys To + * kC - extra context data available to process functions */ -export type TranslationEntry = { +export type TranslationEntry = { from: keyof kF; to: keyof kT; -} & (kF extends kT - ? { - process?: (value: kF[keyof kF]) => kT[keyof kT]; - } - : { - process: (value: kF[keyof kF]) => kT[keyof kT]; - }); +} & { + [kFrom in keyof kF]: { + [kTo in keyof kT]: { + from: kFrom; + to: kTo; + } & (kF[kFrom] extends kT[kTo] + ? { + process?: (value: kF[kFrom], context: kC, row: kF) => kT[kTo]; + } + : { + process: (value: kF[kFrom], context: kC, row: kF) => kT[kTo]; + }); + }[keyof kT]; +}[keyof kF]; -export interface Translation { - values: TranslationEntry[]; +export interface Translation { + values: TranslationEntry[]; +} + +/** + * Represents the outcome of translating a single row. + * - ok: true → translation succeeded, `value` contains the translated row + * - ok: false → row should be skipped, `reason` explains why + */ +export type TranslationResult = + | { ok: true; value: T } + | { ok: false; reason: string }; + +/** + * Sentinel class thrown by `skipRow()` inside process functions. + * Caught by `translateRow()` and converted to a `TranslationResult`. + * This avoids string-prefix matching on generic Error messages. + */ +export class SkipRowError extends Error { + readonly __brand = "SkipRowError" as const; + constructor(reason: string) { + super(reason); + this.name = "SkipRowError"; + } +} + +/** + * Convenience helper for process functions to signal that a row + * should be skipped. Preferred over `throw new Error("SKIP_ROW: …")`. + */ +export function skipRow(reason: string): never { + throw new SkipRowError(reason); } diff --git a/dalpuri/src/translations/user.ts b/dalpuri/src/translations/user.ts new file mode 100644 index 0000000..1288b9a --- /dev/null +++ b/dalpuri/src/translations/user.ts @@ -0,0 +1,76 @@ +import { Member as CwMember } from "../../generated/prisma/client"; +import { User as ApiUser } from "../../../api/generated/prisma/client"; +import { Translation, skipRow } from "./types"; + +const isValidEmail = (value: string | null): boolean => { + const normalized = value?.trim().toLowerCase(); + return !!normalized && normalized.includes("@"); +}; + +const isFullMember = (row: CwMember): boolean => + row.memberClass === "F" && isValidEmail(row.emailAddress); + +/** + * For full members, use their real email. + * For hidden members (non-F or missing valid email), generate a stable + * placeholder that satisfies the unique non-null login/email constraints. + */ +const resolveEmail = (row: CwMember): string => { + if (isFullMember(row)) { + return row.emailAddress!.trim().toLowerCase(); + } + return `${row.memberId}@cw.local`; +}; + +export const userTranslation: Translation = { + values: [ + { + from: "memberId", + to: "login", + // Use memberId@cw.local as a stable, unique login for all synced members. + // The real email goes into `email`; Microsoft OAuth overwrites `login` with + // the UPN on first login so this value is only a placeholder. + process: (value) => `${value}@cw.local`, + }, + { + from: "firstName", + to: "firstName", + process: (value) => (value && value.trim() ? value.trim() : "Unknown"), + }, + { + from: "lastName", + to: "lastName", + process: (value) => (value && value.trim() ? value.trim() : "Unknown"), + }, + { + from: "emailAddress", + to: "email", + process: (_value, _context, row) => resolveEmail(row), + }, + { from: "memberRecId", to: "cwMemberId" }, + { + from: "memberId", + to: "cwIdentifier", + process: (value) => { + const normalized = value?.trim(); + return normalized ? normalized : null; + }, + }, + { + from: "inactiveFlag", + to: "active", + process: (value) => !value, + }, + { + from: "memberClass", + to: "hidden", + process: (_value, _context, row) => !isFullMember(row), + }, + { + from: "dateHire", + to: "createdAt", + process: (value, _context, row) => value ?? row.lastUpdatedUtc, + }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + ], +}; diff --git a/dalpuri/src/translations/warehouse-bin.ts b/dalpuri/src/translations/warehouse-bin.ts new file mode 100644 index 0000000..1f76cd5 --- /dev/null +++ b/dalpuri/src/translations/warehouse-bin.ts @@ -0,0 +1,42 @@ +import { WarehouseBin as CwWarehouseBin } from "../../generated/prisma/client"; +import { WarehouseBin as ApiWarehouseBin } from "../../../api/generated/prisma/client"; +import { Translation } from "./types"; + +export const warehouseBinTranslation: Translation< + CwWarehouseBin, + ApiWarehouseBin +> = { + values: [ + { from: "warehouseBinRecId", to: "id" }, + { + from: "description", + to: "name", + process: (value) => (value ? value : "Unlabeled Bin"), + }, + { from: "description", to: "description" }, + { + from: "minQuantity", + to: "minQuantity", + process: (value) => (value == null ? 0 : Number(value)), + }, + { + from: "maxQuantity", + to: "maxQuantity", + process: (value) => (value == null ? 0 : Number(value)), + }, + { + from: "inactiveFlag", + to: "inactiveFlag", + process: (value) => Boolean(value), + }, + { + from: "defaultFlag", + to: "defaultFlag", + process: (value) => Boolean(value), + }, + { from: "updatedBy", to: "updatedById" }, + { from: "enteredBy", to: "createdById" }, + { from: "lastUpdatedUtc", to: "updatedAt" }, + { from: "dateEnteredUtc", to: "createdAt" }, + ], +}; diff --git a/dalpuri/temp-check-config-udf.ts b/dalpuri/temp-check-config-udf.ts new file mode 100644 index 0000000..46c0430 --- /dev/null +++ b/dalpuri/temp-check-config-udf.ts @@ -0,0 +1,76 @@ +import { PrismaMssql } from "@prisma/adapter-mssql"; +import { PrismaClient } from "./generated/prisma/client"; + +const connectionString = process.env.DATABASE_URL; +if (!connectionString) { + throw new Error("DATABASE_URL is not set."); +} + +const prisma = new PrismaClient({ + adapter: new PrismaMssql(connectionString), +}); + +try { + const rowSummary = await prisma.$queryRawUnsafe< + Array<{ total_rows: number; distinct_configs: number }> + >(` + SELECT + COUNT(*) AS total_rows, + COUNT(DISTINCT Config_RecID) AS distinct_configs + FROM dbo.Config_User_Defined_Field_Value; + `); + + const relatedRowCounts = await prisma.$queryRawUnsafe< + Array<{ + config_rows: number; + cs_result_detail_rows: number; + config_custom_field_nonempty: number; + }> + >(` + SELECT + (SELECT COUNT(*) FROM dbo.Config) AS config_rows, + (SELECT COUNT(*) FROM dbo.CS_Result_Detail) AS cs_result_detail_rows, + (SELECT COUNT(*) + FROM dbo.Config + WHERE Custom_Field IS NOT NULL + AND LEN(LTRIM(RTRIM(CONVERT(nvarchar(max), Custom_Field)))) > 0) AS config_custom_field_nonempty; + `); + + const topConfigs = await prisma.$queryRawUnsafe< + Array<{ config_recid: number; field_count: number }> + >(` + SELECT TOP 10 + Config_RecID AS config_recid, + COUNT(*) AS field_count + FROM dbo.Config_User_Defined_Field_Value + GROUP BY Config_RecID + ORDER BY field_count DESC, config_recid ASC; + `); + + const customFieldSamples = await prisma.$queryRawUnsafe< + Array<{ config_recid: number; custom_field_prefix: string }> + >(` + SELECT TOP 5 + Config_RecID AS config_recid, + LEFT(CONVERT(nvarchar(max), Custom_Field), 250) AS custom_field_prefix + FROM dbo.Config + WHERE Custom_Field IS NOT NULL + AND LEN(LTRIM(RTRIM(CONVERT(nvarchar(max), Custom_Field)))) > 0 + ORDER BY Config_RecID ASC; + `); + + console.log( + JSON.stringify( + { + rowSummary: rowSummary[0] ?? null, + relatedRowCounts: relatedRowCounts[0] ?? null, + topConfigs, + customFieldSamples, + }, + null, + 2, + ), + ); +} finally { + await prisma.$disconnect(); +} diff --git a/dalpuri/temp-export-configuration.ts b/dalpuri/temp-export-configuration.ts new file mode 100644 index 0000000..54d6665 --- /dev/null +++ b/dalpuri/temp-export-configuration.ts @@ -0,0 +1,43 @@ +import { PrismaMssql } from "@prisma/adapter-mssql"; +import { PrismaClient } from "./generated/prisma/client"; +import { writeFileSync } from "node:fs"; + +const outputPath = + process.argv[2] ?? + process.env.CONFIG_OUTPUT_FILE ?? + "configurations-first-10-with-relations.json"; + +const connectionString = process.env.DATABASE_URL; +if (!connectionString) { + throw new Error("DATABASE_URL is not set."); +} + +const adapter = new PrismaMssql(connectionString); +const prisma = new PrismaClient({ adapter }); + +try { + const configurations = await prisma.configuration.findMany({ + take: 10, + orderBy: { configRecId: "asc" }, + include: { + configStatus: true, + configurationAudits: { + orderBy: { lastUpdatedUtc: "desc" }, + include: { + configurationValues: { + orderBy: { configurationAuditValueRecId: "asc" }, + }, + }, + }, + }, + }); + + if (configurations.length === 0) { + console.error("No configurations found."); + process.exit(1); + } + + writeFileSync(outputPath, JSON.stringify(configurations, null, 2)); +} finally { + await prisma.$disconnect(); +} diff --git a/dalpuri/temp-find-current-config-value-store.ts b/dalpuri/temp-find-current-config-value-store.ts new file mode 100644 index 0000000..848bde2 --- /dev/null +++ b/dalpuri/temp-find-current-config-value-store.ts @@ -0,0 +1,150 @@ +import { PrismaMssql } from "@prisma/adapter-mssql"; +import { Prisma, PrismaClient } from "./generated/prisma/client"; + +const connectionString = process.env.DATABASE_URL; +if (!connectionString) { + throw new Error("DATABASE_URL is not set."); +} + +const adapter = new PrismaMssql(connectionString); +const prisma = new PrismaClient({ adapter }); + +type CandidateTable = { table_name: string }; +type CandidateColumn = { table_name: string; column_name: string }; + +type DmmfField = { + name: string; + dbName: string | null; +}; + +type DmmfModel = { + name: string; + dbName: string | null; + fields: DmmfField[]; +}; + +const TABLE_PATTERN = /config|configur/i; +const VALUE_COLUMN_PATTERN = /value|field|question|token/i; +const TOP_CONFIG_LIMIT = 150; +const CONFIG_KEY_COLUMNS = new Set([ + "Config_RecID", + "Configuration_RecID", + "Configuration_RecId", +]); + +function byName(a: string, b: string) { + return a.localeCompare(b); +} + +try { + const models = Prisma.dmmf.datamodel.models as unknown as DmmfModel[]; + + const configModels = models + .map((model) => ({ + model, + tableName: model.dbName ?? model.name, + })) + .filter(({ tableName }) => TABLE_PATTERN.test(tableName)); + + const candidateTables: CandidateTable[] = configModels + .map(({ tableName }) => ({ table_name: tableName })) + .sort((a, b) => byName(a.table_name, b.table_name)); + + const candidateColumns: CandidateColumn[] = configModels + .flatMap(({ model, tableName }) => + model.fields + .map((field) => field.dbName ?? field.name) + .filter((columnName) => VALUE_COLUMN_PATTERN.test(columnName)) + .map((columnName) => ({ + table_name: tableName, + column_name: columnName, + })) + ) + .sort( + (a, b) => + byName(a.table_name, b.table_name) || + byName(a.column_name, b.column_name) + ); + + const valueTablesWithConfigKey: CandidateTable[] = configModels + .filter(({ model }) => { + const columnNames = model.fields.map( + (field) => field.dbName ?? field.name + ); + const hasConfigKey = columnNames.some((column) => + CONFIG_KEY_COLUMNS.has(column) + ); + const hasValueLikeColumn = columnNames.some((column) => + VALUE_COLUMN_PATTERN.test(column) + ); + return hasConfigKey && hasValueLikeColumn; + }) + .map(({ tableName }) => ({ table_name: tableName })) + .sort((a, b) => byName(a.table_name, b.table_name)); + + const [ + configRows, + auditRows, + auditValueRows, + nonNullCustomFields, + groupedAuditTokens, + topConfigs, + ] = await prisma.$transaction([ + prisma.configuration.count(), + prisma.configurationAudit.count(), + prisma.configurationAuditValue.count(), + prisma.configuration.findMany({ + where: { customField: { not: null } }, + select: { customField: true }, + }), + prisma.configurationAuditValue.groupBy({ + by: ["auditToken"], + _count: true, + orderBy: [{ _count: { auditToken: "desc" } }, { auditToken: "asc" }], + take: 20, + }), + prisma.configuration.findMany({ + take: TOP_CONFIG_LIMIT, + orderBy: { configRecId: "asc" }, + include: { + configurationAudits: { + orderBy: { configurationAuditRecId: "asc" }, + include: { + configurationValues: { + orderBy: { configurationAuditValueRecId: "asc" }, + }, + }, + }, + }, + }), + ]); + + const configCustomFieldNonempty = nonNullCustomFields.reduce((count, row) => { + return row.customField?.trim() ? count + 1 : count; + }, 0); + + const rowStats = { + config_rows: configRows, + config_custom_field_nonempty: configCustomFieldNonempty, + audit_rows: auditRows, + audit_value_rows: auditValueRows, + }; + + const topAuditTokens = groupedAuditTokens.map(({ auditToken, _count }) => ({ + audit_token: auditToken, + row_count: _count, + })); + + const output = { + candidateTables, + candidateColumns, + valueTablesWithConfigKey, + rowStats, + topAuditTokens, + topConfigs, + }; + + console.log(JSON.stringify(output, null, 2)); +} finally { + await prisma.$disconnect(); +} diff --git a/dalpuri/tmp-diagnose-ticket-note-sync-gaps.ts b/dalpuri/tmp-diagnose-ticket-note-sync-gaps.ts new file mode 100644 index 0000000..89bf5ec --- /dev/null +++ b/dalpuri/tmp-diagnose-ticket-note-sync-gaps.ts @@ -0,0 +1,334 @@ +import { PrismaMssql } from "@prisma/adapter-mssql"; +import { PrismaPg } from "@prisma/adapter-pg"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +import { PrismaClient as CwPrismaClient } from "./generated/prisma/client"; +import { PrismaClient as ApiPrismaClient } from "../api/generated/prisma/client"; + +type EnvMap = Record; + +type Summary = { + cwTotal: number; + apiTotal: number; + eligibleForSync: number; + missingSrServiceRecId: number; + missingMemberRecId: number; + missingParentTicketInApi: number; + missingAuthorMappingInApi: number; + eligibleButMissingInApi: number; + sampleMissingParentTicketIds: number[]; + sampleMissingAuthorMemberRecIds: number[]; + sampleEligibleButMissingNoteIds: number[]; + topMissingAuthorMemberRecIds: Array<{ memberRecId: number; count: number }>; + topMissingParentTicketIds: Array<{ srServiceRecId: number; count: number }>; + automateApi: { + total: number; + eligibleForSync: number; + missingSrServiceRecId: number; + missingMemberRecId: number; + missingParentTicketInApi: number; + missingAuthorMappingInApi: number; + eligibleButMissingInApi: number; + sampleMemberRecIds: number[]; + sampleNoteIdsMissingInApi: number[]; + }; +}; + +const isAutomateApiAuthor = ( + createdBy: string | null, + originalAuthor: string | null +): boolean => { + const normalizedCreatedBy = createdBy?.trim().toLowerCase() ?? ""; + const normalizedOriginalAuthor = originalAuthor?.trim().toLowerCase() ?? ""; + return ( + normalizedCreatedBy.includes("automateapi") || + normalizedOriginalAuthor.includes("automateapi") + ); +}; + +const parseEnvFile = (path: string): EnvMap => { + const envData = readFileSync(path, "utf8"); + const out: EnvMap = {}; + + for (const rawLine of envData.split(/\r?\n/)) { + const line = rawLine.trim(); + if (!line || line.startsWith("#")) continue; + + const index = line.indexOf("="); + if (index <= 0) continue; + + const key = line.slice(0, index).trim(); + let value = line.slice(index + 1).trim(); + + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + + out[key] = value; + } + + return out; +}; + +const readApiEnv = (): EnvMap => { + const candidates = [ + resolve(import.meta.dir, "../api/.env"), + resolve(process.cwd(), "../api/.env"), + resolve(process.cwd(), "api/.env"), + ]; + + for (const candidate of candidates) { + try { + return parseEnvFile(candidate); + } catch { + // Try next + } + } + + return {}; +}; + +const main = async (): Promise => { + const apiEnv = readApiEnv(); + + const cwDatabaseUrl = + process.env.CW_DATABASE_URL || + process.env.DATABASE_URL || + apiEnv.CW_DATABASE_URL; + const apiDatabaseUrl = + process.env.API_DATABASE_URL || + process.env.OPTIMA_API_DATABASE_URL || + apiEnv.API_DATABASE_URL || + apiEnv.OPTIMA_API_DATABASE_URL || + apiEnv.DATABASE_URL; + + if (!cwDatabaseUrl) { + throw new Error("Missing CW DB URL. Set CW_DATABASE_URL or DATABASE_URL."); + } + + if (!apiDatabaseUrl) { + throw new Error( + "Missing API DB URL. Set API_DATABASE_URL/OPTIMA_API_DATABASE_URL or provide api/.env DATABASE_URL." + ); + } + + const cwPrisma = new CwPrismaClient({ + adapter: new PrismaMssql(cwDatabaseUrl), + }); + const apiPrisma = new ApiPrismaClient({ + adapter: new PrismaPg({ connectionString: apiDatabaseUrl }), + }); + + try { + console.log("[diag] Loading API reference sets..."); + const [apiNotes, apiTickets, apiUsers] = await Promise.all([ + apiPrisma.serviceTicketNote.findMany({ select: { id: true } }), + apiPrisma.serviceTicket.findMany({ select: { id: true } }), + apiPrisma.user.findMany({ select: { cwMemberId: true } }), + ]); + + const apiNoteIds = new Set(apiNotes.map((r) => r.id)); + const apiTicketIds = new Set(apiTickets.map((r) => r.id)); + const apiUserMemberIds = new Set( + apiUsers + .map((r) => r.cwMemberId) + .filter((v): v is number => Number.isInteger(v)) + ); + + console.log( + `[diag] API sets: notes=${apiNoteIds.size} tickets=${apiTicketIds.size} usersWithCwMemberId=${apiUserMemberIds.size}` + ); + + const cwTotal = await cwPrisma.ticketNote.count(); + const apiTotal = apiNoteIds.size; + + let missingSrServiceRecId = 0; + let missingMemberRecId = 0; + let missingParentTicketInApi = 0; + let missingAuthorMappingInApi = 0; + let eligibleForSync = 0; + let eligibleButMissingInApi = 0; + + let automateTotal = 0; + let automateEligibleForSync = 0; + let automateMissingSrServiceRecId = 0; + let automateMissingMemberRecId = 0; + let automateMissingParentTicketInApi = 0; + let automateMissingAuthorMappingInApi = 0; + let automateEligibleButMissingInApi = 0; + + const sampleMissingParentTicketIds: number[] = []; + const sampleMissingAuthorMemberRecIds: number[] = []; + const sampleEligibleButMissingNoteIds: number[] = []; + const automateSampleMemberRecIds: number[] = []; + const automateSampleNoteIdsMissingInApi: number[] = []; + const missingAuthorCounts = new Map(); + const missingParentCounts = new Map(); + + let cursor = 0; + const batchSize = 5000; + + console.log( + `[diag] Scanning CW TicketNote rows in batches of ${batchSize}...` + ); + + while (true) { + const batch = await cwPrisma.ticketNote.findMany({ + where: { + ticketNoteRecId: { + gt: cursor, + }, + }, + orderBy: { + ticketNoteRecId: "asc", + }, + select: { + ticketNoteRecId: true, + srServiceRecId: true, + memberRecId: true, + createdBy: true, + originalAuthor: true, + }, + take: batchSize, + }); + + if (batch.length === 0) break; + + for (const row of batch) { + const noteId = row.ticketNoteRecId; + const srServiceRecId = row.srServiceRecId; + const memberRecId = row.memberRecId; + const isAutomate = isAutomateApiAuthor( + row.createdBy, + row.originalAuthor + ); + + if (isAutomate) { + automateTotal++; + if ( + memberRecId && + automateSampleMemberRecIds.length < 20 && + !automateSampleMemberRecIds.includes(memberRecId) + ) { + automateSampleMemberRecIds.push(memberRecId); + } + } + + let blocked = false; + + if (!srServiceRecId) { + missingSrServiceRecId++; + if (isAutomate) { + automateMissingSrServiceRecId++; + } + blocked = true; + } else if (!apiTicketIds.has(srServiceRecId)) { + missingParentTicketInApi++; + if (isAutomate) { + automateMissingParentTicketInApi++; + } + blocked = true; + missingParentCounts.set( + srServiceRecId, + (missingParentCounts.get(srServiceRecId) ?? 0) + 1 + ); + if (sampleMissingParentTicketIds.length < 20) { + sampleMissingParentTicketIds.push(srServiceRecId); + } + } + + if (!memberRecId) { + missingMemberRecId++; + if (isAutomate) { + automateMissingMemberRecId++; + } + blocked = true; + } else if (!apiUserMemberIds.has(memberRecId)) { + missingAuthorMappingInApi++; + if (isAutomate) { + automateMissingAuthorMappingInApi++; + } + blocked = true; + missingAuthorCounts.set( + memberRecId, + (missingAuthorCounts.get(memberRecId) ?? 0) + 1 + ); + if (sampleMissingAuthorMemberRecIds.length < 20) { + sampleMissingAuthorMemberRecIds.push(memberRecId); + } + } + + if (!blocked) { + eligibleForSync++; + if (isAutomate) { + automateEligibleForSync++; + } + if (!apiNoteIds.has(noteId)) { + eligibleButMissingInApi++; + if (isAutomate) { + automateEligibleButMissingInApi++; + if (automateSampleNoteIdsMissingInApi.length < 20) { + automateSampleNoteIdsMissingInApi.push(noteId); + } + } + if (sampleEligibleButMissingNoteIds.length < 50) { + sampleEligibleButMissingNoteIds.push(noteId); + } + } + } + } + + cursor = batch[batch.length - 1]!.ticketNoteRecId; + if (cursor % 50000 < batchSize) { + console.log(`[diag] Progress cursor=${cursor}`); + } + } + + const summary: Summary = { + cwTotal, + apiTotal, + eligibleForSync, + missingSrServiceRecId, + missingMemberRecId, + missingParentTicketInApi, + missingAuthorMappingInApi, + eligibleButMissingInApi, + sampleMissingParentTicketIds, + sampleMissingAuthorMemberRecIds, + sampleEligibleButMissingNoteIds, + topMissingAuthorMemberRecIds: [...missingAuthorCounts.entries()] + .sort((a, b) => b[1] - a[1]) + .slice(0, 20) + .map(([memberRecId, count]) => ({ memberRecId, count })), + topMissingParentTicketIds: [...missingParentCounts.entries()] + .sort((a, b) => b[1] - a[1]) + .slice(0, 20) + .map(([srServiceRecId, count]) => ({ srServiceRecId, count })), + automateApi: { + total: automateTotal, + eligibleForSync: automateEligibleForSync, + missingSrServiceRecId: automateMissingSrServiceRecId, + missingMemberRecId: automateMissingMemberRecId, + missingParentTicketInApi: automateMissingParentTicketInApi, + missingAuthorMappingInApi: automateMissingAuthorMappingInApi, + eligibleButMissingInApi: automateEligibleButMissingInApi, + sampleMemberRecIds: automateSampleMemberRecIds, + sampleNoteIdsMissingInApi: automateSampleNoteIdsMissingInApi, + }, + }; + + console.log("[diag] TicketNote sync gap summary:"); + console.log(JSON.stringify(summary, null, 2)); + } finally { + await Promise.all([cwPrisma.$disconnect(), apiPrisma.$disconnect()]); + } +}; + +main().catch((error) => { + console.error("[diag] Failed:", error); + process.exit(1); +}); diff --git a/patches/@sveltejs__kit.patch b/patches/@sveltejs__kit.patch new file mode 100644 index 0000000..92f47e9 --- /dev/null +++ b/patches/@sveltejs__kit.patch @@ -0,0 +1,26 @@ +diff --git a/src/exports/vite/index.js b/src/exports/vite/index.js +index e23cf2b833fc0a04287d34328b97bf64c7916f0d..81b6f30f3a5f2c8d3d0337777e141e549c4a5f98 100644 +--- a/src/exports/vite/index.js ++++ b/src/exports/vite/index.js +@@ -473,7 +473,7 @@ Tips: + // for internal use only. it's published as $app/paths externally + // we use this alias so that we won't collide with user aliases + case sveltekit_paths: { +- const { assets, base } = svelte_config.kit.paths; ++ const { assets, base, relative } = svelte_config.kit.paths; + + // use the values defined in `global`, but fall back to hard-coded values + // for the sake of things like Vitest which may import this module +@@ -488,10 +488,10 @@ Tips: + + return dedent` + export let base = ${s(base)}; +- export let assets = ${assets ? s(assets) : 'base'}; ++ export let assets = ${relative ? "'.' + " : ''}${assets ? s(assets) : 'base'}; + export const app_dir = ${s(kit.appDir)}; + +- export const relative = ${svelte_config.kit.paths.relative}; ++ export const relative = ${relative}; + + const initial = { base, assets }; + diff --git a/ui/.claude/settings.local.json b/ui/.claude/settings.local.json new file mode 100644 index 0000000..93ff582 --- /dev/null +++ b/ui/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(npx svelte-check:*)" + ] + } +} diff --git a/ui/package.json b/ui/package.json index 4755df4..1c9c8f3 100644 --- a/ui/package.json +++ b/ui/package.json @@ -21,6 +21,7 @@ "package": "electron-forge package", "make": "electron-forge make", "make:macos": "electron-forge make --platform darwin", + "dev:server": "vite dev --host", "build:server": "vite build", "publish": "electron-forge publish" }, @@ -57,6 +58,7 @@ "vitest": "^3.2.4" }, "dependencies": { + "@event-calendar/core": "^5.6.0", "axios": "^1.13.3", "dotenv": "^17.2.3", "electron-squirrel-startup": "^1.0.1", diff --git a/ui/src/app.d.ts b/ui/src/app.d.ts index d04fe0b..68feb8a 100644 --- a/ui/src/app.d.ts +++ b/ui/src/app.d.ts @@ -1,5 +1,6 @@ // See https://svelte.dev/docs/kit/types#app.d.ts // for information about these interfaces + declare global { namespace App { // interface Error {} diff --git a/ui/src/components/AddProductModal.svelte b/ui/src/components/AddProductModal.svelte index 222b1a4..114b3d2 100644 --- a/ui/src/components/AddProductModal.svelte +++ b/ui/src/components/AddProductModal.svelte @@ -19,7 +19,7 @@ export let accessToken: string; export let opportunityId = ""; export let onSelect: ( - items: CatalogItem | CatalogItem[], + items: CatalogItem | CatalogItem[] ) => void | Promise = () => {}; // ── Step state ── @@ -179,11 +179,11 @@ $: subcategoryNameById = buildSubcategoryNameById(categories); $: categoryFilterLabel = resolveFilterLabel( filters.category, - categoryNameById, + categoryNameById ); $: subcategoryFilterLabel = resolveFilterLabel( filters.subcategory, - subcategoryNameById, + subcategoryNameById ); $: breadcrumb = buildBreadcrumb( @@ -192,7 +192,7 @@ selectedCategory, selectedGroup, selectedEcosystem, - filters, + filters ); $: specialOrderQty = Number(specialOrderForm.quantity); @@ -294,7 +294,7 @@ try { const response = await sales.fetchLaborOptions( accessToken, - opportunityId, + opportunityId ); laborOptions = response?.data ?? null; applyLaborDefaultsFromOptions(); @@ -313,7 +313,7 @@ _selectedCategory: CategoryTreeEntry | null, _selectedGroup: CategoryTreeEntry | null, _selectedEcosystem: EcosystemEntry | null, - _filters: CatalogItemFilters, + _filters: CatalogItemFilters ): string[] { const crumbs: string[] = ["Browse"]; if (_browseMode === "category" && _selectedCategory) { @@ -330,7 +330,7 @@ } function buildCategoryNameById( - entries: CategoryTreeEntry[], + entries: CategoryTreeEntry[] ): Map { const map = new Map(); for (const category of entries) { @@ -340,7 +340,7 @@ } function buildSubcategoryNameById( - entries: CategoryTreeEntry[], + entries: CategoryTreeEntry[] ): Map { const map = new Map(); for (const category of entries) { @@ -358,7 +358,7 @@ function resolveFilterLabel( value: CatalogItemFilters["category"] | CatalogItemFilters["subcategory"], - nameMap: Map, + nameMap: Map ): string { if (value == null || value === "") return ""; if (typeof value === "number") return nameMap.get(value) ?? String(value); @@ -379,20 +379,29 @@ $: filterSubcategories = collectSubcategories(selectedCategory); function collectSubcategories( - category: CategoryTreeEntry | null, + category: CategoryTreeEntry | null ): CategoryTreeEntry[] { if (!category?.entries) return []; const seen = new Set(); const options: CategoryTreeEntry[] = []; - for (const group of category.entries) { - for (const sub of group.subcategories ?? []) { - if (sub.cwId == null) continue; - const key = `${sub.cwId}`; + for (const entry of category.entries) { + if (entry.subcategories && entry.subcategories.length > 0) { + // Group entry — collect its nested subcategories + for (const sub of entry.subcategories) { + if (sub.cwId == null) continue; + const key = `${sub.cwId}`; + if (seen.has(key)) continue; + seen.add(key); + options.push(sub); + } + } else if (entry.cwId != null) { + // Direct subcategory entry + const key = `${entry.cwId}`; if (seen.has(key)) continue; seen.add(key); - options.push(sub); + options.push(entry); } } @@ -419,7 +428,7 @@ async function loadFilterValues( category?: string | number, - subcategory?: string | number, + subcategory?: string | number ) { if (!accessToken) return; isLoadingFilters = true; @@ -445,7 +454,7 @@ accessToken, page, filters, - rpp, + rpp ); console.log("[AddProductModal] Catalog fetch meta:", response?.meta); results = response?.data ?? []; @@ -472,6 +481,7 @@ function selectCategory(cat: CategoryTreeEntry) { selectedCategory = cat; + selectedEcosystem = null; browseMode = "category"; expandedGroupName = null; } @@ -499,6 +509,7 @@ // ── Navigation: Ecosystems ── function selectEcosystem(eco: EcosystemEntry) { + selectedCategory = null; browseMode = "ecosystem"; selectedEcosystem = eco; } @@ -2201,7 +2212,7 @@ >{formatPrice( Number.isFinite(laborUnitPrice) ? laborUnitPrice - : laborDefaultRate, + : laborDefaultRate )}/hr @@ -2228,11 +2239,11 @@ Default rates: Corporate {formatPrice( - laborRates.corporate, + laborRates.corporate )}/hr, Residential {formatPrice( - laborRates.residential, + laborRates.residential )}/hr. CPU defaults to {Math.round( - laborCpuMultiplier * 100, + laborCpuMultiplier * 100 )}% of selected rate. @@ -2307,7 +2318,7 @@ value={laborForm.pricePerUnit} on:input={(e) => handleLaborPpuInput( - (e.currentTarget as HTMLInputElement).value, + (e.currentTarget as HTMLInputElement).value )} /> @@ -2322,7 +2333,7 @@ value={laborForm.costPerUnit} on:input={(e) => handleLaborCpuInput( - (e.currentTarget as HTMLInputElement).value, + (e.currentTarget as HTMLInputElement).value )} /> @@ -2621,10 +2632,14 @@ >
- {item.description || + {item.name || + item.description || item.identifier || "Unknown Item"}
+ {#if item.identifier} +
{item.identifier}
+ {/if}
{#if item.manufacturer} openDetailPane(item)} > {item.description || item.identifier || "Item"}{item.name || item.description || item.identifier || "Item"}
{#if item.manufacturer} @@ -2901,7 +2916,7 @@ on:click={() => setItemQty( item.id, - (itemQuantities[item.id] ?? 1) - 1, + (itemQuantities[item.id] ?? 1) - 1 )} disabled={(itemQuantities[item.id] ?? 1) <= 1} aria-label="Decrease quantity">− setItemQty( item.id, - parseInt(e.currentTarget.value, 10), + parseInt(e.currentTarget.value, 10) )} /> @@ -2954,7 +2969,8 @@
{detailItem.description || + >{detailItem.name || + detailItem.description || detailItem.identifier || "Catalog Item"} @@ -2981,7 +2997,8 @@

- {detailItem.description || + {detailItem.name || + detailItem.description || detailItem.identifier || "Catalog Item"}

@@ -2991,6 +3008,22 @@
+ {#if detailItem.identifier} +
+ Product ID + {detailItem.identifier} +
+ {/if} + {#if detailItem.description} +
+ Customer Description + {detailItem.description} +
+ {/if} {#if detailItem.partNumber}
Part Number @@ -3007,14 +3040,6 @@ >
{/if} - {#if detailItem.identifier} -
- Identifier - {detailItem.identifier} -
- {/if} {#if detailItem.category}
Category @@ -3069,6 +3094,22 @@ {/if}
+
+ Product Narrative + {detailItem.productNarrative || "—"} +
+
+ Procurement Notes + {detailItem.procurementNotes || "—"} +
@@ -3084,7 +3125,7 @@ if (detailItem) setItemQty( detailItem.id, - (itemQuantities[detailItem.id] ?? 1) - 1, + (itemQuantities[detailItem.id] ?? 1) - 1 ); }} disabled={!detailItem || @@ -3103,7 +3144,7 @@ if (detailItem) setItemQty( detailItem.id, - parseInt(e.currentTarget.value, 10), + parseInt(e.currentTarget.value, 10) ); }} /> @@ -3114,7 +3155,7 @@ if (detailItem) setItemQty( detailItem.id, - (itemQuantities[detailItem.id] ?? 1) + 1, + (itemQuantities[detailItem.id] ?? 1) + 1 ); }} aria-label="Increase quantity">+ void = () => {}; + export let opportunityTypes: OpportunityType[] = []; // ── Form state ── let name = ""; @@ -36,6 +37,7 @@ let description = ""; let source = ""; let customerPO = ""; + let typeId = ""; let primarySalesRepId = ""; let secondarySalesRepId = ""; let companyId = ""; @@ -58,11 +60,35 @@ let companySearchInput = ""; let companySearchTimer: ReturnType; let showCompanyDropdown = false; + let companyHighlightIndex = -1; let navigateOnCreate = true; let companyInputWrapEl: HTMLDivElement; let dropdownStyle = ""; let primaryRepSelect: HTMLSelectElement; + // ── Opportunity types state ── + let loadedTypes: OpportunityType[] = []; + let isLoadingTypes = false; + $: resolvedTypes = opportunityTypes.length > 0 ? opportunityTypes : loadedTypes; + + async function loadOpportunityTypes() { + if (opportunityTypes.length > 0) return; + if (loadedTypes.length > 0) return; + isLoadingTypes = true; + try { + const json = await clientFetch<{ data: OpportunityType[] }>( + "/api/sales/opportunity-types" + ); + loadedTypes = json.data ?? []; + } catch { + loadedTypes = []; + } finally { + isLoadingTypes = false; + } + } + + $: if (isOpen) loadOpportunityTypes(); + const steps = [ { label: "Details", icon: "document" }, { label: "Assignment", icon: "people" }, @@ -91,7 +117,10 @@ $: if (isOpen && !expectedCloseDate) { const d = new Date(); d.setDate(d.getDate() + 30); - expectedCloseDate = d.toISOString().split("T")[0]; + const yyyy = d.getFullYear(); + const mm = String(d.getMonth() + 1).padStart(2, "0"); + const dd = String(d.getDate()).padStart(2, "0"); + expectedCloseDate = `${yyyy}-${mm}-${dd}`; } // --- Set default sales rep from logged-in user --- @@ -140,6 +169,7 @@ function handleCompanySearch() { clearTimeout(companySearchTimer); showCompanyDropdown = true; + companyHighlightIndex = -1; if (!companySearchInput.trim()) { companies = []; return; @@ -151,6 +181,7 @@ data: { id: string; name: string; cw_CompanyId?: number }[]; }>( `/api/companies/search?search=${encodeURIComponent(companySearchInput)}&rpp=10`, + ); companies = (json.data ?? []).map( (c: { id: string; name: string; cw_CompanyId?: number }) => ({ @@ -174,6 +205,7 @@ companyName = c.name; companySearchInput = c.name; showCompanyDropdown = false; + companyHighlightIndex = -1; companies = []; // Reset contact/site when company changes contacts = []; @@ -181,6 +213,29 @@ companyAddress = null; } + function handleCompanyKeydown(e: KeyboardEvent) { + if (!showCompanyDropdown || companies.length === 0) return; + if (e.key === "ArrowDown") { + e.preventDefault(); + companyHighlightIndex = + companyHighlightIndex < companies.length - 1 + ? companyHighlightIndex + 1 + : 0; + } else if (e.key === "ArrowUp") { + e.preventDefault(); + companyHighlightIndex = + companyHighlightIndex > 0 + ? companyHighlightIndex - 1 + : companies.length - 1; + } else if (e.key === "Enter" && companyHighlightIndex >= 0) { + e.preventDefault(); + selectCompany(companies[companyHighlightIndex]); + } else if (e.key === "Escape") { + showCompanyDropdown = false; + companyHighlightIndex = -1; + } + } + function clearCompany() { companyId = ""; companyOptimaId = ""; @@ -208,6 +263,7 @@ }; }>(`/api/companies/${companyOptimaId}/details`); const data = json.data; + console.log("[CreateOpportunityModal] Contact & Site data:", data); if (data) { const allContacts: CompanyContact[] = data.cw_Data?.allContacts ?? []; contacts = allContacts; @@ -278,6 +334,7 @@ if (description.trim()) body.notes = description.trim(); if (source.trim()) body.source = source.trim(); if (customerPO.trim()) body.customerPO = customerPO.trim(); + if (typeId) body.type = { id: Number(typeId) }; if (primarySalesRepId) body.primarySalesRep = { id: Number(primarySalesRepId) }; if (secondarySalesRepId) @@ -324,6 +381,7 @@ description = ""; source = ""; customerPO = ""; + typeId = ""; primarySalesRepId = ""; secondarySalesRepId = ""; companyId = ""; @@ -560,6 +618,21 @@ disabled={isSubmitting} />
+ +
+ + +
@@ -784,6 +857,7 @@ type="text" bind:value={companySearchInput} on:input={handleCompanySearch} + on:keydown={handleCompanyKeydown} on:focus={() => { if (companySearchInput.trim()) { showCompanyDropdown = true; @@ -822,10 +896,11 @@ {#if showCompanyDropdown && companies.length > 0}
    - {#each companies as c} + {#each companies as c, i}
  • + + +
    + + + + {#if currentOpp.cwOpportunityId} + + {/if} + + + {#if workflowLoading} +
    +
    + + Loading actions… +
    + {:else if workflowStatus && workflowStatus.isOptimaStage && !isTerminal && allActions.length > 0} +
    + + {#each nonFinalizeActions as action (action.action)} + + {/each} + {#if finalizeWonAction} + + {/if} + {#if finalizeLostAction} + + {/if} + {/if} + +{/if} + + +{#if activeModal && currentOpp} + {#if !isSpecialModal(activeModal)} + {@const action = allActions.find((a) => a.action === activeModal)} + {#if action} + submitAction(action.action, e.detail)} + on:close={closeModal} + /> + {/if} + {/if} + + {#if activeModal === "sendQuote" || activeModal === "resendQuote"} + submitAction(activeModal ?? "sendQuote", e.detail)} + on:close={closeModal} + /> + {/if} + + {#if activeModal === "reviewDecision"} + submitAction("reviewDecision", e.detail)} + on:sendQuote={() => { activeModal = "sendQuote"; }} + on:close={closeModal} + /> + {/if} + + {#if activeModal === "finalize"} + submitAction("finalize", e.detail)} + on:close={closeModal} + /> + {/if} +{/if} + + diff --git a/ui/src/event-calendar.d.ts b/ui/src/event-calendar.d.ts new file mode 100644 index 0000000..9dfef72 --- /dev/null +++ b/ui/src/event-calendar.d.ts @@ -0,0 +1,2 @@ +declare module "@event-calendar/core"; +declare module "@event-calendar/core/index.css"; diff --git a/ui/src/hooks.server.spec.ts b/ui/src/hooks.server.spec.ts index 2a85cd9..7b42641 100644 --- a/ui/src/hooks.server.spec.ts +++ b/ui/src/hooks.server.spec.ts @@ -9,6 +9,7 @@ const { mockApi, mockOptima, mockIsInvalidSignatureError, mockRedirect } = auth: {}, user: { isLoggedIn: vi.fn(), + isLoggedInServer: vi.fn(), logout: vi.fn(), refreshSession: vi.fn(), fetchInfo: vi.fn(), @@ -128,7 +129,7 @@ describe("hooks.server handle", () => { it("redirects to / when visiting /login while already logged in", async () => { mockApi.get.mockResolvedValueOnce({ status: 418 }); - mockOptima.user.isLoggedIn.mockReturnValue(true); + mockOptima.user.isLoggedInServer.mockReturnValue(true); const event = createMockEvent({ url: new URL("http://localhost/login"), }); @@ -147,7 +148,7 @@ describe("hooks.server handle", () => { it("resolves /login normally when user is not logged in", async () => { mockApi.get.mockResolvedValueOnce({ status: 418 }); - mockOptima.user.isLoggedIn.mockReturnValue(false); + mockOptima.user.isLoggedInServer.mockReturnValue(false); const event = createMockEvent({ url: new URL("http://localhost/login"), }); @@ -188,7 +189,7 @@ describe("hooks.server handle", () => { sub: "user-1", }; const encodedPayload = Buffer.from(JSON.stringify(payload)).toString( - "base64url", + "base64url" ); const fakeJwt = `header.${encodedPayload}.signature`; @@ -216,7 +217,7 @@ describe("hooks.server handle", () => { // Create an expired JWT const payload = { exp: Math.floor(Date.now() / 1000) - 100, sub: "u1" }; const encodedPayload = Buffer.from(JSON.stringify(payload)).toString( - "base64url", + "base64url" ); const fakeJwt = `h.${encodedPayload}.s`; @@ -241,7 +242,7 @@ describe("hooks.server handle", () => { expect(event.cookies.set).toHaveBeenCalledWith( "accessToken", "new-access", - { path: "/" }, + { path: "/" } ); expect(resolve).toHaveBeenCalledWith(event); }); @@ -252,7 +253,7 @@ describe("hooks.server handle", () => { mockApi.get.mockResolvedValueOnce({ status: 418 }); const payload = { exp: Math.floor(Date.now() / 1000) - 100, sub: "u1" }; const encodedPayload = Buffer.from(JSON.stringify(payload)).toString( - "base64url", + "base64url" ); const fakeJwt = `h.${encodedPayload}.s`; @@ -360,7 +361,7 @@ describe("hooks.server handle", () => { mockApi.get.mockResolvedValueOnce({ status: 418 }); mockIsInvalidSignatureError.mockReturnValue(false); mockOptima.user.refreshSession.mockRejectedValueOnce( - new Error("Refresh failed"), + new Error("Refresh failed") ); const event = createMockEvent({ @@ -395,7 +396,7 @@ describe("hooks.server handle", () => { .mockReturnValueOnce(false) .mockReturnValueOnce(true); mockOptima.user.refreshSession.mockRejectedValueOnce( - new Error("invalid signature"), + new Error("invalid signature") ); const event = createMockEvent({ @@ -415,7 +416,7 @@ describe("hooks.server handle", () => { } expect(warnSpy).toHaveBeenCalledWith( - "Invalid refresh token signature — forcing logout.", + "Invalid refresh token signature — forcing logout." ); expect(mockOptima.user.logout).toHaveBeenCalled(); warnSpy.mockRestore(); diff --git a/ui/src/hooks.server.ts b/ui/src/hooks.server.ts index 9191376..253490a 100644 --- a/ui/src/hooks.server.ts +++ b/ui/src/hooks.server.ts @@ -128,7 +128,10 @@ export const handle: Handle = async ({ event, resolve }) => { return redirect(303, "/login"); } - if (event.url.pathname.startsWith("/login") && optima.user.isLoggedIn()) { + if ( + event.url.pathname.startsWith("/login") && + optima.user.isLoggedInServer(event) + ) { return redirect(303, "/"); } @@ -149,7 +152,7 @@ export const handle: Handle = async ({ event, resolve }) => { try { const [, payload] = currentAccessToken.split("."); const decoded = JSON.parse( - Buffer.from(payload, "base64url").toString("utf8"), + Buffer.from(payload, "base64url").toString("utf8") ); const nowSec = Math.floor(Date.now() / 1000); const thresholdSec = 60; // refresh if < 60s remaining @@ -157,8 +160,9 @@ export const handle: Handle = async ({ event, resolve }) => { if (!decoded?.exp || decoded.exp - nowSec < thresholdSec) { // Token is expired or about to expire — try to refresh if (currentRefreshToken) { - const refreshed = - await optima.user.refreshSession(currentRefreshToken); + const refreshed = await optima.user.refreshSession( + currentRefreshToken + ); currentAccessToken = refreshed.accessToken; currentRefreshToken = refreshed.refreshToken ?? currentRefreshToken; } else { @@ -177,8 +181,9 @@ export const handle: Handle = async ({ event, resolve }) => { // Token is malformed or refresh failed — try refresh as fallback if (currentRefreshToken) { try { - const refreshed = - await optima.user.refreshSession(currentRefreshToken); + const refreshed = await optima.user.refreshSession( + currentRefreshToken + ); currentAccessToken = refreshed.accessToken; currentRefreshToken = refreshed.refreshToken ?? currentRefreshToken; } catch (refreshErr) { diff --git a/ui/src/lib/index.ts b/ui/src/lib/index.ts index 0564590..5e47c94 100644 --- a/ui/src/lib/index.ts +++ b/ui/src/lib/index.ts @@ -12,6 +12,7 @@ import { unifi } from "./optima-api/modules/unifi"; import { procurement } from "./optima-api/modules/procurement"; import { sales } from "./optima-api/modules/sales"; import { cw } from "./optima-api/modules/cw"; +import { schedule } from "./optima-api/modules/schedule"; export const optima = { auth, @@ -26,6 +27,7 @@ export const optima = { procurement, sales, cw, + schedule, }; /** * @TODO diff --git a/ui/src/lib/optima-api/modules/procurement.ts b/ui/src/lib/optima-api/modules/procurement.ts index 57eb6a0..78c32ef 100644 --- a/ui/src/lib/optima-api/modules/procurement.ts +++ b/ui/src/lib/optima-api/modules/procurement.ts @@ -48,6 +48,7 @@ export interface CatalogItem { id: string; identifier?: string; cwCatalogId?: number; + name?: string; description?: string; quantity?: number; partNumber?: string; diff --git a/ui/src/lib/optima-api/modules/sales.ts b/ui/src/lib/optima-api/modules/sales.ts index 3cbe913..4eb84ef 100644 --- a/ui/src/lib/optima-api/modules/sales.ts +++ b/ui/src/lib/optima-api/modules/sales.ts @@ -16,7 +16,7 @@ export interface SalesOpportunity { stage?: { id?: number; name?: string } | null; status?: { id?: number; name?: string } | null; priority?: { id?: number; name?: string } | null; - rating?: { id?: number; name?: string } | null; + interest?: "HOT" | "WARM" | "COLD" | null; source?: string | null; campaign?: string | null; primarySalesRep?: { @@ -76,6 +76,7 @@ export interface SalesOpportunity { customerPO?: string | null; totalSalesTax?: number | null; expectedSalesTaxRate?: number | null; + taxCodeDescription?: string | null; location?: { id?: number; name?: string } | null; department?: { id?: number; name?: string } | null; expectedCloseDate?: string | null; @@ -240,7 +241,7 @@ export const STATUS_ID_TO_KEY: Record = Object.entries(WORKFLOW_STATUS_IDS).map(([k, v]) => [ v, k as WorkflowStatusKey, - ]), + ]) ) as Record; /** Human-readable labels for each workflow status */ @@ -274,7 +275,7 @@ export const REOPENABLE_STATUSES: ReadonlySet = new Set([ /** Statuses where the quote has been confirmed (finalize is allowed) */ export const QUOTE_CONFIRMED_STATUSES: ReadonlySet = new Set( - ["ConfirmedQuote", "ReadyToSend", "Active", "PendingWon", "PendingLost"], + ["ConfirmedQuote", "ReadyToSend", "Active", "PendingWon", "PendingLost"] ); export interface OpportunityType { @@ -382,7 +383,7 @@ export interface CreateOpportunityBody { name: string; expectedCloseDate: string; notes?: string; - rating?: { id: number }; + interest?: "HOT" | "WARM" | "COLD" | null; type?: { id: number }; stage?: { id: number }; status?: { id: number }; @@ -519,10 +520,11 @@ export interface MemberSalesMetrics { export const sales = { async fetchMetrics(accessToken: string): Promise { const response = await api.get("/v1/sales/opportunities/metrics", { - params: { scope: "me" }, - headers: { Authorization: `Bearer ${accessToken}` }, + headers: { + Authorization: `Bearer ${accessToken}`, + }, }); - return response.data?.data?.metrics ?? null; + return response.data?.data ?? null; }, async fetchMe(accessToken: string, includeClosed: boolean = false) { @@ -542,7 +544,7 @@ export const sales = { page: number = 1, search: string = "", rpp: number = 30, - includeClosed: boolean = true, + includeClosed: boolean = true ) { const params: Record = { page, rpp }; if (search) params.search = search; @@ -578,7 +580,7 @@ export const sales = { async fetchOne( accessToken: string, identifier: string, - include?: ("notes" | "contacts" | "products" | "quotes")[], + include?: ("notes" | "contacts" | "products" | "quotes")[] ) { const params: Record = {}; if (include && include.length > 0) { @@ -591,43 +593,49 @@ export const sales = { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, async fetchForecasts(accessToken: string, identifier: string) { const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/forecasts`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/forecasts`, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, async fetchProducts(accessToken: string, identifier: string) { const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/products`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/products`, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, async fetchNotes(accessToken: string, identifier: string) { const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/notes`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/notes`, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -635,16 +643,18 @@ export const sales = { async createNote( accessToken: string, identifier: string, - data: { text: string; flagged?: boolean }, + data: { text: string; flagged?: boolean } ) { const response = await api.post( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/notes`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/notes`, data, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -653,40 +663,46 @@ export const sales = { accessToken: string, identifier: string, noteId: number, - data: { text?: string; flagged?: boolean }, + data: { text?: string; flagged?: boolean } ) { const response = await api.patch( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/notes/${noteId}`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/notes/${noteId}`, data, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, async deleteNote(accessToken: string, identifier: string, noteId: number) { const response = await api.delete( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/notes/${noteId}`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/notes/${noteId}`, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, async fetchContacts(accessToken: string, identifier: string) { const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/contacts`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/contacts`, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -694,16 +710,18 @@ export const sales = { async sequenceProducts( accessToken: string, identifier: string, - orderedIds: number[], + orderedIds: number[] ) { const response = await api.patch( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/products/sequence`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/products/sequence`, { orderedIds }, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -711,16 +729,18 @@ export const sales = { async addProduct( accessToken: string, identifier: string, - body: AddProductBody, + body: AddProductBody ) { const response = await api.post( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/products`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/products`, body, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -728,28 +748,32 @@ export const sales = { async addSpecialOrder( accessToken: string, identifier: string, - body: SpecialOrderBody | SpecialOrderBody[], + body: SpecialOrderBody | SpecialOrderBody[] ) { const response = await api.post( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/products/special-order`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/products/special-order`, body, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, async fetchLaborOptions(accessToken: string, identifier: string) { const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/products/labor/options`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/products/labor/options`, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data as { status?: number; @@ -761,13 +785,15 @@ export const sales = { async addLabor(accessToken: string, identifier: string, body: AddLaborBody) { const response = await api.post( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/products/labor`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/products/labor`, body, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -776,16 +802,18 @@ export const sales = { accessToken: string, identifier: string, productId: number, - body: EditOpportunityProductBody, + body: EditOpportunityProductBody ) { const response = await api.patch( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/products/${productId}/edit`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/products/${productId}/edit`, body, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -794,29 +822,33 @@ export const sales = { accessToken: string, identifier: string, productId: number, - body: CancelOpportunityProductBody, + body: CancelOpportunityProductBody ) { const response = await api.patch( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/products/${productId}/cancel`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/products/${productId}/cancel`, body, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, async refreshOpportunity(accessToken: string, identifier: string) { const response = await api.post( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/refresh`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/refresh`, {}, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -824,7 +856,7 @@ export const sales = { async updateOpportunity( accessToken: string, identifier: string, - body: Record, + body: Record ) { const response = await api.patch( `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}`, @@ -833,7 +865,7 @@ export const sales = { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -844,19 +876,21 @@ export const sales = { options?: { includeRegenData?: boolean; includeRegenParams?: boolean; - }, + } ) { const params: Record = {}; if (options?.includeRegenData) params.includeRegenData = "true"; if (options?.includeRegenParams) params.includeRegenParams = "true"; const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/quotes`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/quotes`, { params, headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data as { status?: number; @@ -873,16 +907,18 @@ export const sales = { lineItemPricing?: boolean; includeQuoteNarrative?: boolean; includeItemNarratives?: boolean; - }, + } ) { const response = await api.post( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/quote/commit`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/quote/commit`, body ?? {}, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data as { status?: number; @@ -905,14 +941,65 @@ export const sales = { }; }, - async previewQuote(accessToken: string, identifier: string, quoteId: string) { + async fetchQuoteNarrative(accessToken: string, identifier: string) { const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/quote/${encodeURIComponent(quoteId)}/preview`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/quotes/narrative`, { headers: { Authorization: `Bearer ${accessToken}`, }, + } + ); + return response.data as { + status?: number; + message?: string; + data: { + quoteNarrative: string | null; + }; + successful?: boolean; + }; + }, + + async updateQuoteNarrative( + accessToken: string, + identifier: string, + quoteNarrative: string | null + ) { + const response = await api.patch( + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/quotes/narrative`, + { + quoteNarrative, }, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + return response.data as { + status?: number; + message?: string; + data: { + quoteNarrative: string | null; + }; + successful?: boolean; + }; + }, + + async previewQuote(accessToken: string, identifier: string, quoteId: string) { + const response = await api.get( + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/quote/${encodeURIComponent(quoteId)}/preview`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } ); return response.data as { status?: number; @@ -929,16 +1016,18 @@ export const sales = { accessToken: string, identifier: string, quoteId: string, - fetchAction: "download" | "print", + fetchAction: "download" | "print" ) { const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/quote/${encodeURIComponent(quoteId)}/download`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/quote/${encodeURIComponent(quoteId)}/download`, { headers: { Authorization: `Bearer ${accessToken}`, }, params: { fetchAction }, - }, + } ); return response.data as { status?: number; @@ -955,12 +1044,14 @@ export const sales = { async fetchQuoteDownloads(accessToken: string, identifier: string) { const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/quotes/downloads`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/quotes/downloads`, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data as { status?: number; @@ -973,15 +1064,17 @@ export const sales = { async deleteProduct( accessToken: string, identifier: string, - productId: number, + productId: number ) { const response = await api.delete( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/products/${productId}`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/products/${productId}`, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -993,7 +1086,7 @@ export const sales = { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, @@ -1002,12 +1095,14 @@ export const sales = { async fetchWorkflowStatus(accessToken: string, identifier: string) { const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/workflow`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/workflow`, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data as { status?: number; @@ -1021,16 +1116,18 @@ export const sales = { accessToken: string, identifier: string, action: WorkflowAction, - payload: WorkflowActionPayload, + payload: WorkflowActionPayload ) { const response = await api.post( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/workflow`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/workflow`, { action, payload }, { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data as { status?: number; @@ -1043,18 +1140,20 @@ export const sales = { async fetchWorkflowHistory( accessToken: string, identifier: string, - type?: string, + type?: string ) { const params: Record = {}; if (type) params.type = type; const response = await api.get( - `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/workflow/history`, + `/v1/sales/opportunities/opportunity/${encodeURIComponent( + identifier + )}/workflow/history`, { params, headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data as { status?: number; diff --git a/ui/src/lib/optima-api/modules/schedule.ts b/ui/src/lib/optima-api/modules/schedule.ts new file mode 100644 index 0000000..d56ff86 --- /dev/null +++ b/ui/src/lib/optima-api/modules/schedule.ts @@ -0,0 +1,53 @@ +import api from "../axios"; + +export interface ScheduleStatus { + id: string; + name: string; + color: string | null; +} + +export interface ScheduleType { + id: string; + name: string; + displayColor: string | null; +} + +export interface ScheduleSpan { + id: string; + scheduleSpanId: number | null; + spanDesc: string | null; +} + +export interface ScheduleEntry { + id: string; + cwId: number; + memberId: string | null; + name: string; + description: string | null; + allDayFlag: boolean; + startDate: string | null; + endDate: string | null; + hoursScheduled: number | null; + status: ScheduleStatus | null; + type: ScheduleType | null; + scheduleSpan: ScheduleSpan | null; +} + +export const schedule = { + async fetchMe( + accessToken: string, + start: Date, + end: Date, + ): Promise { + const response = await api.get("/v1/schedule/@me", { + params: { + start: start.toISOString(), + end: end.toISOString(), + }, + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + return (response.data?.data as ScheduleEntry[]) ?? []; + }, +}; diff --git a/ui/src/lib/optima-api/modules/user.spec.ts b/ui/src/lib/optima-api/modules/user.spec.ts index c482fa9..53b6e7e 100644 --- a/ui/src/lib/optima-api/modules/user.spec.ts +++ b/ui/src/lib/optima-api/modules/user.spec.ts @@ -1,25 +1,23 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -const { mockGetRequestEvent, mockRedirect, mockAxiosPost, mockIo, mockApi } = - vi.hoisted(() => ({ - mockGetRequestEvent: vi.fn(), - mockRedirect: vi.fn(), - mockAxiosPost: vi.fn(), - mockIo: vi.fn(), - mockApi: { - get: vi.fn(), - post: vi.fn(), - }, - })); - -vi.mock("$app/server", () => ({ - getRequestEvent: mockGetRequestEvent, +const { mockRedirect, mockAxiosPost, mockIo, mockApi } = vi.hoisted(() => ({ + mockRedirect: vi.fn(), + mockAxiosPost: vi.fn(), + mockIo: vi.fn(), + mockApi: { + get: vi.fn(), + post: vi.fn(), + }, })); vi.mock("$env/static/public", () => ({ PUBLIC_API_URL: "https://api.example.com", })); +vi.mock("$app/environment", () => ({ + browser: false, +})); + vi.mock("@sveltejs/kit", () => ({ redirect: mockRedirect, })); @@ -45,24 +43,30 @@ describe("user module", () => { vi.clearAllMocks(); }); - it("isLoggedIn returns true when accessToken cookie exists", () => { - mockGetRequestEvent.mockReturnValueOnce({ + it("isLoggedInServer returns true when accessToken cookie exists", () => { + const mockEvent = { cookies: { get: vi.fn().mockReturnValue("token"), }, - }); + } as any; - expect(user.isLoggedIn()).toBe(true); + expect(user.isLoggedInServer(mockEvent)).toBe(true); }); - it("isLoggedIn returns false when accessToken cookie is missing", () => { - mockGetRequestEvent.mockReturnValueOnce({ + it("isLoggedInServer returns false when accessToken cookie is missing", () => { + const mockEvent = { cookies: { get: vi.fn().mockReturnValue(undefined), }, - }); + } as any; - expect(user.isLoggedIn()).toBe(false); + expect(user.isLoggedInServer(mockEvent)).toBe(false); + }); + + it("isLoggedIn throws an error when called without event", () => { + expect(() => user.isLoggedIn()).toThrow( + "user.isLoggedIn() should not be called without a request event. Use isLoggedInServer() instead." + ); }); it("refreshSession posts refresh header and returns token payload", async () => { @@ -84,7 +88,7 @@ describe("user module", () => { headers: { "x-refresh-token": "refresh-123", }, - }, + } ); expect(result).toEqual({ accessToken: "new-access", @@ -105,7 +109,7 @@ describe("user module", () => { expect(mockApi.post).toHaveBeenCalledWith( "/v1/user/@me/check-permission", { permissions: ["company.read"] }, - { headers: { Authorization: "Bearer token" } }, + { headers: { Authorization: "Bearer token" } } ); }); diff --git a/ui/src/lib/optima-api/modules/user.ts b/ui/src/lib/optima-api/modules/user.ts index 7a6686d..db5c6b0 100644 --- a/ui/src/lib/optima-api/modules/user.ts +++ b/ui/src/lib/optima-api/modules/user.ts @@ -1,13 +1,31 @@ -import { getRequestEvent } from "$app/server"; import { PUBLIC_API_URL } from "$env/static/public"; -import { redirect, RequestEvent } from "@sveltejs/kit"; +import { redirect, type RequestEvent } from "@sveltejs/kit"; import axios from "axios"; import api from "../axios"; import { io } from "socket.io-client"; +import { browser } from "$app/environment"; export const user = { isLoggedIn(): boolean { - const event = getRequestEvent(); + // This function can't reliably check authentication status in client-side code + // since it doesn't have access to httpOnly cookies. + // It should be used only in server-side contexts like load functions. + if (browser) { + console.warn( + "user.isLoggedIn() called in browser context - this may not work as expected" + ); + return false; + } + + // In server context, this would need the request event to be passed in + // Rather than using getRequestEvent() which makes this module server-only + throw new Error( + "user.isLoggedIn() should not be called without a request event. Use isLoggedInServer() instead." + ); + }, + + // Server-side version that takes a request event parameter + isLoggedInServer(event: RequestEvent): boolean { const authToken = event.cookies.get("accessToken"); return !!authToken; }, @@ -21,7 +39,7 @@ export const user = { headers: { "x-refresh-token": refreshToken, }, - }, + } ) ).data.data; @@ -55,96 +73,40 @@ export const user = { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, /** - * @todo Get communication with server working and setup a key system so that the frontend can listen for a specific key from the backend so that nobody can poach off of login events. - * - * Note: This function no longer mutates SvelteKit request event/cookies asynchronously. - * It returns the tokens to the caller so the caller (within the same request lifecycle) - * can set cookies using the event object synchronously. + * Polls the API until the Microsoft OAuth callback has been processed and + * tokens are available, then returns them. Uses a simple HTTP long-poll + * pattern instead of socket.io — much more reliable through reverse proxies. */ - async awaitAuthCallback(callbackKey: string): Promise<{ + async awaitAuthCallback(callbackKey: string, socketUrl?: string): Promise<{ accessToken: string; refreshToken: string; }> { - const base = PUBLIC_API_URL || ""; + const base = socketUrl ?? PUBLIC_API_URL ?? ""; + const pollUrl = `${base}/v1/auth/callback/${encodeURIComponent(callbackKey)}`; + const timeoutMs = 5 * 60 * 1000; // 5 minutes + const intervalMs = 600; + const deadline = Date.now() + timeoutMs; - return new Promise((resolve, reject) => { - let settled = false; - const socket = io(`${base}/auth_callback`, { - transports: ["websocket"], - rejectUnauthorized: false, - }); - const timeout = setTimeout( - () => { - if (settled) return; - settled = true; - try { - socket.disconnect(); - } catch {} - reject(new Error("Timed out waiting for auth callback")); - }, - 5 * 60 * 1000, - ); // 5 minutes - - const handlePayload = (payload: any) => { - try { - const { accessToken, refreshToken } = payload ?? {}; - if (accessToken && refreshToken) { - if (settled) return; - settled = true; - clearTimeout(timeout); - try { - socket.disconnect(); - } catch {} - resolve({ accessToken, refreshToken }); - } - } catch { - // ignore parse errors - } - }; - - socket.on("connect", () => {}); - // listen for a specific callback key if provided - if (callbackKey) { - socket.on(`auth:login:callback:${callbackKey}`, handlePayload); - } else { - socket.on("auth-callback", handlePayload); + while (Date.now() < deadline) { + const res = await fetch(pollUrl); + if (res.status === 200) { + const body = await res.json() as { data: { accessToken: string; refreshToken: string } }; + return body.data; } - socket.on("message", console.log); - socket.on("connect_error", (err: any) => { - if (settled) return; - settled = true; - clearTimeout(timeout); - try { - socket.disconnect(); - } catch {} - reject(err instanceof Error ? err : new Error("Socket connect_error")); - }); - socket.on("error", (err: any) => { - if (settled) return; - settled = true; - clearTimeout(timeout); - try { - socket.disconnect(); - } catch {} - reject(err instanceof Error ? err : new Error("Socket error")); - }); - socket.on("disconnect", (reason: any) => { - if (settled) return; - settled = true; - clearTimeout(timeout); - reject( - new Error( - "Socket disconnected before auth was received: " + String(reason), - ), - ); - }); - }); + if (res.status !== 202) { + throw new Error(`Unexpected status ${res.status} from auth callback endpoint`); + } + // 202 = still pending — wait and retry + await new Promise((r) => setTimeout(r, intervalMs)); + } + + throw new Error("Timed out waiting for auth callback"); }, }; diff --git a/ui/src/lib/optima-api/modules/users.ts b/ui/src/lib/optima-api/modules/users.ts index 0b718e6..c293cae 100644 --- a/ui/src/lib/optima-api/modules/users.ts +++ b/ui/src/lib/optima-api/modules/users.ts @@ -4,6 +4,8 @@ import type { Role } from "./roles"; export interface User { id: string; name: string; + firstName?: string | null; + lastName?: string | null; email: string; login: string; image?: string; @@ -38,7 +40,7 @@ export const users = { */ async fetch( accessToken: string, - identifier: string, + identifier: string ): Promise<{ data: User }> { const response = await api.get(`/v1/user/users/${identifier}`, { headers: { @@ -58,10 +60,12 @@ export const users = { identifier: string, updates: { name?: string; + firstName?: string | null; + lastName?: string | null; image?: string; roles?: string[]; permissions?: string[]; - }, + } ): Promise<{ data: User }> { const response = await api.patch(`/v1/user/users/${identifier}`, updates, { headers: { @@ -77,7 +81,7 @@ export const users = { */ async delete( accessToken: string, - identifier: string, + identifier: string ): Promise<{ data: User }> { const response = await api.delete(`/v1/user/users/${identifier}`, { headers: { @@ -93,7 +97,7 @@ export const users = { */ async fetchRoles( accessToken: string, - identifier: string, + identifier: string ): Promise<{ data: Role[] }> { const response = await api.get(`/v1/user/users/${identifier}/roles`, { headers: { @@ -110,7 +114,7 @@ export const users = { async checkPermissions( accessToken: string, identifier: string, - permissions: string[], + permissions: string[] ): Promise<{ data: { results: PermissionCheckResult[] } }> { const response = await api.post( `/v1/user/users/${identifier}/check-permission`, @@ -119,7 +123,7 @@ export const users = { headers: { Authorization: `Bearer ${accessToken}`, }, - }, + } ); return response.data; }, diff --git a/ui/src/lib/utils.ts b/ui/src/lib/utils.ts index 1ec7aac..90f98a9 100644 --- a/ui/src/lib/utils.ts +++ b/ui/src/lib/utils.ts @@ -1,6 +1,8 @@ /** * Format an ISO date string into a human-readable locale date. * Returns an empty string (or "—" when `dash` is true) for missing/invalid values. + * Date-only ISO strings (e.g. "2025-03-15T00:00:00.000Z") are interpreted as + * calendar dates to avoid off-by-one errors west of UTC. */ export function formatDate( dateStr?: string | null, @@ -9,7 +11,11 @@ export function formatDate( const fallback = opts.dash ? "—" : ""; if (!dateStr) return fallback; try { - return new Date(dateStr).toLocaleDateString("en-US", { + // Strip time portion for date-only fields to prevent timezone shift + const dateOnly = dateStr.split("T")[0]; + const date = new Date(dateOnly + "T00:00:00"); + if (isNaN(date.getTime())) return fallback; + return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", diff --git a/ui/src/routes/(auth)/login/+page.server.ts b/ui/src/routes/(auth)/login/+page.server.ts index 8a80560..0e2981e 100644 --- a/ui/src/routes/(auth)/login/+page.server.ts +++ b/ui/src/routes/(auth)/login/+page.server.ts @@ -1,5 +1,6 @@ import { Actions, redirect } from "@sveltejs/kit"; import { optima } from "$lib"; +import { INTERNAL_API_URL } from "$env/static/private"; export const actions: Actions = { login: async (event) => { @@ -7,6 +8,7 @@ export const actions: Actions = { const tokens = await optima.user.awaitAuthCallback( data.get("callbackKey") as string, + INTERNAL_API_URL, ); event.cookies.set("accessToken", tokens.accessToken, { diff --git a/ui/src/routes/+layout.svelte b/ui/src/routes/+layout.svelte index 1a3bbd4..219febc 100644 --- a/ui/src/routes/+layout.svelte +++ b/ui/src/routes/+layout.svelte @@ -31,6 +31,14 @@ }, ]; + const personalNavItems = [ + { + href: "/calendar", + label: "Calendar", + icon: '', + }, + ]; + const adminNavItem = { href: "/admin", label: "Admin", @@ -115,6 +123,28 @@ {/each} + + {#each personalNavItems as item} + + + {@html item.icon} + + {item.label} + + {/each} + {#if canViewAdmin} { try { const rolesResult = await optima.users.fetchRoles( accessToken, - user.id, + user.id ); return { ...user, roleDetails: rolesResult?.data ?? [] }; } catch { return { ...user, roleDetails: [] }; } - }), + }) ); return { @@ -66,21 +66,30 @@ export const actions: Actions = { const formData = await request.formData(); const id = (formData.get("id") as string)?.trim(); - const name = (formData.get("name") as string)?.trim(); + const firstName = (formData.get("firstName") as string)?.trim(); + const lastName = (formData.get("lastName") as string)?.trim(); const image = (formData.get("image") as string)?.trim() || undefined; const rolesJson = (formData.get("roles") as string)?.trim(); const permissionsJson = (formData.get("permissions") as string)?.trim(); - if (!id || !name) { - return fail(400, { message: "User ID and name are required." }); + if (!id) { + return fail(400, { message: "User ID is required." }); } const updates: { - name: string; + firstName?: string | null; + lastName?: string | null; image?: string; roles?: string[]; permissions?: string[]; - } = { name, image }; + } = { image }; + + if (formData.has("firstName")) { + updates.firstName = firstName || null; + } + if (formData.has("lastName")) { + updates.lastName = lastName || null; + } if (rolesJson) { try { diff --git a/ui/src/routes/admin/users/+page.svelte b/ui/src/routes/admin/users/+page.svelte index 3dfdc37..69b2f61 100644 --- a/ui/src/routes/admin/users/+page.svelte +++ b/ui/src/routes/admin/users/+page.svelte @@ -29,13 +29,18 @@ $: users = data.users; $: allRoles = data.roles; + function fullName(user: UserWithRoles): string { + const computed = `${user.firstName ?? ""} ${user.lastName ?? ""}`.trim(); + return computed || user.name || user.login || user.email; + } + // Search / filter let searchQuery = ""; $: filteredUsers = users.filter((u) => { if (!searchQuery.trim()) return true; const q = searchQuery.toLowerCase(); return ( - u.name.toLowerCase().includes(q) || + fullName(u).toLowerCase().includes(q) || u.email.toLowerCase().includes(q) || u.login.toLowerCase().includes(q) ); @@ -151,8 +156,9 @@ onCancel={cancelDelete} handleEnhance={handleDeleteEnhance} > - Are you sure you want to delete {userToDelete?.name}? This - action cannot be undone. + Are you sure you want to delete {userToDelete ? fullName(userToDelete) : "this user"}? This action cannot be undone.
    @@ -211,15 +217,15 @@ {#if user.image} {user.name} {:else}
    - {initials(user.name)} + {initials(fullName(user))}
    {/if} - {user.name} + {fullName(user)}
- + @@ -715,6 +822,7 @@ tabindex="0" on:click={() => goto(`/sales/opportunity/${opp.id}?from=dashboard`)} + on:contextmenu={(e) => contextMenu.open(e, opp)} on:keydown={(e) => handleRowKeydown(e, opp.id, "dashboard")} style="cursor: pointer;" @@ -739,34 +847,35 @@ - @@ -894,7 +1003,7 @@ - + @@ -908,6 +1017,7 @@ tabindex="0" on:click={() => goto(`/sales/opportunity/${opp.id}?from=opportunities`)} + on:contextmenu={(e) => contextMenu.open(e, opp)} on:keydown={(e) => handleRowKeydown(e, opp.id, "opportunities")} style="cursor: pointer;" @@ -935,27 +1045,31 @@ -
diff --git a/ui/src/routes/admin/users/page.server.spec.ts b/ui/src/routes/admin/users/page.server.spec.ts index 134e858..87d1eb8 100644 --- a/ui/src/routes/admin/users/page.server.spec.ts +++ b/ui/src/routes/admin/users/page.server.spec.ts @@ -20,6 +20,10 @@ const { mockOptima, mockCheckPermissions, mockHandleApiError, mockFail } = })), })); +vi.mock("$env/static/public", () => ({ + PUBLIC_API_URL: "https://api.example.com", +})); + vi.mock("$lib", () => ({ optima: mockOptima })); vi.mock("$lib/permissions", () => ({ checkPermissions: mockCheckPermissions, @@ -102,15 +106,15 @@ describe("admin/users +page.server.ts", () => { }); }); - it("returns 400 when required fields are missing", async () => { + it("returns 400 when user ID is missing", async () => { await actions.updateUser({ locals: { session: { accessToken: "tok" } }, request: { - formData: vi.fn().mockResolvedValue(createFormData({ id: "u1" })), + formData: vi.fn().mockResolvedValue(createFormData({})), }, } as any); expect(mockFail).toHaveBeenCalledWith(400, { - message: "User ID and name are required.", + message: "User ID is required.", }); }); @@ -120,14 +124,19 @@ describe("admin/users +page.server.ts", () => { const result = await actions.updateUser({ locals: { session: { accessToken: "tok" } }, request: { - formData: vi - .fn() - .mockResolvedValue(createFormData({ id: "u1", name: "Updated" })), + formData: vi.fn().mockResolvedValue( + createFormData({ + id: "u1", + firstName: "Updated", + lastName: "User", + }) + ), }, } as any); expect(mockOptima.users.update).toHaveBeenCalledWith("tok", "u1", { - name: "Updated", + firstName: "Updated", + lastName: "User", image: undefined, }); expect(result).toEqual({}); @@ -142,15 +151,17 @@ describe("admin/users +page.server.ts", () => { formData: vi.fn().mockResolvedValue( createFormData({ id: "u1", - name: "Updated", + firstName: "Updated", + lastName: "User", roles: '["r1","r2"]', - }), + }) ), }, } as any); expect(mockOptima.users.update).toHaveBeenCalledWith("tok", "u1", { - name: "Updated", + firstName: "Updated", + lastName: "User", image: undefined, roles: ["r1", "r2"], }); @@ -163,9 +174,9 @@ describe("admin/users +page.server.ts", () => { formData: vi.fn().mockResolvedValue( createFormData({ id: "u1", - name: "Updated", + firstName: "Updated", roles: "bad json", - }), + }) ), }, } as any); diff --git a/ui/src/routes/api/sales/opportunities/+server.ts b/ui/src/routes/api/sales/opportunities/+server.ts index 31089e1..83eea16 100644 --- a/ui/src/routes/api/sales/opportunities/+server.ts +++ b/ui/src/routes/api/sales/opportunities/+server.ts @@ -21,7 +21,7 @@ export const POST: RequestHandler = async ({ request, locals }) => { stage: body.stage || undefined, status: body.status || undefined, priority: body.priority || undefined, - rating: body.rating || undefined, + interest: body.interest || undefined, primarySalesRep: body.primarySalesRep || undefined, secondarySalesRep: body.secondarySalesRep || undefined, company: body.company || undefined, diff --git a/ui/src/routes/api/sales/opportunities/server.spec.ts b/ui/src/routes/api/sales/opportunities/server.spec.ts index faa9f15..36b8395 100644 --- a/ui/src/routes/api/sales/opportunities/server.spec.ts +++ b/ui/src/routes/api/sales/opportunities/server.spec.ts @@ -36,7 +36,7 @@ describe("POST /api/sales/opportunities", () => { }; await expect(POST(event as any)).rejects.toEqual( - expect.objectContaining({ status: 401 }), + expect.objectContaining({ status: 401 }) ); }); @@ -51,7 +51,7 @@ describe("POST /api/sales/opportunities", () => { }; await expect(POST(event as any)).rejects.toEqual( - expect.objectContaining({ status: 400 }), + expect.objectContaining({ status: 400 }) ); }); @@ -64,7 +64,7 @@ describe("POST /api/sales/opportunities", () => { }; await expect(POST(event as any)).rejects.toEqual( - expect.objectContaining({ status: 400 }), + expect.objectContaining({ status: 400 }) ); }); @@ -93,7 +93,7 @@ describe("POST /api/sales/opportunities", () => { stage: undefined, status: undefined, priority: undefined, - rating: undefined, + interest: undefined, primarySalesRep: undefined, secondarySalesRep: undefined, company: undefined, diff --git a/ui/src/routes/calendar/+page.svelte b/ui/src/routes/calendar/+page.svelte new file mode 100644 index 0000000..c2682e5 --- /dev/null +++ b/ui/src/routes/calendar/+page.svelte @@ -0,0 +1,172 @@ + + + + Calendar — Project Optima + + +
+
+
+
+ + + + + + +

Calendar

+
+ +
+
+ + + +
+ + {titleText} + +
+ {#each views as v} + + {/each} +
+
+
+ +
+ +
+
+
diff --git a/ui/src/routes/calendar/events/+server.ts b/ui/src/routes/calendar/events/+server.ts new file mode 100644 index 0000000..47cf580 --- /dev/null +++ b/ui/src/routes/calendar/events/+server.ts @@ -0,0 +1,33 @@ +import { optima } from "$lib"; +import { json, error } from "@sveltejs/kit"; +import type { RequestHandler } from "./$types"; + +/** GET /calendar/events?start=&end= — fetch schedule entries for the authenticated user */ +export const GET: RequestHandler = async ({ url, locals }) => { + const accessToken = locals.session?.accessToken; + if (!accessToken) throw error(401, "Unauthorized"); + + const startStr = url.searchParams.get("start"); + const endStr = url.searchParams.get("end"); + + if (!startStr || !endStr) throw error(400, "start and end query params are required"); + + const start = new Date(startStr); + const end = new Date(endStr); + + if (isNaN(start.getTime()) || isNaN(end.getTime())) { + throw error(400, "Invalid date format for start or end"); + } + + try { + const entries = await optima.schedule.fetchMe(accessToken, start, end); + return json({ data: entries }); + } catch (err: unknown) { + console.error("Failed to fetch schedule entries:", err); + const status = + err && typeof err === "object" && "status" in err + ? (err as { status: number }).status + : 500; + throw error(status, "Failed to fetch schedule entries"); + } +}; diff --git a/ui/src/routes/companies/+page.svelte b/ui/src/routes/companies/+page.svelte index 57ef167..c20998c 100644 --- a/ui/src/routes/companies/+page.svelte +++ b/ui/src/routes/companies/+page.svelte @@ -264,7 +264,8 @@ {company.identifier || company.id?.slice(0, 8)}{company.identifier || + company.id?.toString().slice(0, 8)} {/if} diff --git a/ui/src/routes/companies/[id]/types.ts b/ui/src/routes/companies/[id]/types.ts index 2ebbe9a..18fecbe 100644 --- a/ui/src/routes/companies/[id]/types.ts +++ b/ui/src/routes/companies/[id]/types.ts @@ -127,7 +127,10 @@ export function configStatusClass(statusName?: string): string { export function formatDate(dateStr?: string): string { if (!dateStr) return ""; try { - return new Date(dateStr).toLocaleDateString("en-US", { + const dateOnly = dateStr.split("T")[0]; + const date = new Date(dateOnly + "T00:00:00"); + if (isNaN(date.getTime())) return ""; + return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", diff --git a/ui/src/routes/sales/+page.server.ts b/ui/src/routes/sales/+page.server.ts index bf0fd0e..d2ce7f0 100644 --- a/ui/src/routes/sales/+page.server.ts +++ b/ui/src/routes/sales/+page.server.ts @@ -3,6 +3,25 @@ import { handleApiError } from "$lib/optima-api/errorHandler"; import { checkPermissions } from "$lib/permissions"; import type { PageServerLoad } from "./$types"; +function withTimeoutFallback( + promise: Promise, + timeoutMs: number, + fallback: T, +): Promise { + return new Promise((resolve) => { + const timer = setTimeout(() => resolve(fallback), timeoutMs); + promise + .then((value) => { + clearTimeout(timer); + resolve(value); + }) + .catch(() => { + clearTimeout(timer); + resolve(fallback); + }); + }); +} + export const load: PageServerLoad = async ({ locals, url }) => { const accessToken = locals.session?.accessToken; if (!accessToken) { @@ -68,7 +87,11 @@ export const load: PageServerLoad = async ({ locals, url }) => { optima.sales .fetchOpportunityTypes(accessToken) .catch(() => ({ data: [] })), - optima.sales.fetchMetrics(accessToken).catch(() => null), + withTimeoutFallback( + optima.sales.fetchMetrics(accessToken).catch(() => null), + 2500, + null, + ), ]); const opportunities = diff --git a/ui/src/routes/sales/+page.svelte b/ui/src/routes/sales/+page.svelte index 9b329b4..b8c0632 100644 --- a/ui/src/routes/sales/+page.svelte +++ b/ui/src/routes/sales/+page.svelte @@ -7,12 +7,21 @@ OpportunityType, } from "$lib/optima-api/modules/sales"; import { formatDate } from "$lib/utils"; - import { statusColorClass, statusLabel, isEquivalencyStatus, originalStatusName } from "$lib/sales-utils"; + import { + statusColorClass, + statusLabel, + isEquivalencyStatus, + originalStatusName, + } from "$lib/sales-utils"; import CreateOpportunityModal from "../../components/CreateOpportunityModal.svelte"; import AccessDenied from "../../components/AccessDenied.svelte"; + import NoResultsMonkey from "../../components/NoResultsMonkey.svelte"; import Pagination from "../../components/Pagination.svelte"; + import OpportunityContextMenu from "../../components/OpportunityContextMenu.svelte"; import "../../styles/sales/sales.css"; + let contextMenu: OpportunityContextMenu; + type SalesOpportunity = { id: string; cwOpportunityId?: number; @@ -21,7 +30,7 @@ stage?: { id?: number; name?: string } | null; status?: { id?: number; name?: string } | null; priority?: { id?: number; name?: string } | null; - rating?: { id?: number; name?: string } | null; + interest?: "HOT" | "WARM" | "COLD" | null; primarySalesRep?: { id?: number; identifier?: string; @@ -32,7 +41,7 @@ identifier?: string; name?: string; } | null; - company?: { id?: number | string; name?: string } | null; + company?: { id?: string; name?: string } | null; expectedCloseDate?: string | null; closedDate?: string | null; closedFlag?: boolean; @@ -62,7 +71,11 @@ function fmt$(v: number | null | undefined): string { if (v == null) return "—"; - return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 }).format(v); + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + maximumFractionDigits: 0, + }).format(v); } function fmtPct(v: number | null | undefined): string { if (v == null) return "—"; @@ -102,7 +115,11 @@ const params = new URLSearchParams(); params.set("tab", "dashboard"); if (!hideClosed) params.set("dashIncludeClosed", "true"); - goto(`/sales?${params.toString()}`, { replaceState: false, keepFocus: true, noScroll: true }); + goto(`/sales?${params.toString()}`, { + replaceState: false, + keepFocus: true, + noScroll: true, + }); } $: filteredMyOpportunities = dashSearch.trim() ? opportunities.filter((op) => { @@ -157,7 +174,11 @@ function handleDashFilterClickOutside(e: MouseEvent) { if (!dashFilterOpen) return; const target = e.target as Node; - if (dashFilterBtnEl?.contains(target) || dashFilterPopoverEl?.contains(target)) return; + if ( + dashFilterBtnEl?.contains(target) || + dashFilterPopoverEl?.contains(target) + ) + return; dashFilterOpen = false; } @@ -222,7 +243,7 @@ $: opportunities = data.opportunities; function navigateWithFilters( - opts: { page?: number; keepFocus?: boolean } = {}, + opts: { page?: number; keepFocus?: boolean } = {} ) { const params = new URLSearchParams(); params.set("page", String(opts.page ?? currentPage)); @@ -274,8 +295,8 @@ function handleRowKeydown( e: KeyboardEvent, - oppId: number, - from: "dashboard" | "opportunities", + oppId: string | number, + from: "dashboard" | "opportunities" ) { const row = e.currentTarget as HTMLElement; if (e.key === "Enter" || e.key === " ") { @@ -305,7 +326,13 @@ } function ownerLabel(op: SalesOpportunity): string { - return op.primarySalesRep?.name || op.secondarySalesRep?.name || "—"; + return ( + op.primarySalesRep?.name || + op.primarySalesRep?.identifier || + op.secondarySalesRep?.name || + op.secondarySalesRep?.identifier || + "—" + ); } function companyLabel(op: SalesOpportunity): string { @@ -352,7 +379,12 @@ } - { handleFilterClickOutside(e); handleDashFilterClickOutside(e); }} /> + { + handleFilterClickOutside(e); + handleDashFilterClickOutside(e); + }} +/> Sales — Project Optima @@ -443,7 +475,14 @@ on:click={() => (showCreateModal = true)} aria-label="Create opportunity" > - + @@ -464,7 +503,9 @@ {currentUser?.name ? currentUser.name.split(" ")[0] : "Welcome"}

Your sales at a glance

-

Overview data refreshes every 5 min

+

+ Overview data refreshes every 5 min +

@@ -474,17 +515,23 @@
- {m?.openOpportunityCount ?? myOpenOpps.length} + {m?.openOpportunityCount ?? myOpenOpps.length} Open
- {m?.assignedOpportunityCount ?? opportunities.length} + {m?.assignedOpportunityCount ?? opportunities.length} Assigned
- {(m?.closedOpportunityCount?.mtd ?? 0)} + {m?.closedOpportunityCount?.mtd ?? 0} Closed MTD
@@ -509,11 +556,18 @@
-
+
{:else} @@ -529,15 +583,25 @@
Pipeline - {fmt$(m?.pipelineRevenue)} + {fmt$(m?.pipelineRevenue)}
{#if m} - {@const wPct = m.pipelineRevenue > 0 ? (m.weightedPipelineRevenue / m.pipelineRevenue) * 100 : 0} + {@const wPct = + m.pipelineRevenue > 0 + ? (m.weightedPipelineRevenue / m.pipelineRevenue) * 100 + : 0}
-
+
- {fmt$(m.weightedPipelineRevenue)} weighted + {fmt$(m.weightedPipelineRevenue)} weighted
{/if}
@@ -546,14 +610,17 @@
Closed MTD - {fmt$(m?.closedWonRevenueMtd)} + {fmt$(m?.closedWonRevenueMtd)}
Closed YTD - {fmt$(m?.closedWonRevenueYtd)} + {fmt$(m?.closedWonRevenueYtd)}
- @@ -563,17 +630,25 @@
- {fmtDays(m?.avgDaysToClose)} + {fmtDays(m?.avgDaysToClose)} Avg Close
- {fmt$(m?.avgOpenDealSize)} + {fmt$(m?.avgOpenDealSize)} Avg Open
- {fmt$(m?.avgWonDealSize?.ytd)} + {fmt$(m?.avgWonDealSize?.ytd)} Avg Won
@@ -582,23 +657,39 @@
Win Rate MTD - {fmtPct(m?.winRate?.mtd)} + {fmtPct(m?.winRate?.mtd)}
{#if m}
-
+
{/if}
Win Rate YTD - {fmtPct(m?.winRate?.ytd)} + {fmtPct(m?.winRate?.ytd)}
{#if m}
-
+
{/if} @@ -643,8 +734,17 @@ aria-expanded={dashFilterOpen} type="button" > - - + + Filters {#if dashHideClosed} @@ -652,16 +752,23 @@ {/if} {#if dashFilterOpen} -
+
Opportunity Company StatusRatingInterest Expected Close Updated
- {#if opp.rating?.name} + {#if opp.interest} {#each [1, 2, 3] as level} {/each} - {opp.rating.name} + {opp.interest} {:else} — {/if} {formatDate(opp.expectedCloseDate)} {formatDate( - opp.cwLastUpdated || opp.updatedAt, + opp.cwLastUpdated || opp.updatedAt )}
Opportunity Company StatusRatingInterest Owner Expected Close Updated - {#if opp.rating?.name} + {#if opp.interest} {#each [1, 2, 3] as level} - {/each} - {opp.rating.name} + {opp.interest} {:else} — {/if} {ownerLabel(opp)} + {formatDate(opp.expectedCloseDate)} @@ -989,6 +1103,13 @@ invalidateAll()} + opportunityTypes={data.opportunityTypes ?? []} +/> + + invalidateAll()} /> diff --git a/ui/src/routes/sales/opportunity/[id]/components/OpportunitySidebar.svelte b/ui/src/routes/sales/opportunity/[id]/components/OpportunitySidebar.svelte index 3a53249..6852650 100644 --- a/ui/src/routes/sales/opportunity/[id]/components/OpportunitySidebar.svelte +++ b/ui/src/routes/sales/opportunity/[id]/components/OpportunitySidebar.svelte @@ -6,6 +6,7 @@ import type { SalesOpportunity, WorkflowStatusResponse, + OpportunityType, } from "$lib/optima-api/modules/sales"; import { WORKFLOW_STATUS_LABELS, @@ -30,16 +31,17 @@ $: daysUntilClose = (() => { if (!opportunity?.expectedCloseDate || isClosedOpportunity) return null; const raw = opportunity.expectedCloseDate; - const close = new Date(raw.includes("T") ? raw : raw + "T00:00:00"); + const dateOnly = raw.split("T")[0]; + const close = new Date(dateOnly + "T00:00:00"); const now = new Date(); const closeDay = new Date( close.getFullYear(), close.getMonth(), - close.getDate(), + close.getDate() ); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); return Math.round( - (closeDay.getTime() - today.getTime()) / (1000 * 60 * 60 * 24), + (closeDay.getTime() - today.getTime()) / (1000 * 60 * 60 * 24) ); })(); @@ -50,6 +52,7 @@ export let accessToken: string | null = null; export let isClosedOpportunity: boolean = false; export let workflowStatus: WorkflowStatusResponse | null = null; + export let opportunityTypes: OpportunityType[] = []; // Workflow-derived status for badge $: wfStatusKey = workflowStatus @@ -206,6 +209,17 @@ let editSites: EditSite[] = []; let sitesLoading = false; + // Type dropdown state + let editTypeId: number | null = null; + let editTypeName = ""; + let typeDropdownOpen = false; + let typeSearchQuery = ""; + $: filteredTypes = typeSearchQuery + ? opportunityTypes.filter((t) => + t.name.toLowerCase().includes(typeSearchQuery.toLowerCase()) + ) + : opportunityTypes; + // Display-mode contact details (fetched from company details API) let displayContacts: EditContact[] = []; let displayContactsCompanyId: string | null = null; @@ -223,6 +237,7 @@ let secRepTriggerEl: HTMLElement | null = null; let contactTriggerEl: HTMLElement | null = null; let siteTriggerEl: HTMLElement | null = null; + let typeTriggerEl: HTMLElement | null = null; let dropdownPos = { top: 0, left: 0, width: 280 }; function positionDropdown(triggerEl: HTMLElement | null) { @@ -262,7 +277,16 @@ let companySearchTimer: ReturnType | null = null; async function loadCWMembers() { - if ($cwMembers.length > 0) return; + const hasInvalidIds = $cwMembers.some((m) => { + const id = Number((m as { id: unknown }).id); + return !Number.isFinite(id); + }); + + if (hasInvalidIds) { + cwMembers.reset(); + } + + if ($cwMembers.length > 0 && !hasInvalidIds) return; membersLoading = true; try { await cwMembers.load(); @@ -284,7 +308,7 @@ companySearchTimer = setTimeout(async () => { try { const json = await clientFetch<{ data: CompanySearchResult[] }>( - `/api/companies/search?search=${encodeURIComponent(query)}&rpp=15`, + `/api/companies/search?search=${encodeURIComponent(query)}&rpp=15` ); companyResults = json.data ?? []; } catch (err) { @@ -297,7 +321,8 @@ } function selectRep(member: CWMember) { - editRepId = member.id; + const parsed = Number(member.id); + editRepId = Number.isFinite(parsed) ? parsed : null; editRepName = member.name; repDropdownOpen = false; repSearchQuery = ""; @@ -305,7 +330,8 @@ } function selectSecRep(member: CWMember) { - editSecRepId = member.id; + const parsed = Number(member.id); + editSecRepId = Number.isFinite(parsed) ? parsed : null; editSecRepName = member.name; secRepDropdownOpen = false; secRepSearchQuery = ""; @@ -352,6 +378,14 @@ isDirty = true; } + function selectType(t: OpportunityType) { + editTypeId = t.id; + editTypeName = t.name; + typeDropdownOpen = false; + typeSearchQuery = ""; + isDirty = true; + } + type CompanySite = EditSite; async function loadEditSites() { const companyOptimaId = opportunity?.company?.id; @@ -359,7 +393,7 @@ sitesLoading = true; try { const json = await clientFetch<{ data: CompanySite[] }>( - `/api/companies/${companyOptimaId}/sites`, + `/api/companies/${companyOptimaId}/sites` ); editSites = json.data ?? []; } catch (err) { @@ -418,7 +452,7 @@ } catch (err) { console.error( "[OpportunitySidebar] Failed to load display contacts:", - err, + err ); displayContacts = []; displayContactsCompanyId = companyOptimaId; @@ -473,6 +507,7 @@ secRepDropdownOpen = false; contactDropdownOpen = false; siteDropdownOpen = false; + typeDropdownOpen = false; } } @@ -484,13 +519,15 @@ companyDropdownOpen || secRepDropdownOpen || contactDropdownOpen || - siteDropdownOpen) + siteDropdownOpen || + typeDropdownOpen) ) { repDropdownOpen = false; companyDropdownOpen = false; secRepDropdownOpen = false; contactDropdownOpen = false; siteDropdownOpen = false; + typeDropdownOpen = false; e.stopPropagation(); } } @@ -519,13 +556,19 @@ } // Initialize rep selection - editRepId = opportunity.primarySalesRep?.id ?? null; + { + const parsed = Number(opportunity.primarySalesRep?.id); + editRepId = Number.isFinite(parsed) ? parsed : null; + } editRepName = opportunity.primarySalesRep?.name ?? ""; repSearchQuery = ""; repDropdownOpen = false; // Initialize secondary rep selection - editSecRepId = opportunity.secondarySalesRep?.id ?? null; + { + const parsed = Number(opportunity.secondarySalesRep?.id); + editSecRepId = Number.isFinite(parsed) ? parsed : null; + } editSecRepName = opportunity.secondarySalesRep?.name ?? ""; secRepSearchQuery = ""; secRepDropdownOpen = false; @@ -551,6 +594,12 @@ companyDropdownOpen = false; companyResults = []; + // Initialize type selection + editTypeId = (opportunity.type?.id as number) ?? null; + editTypeName = opportunity.type?.name ?? ""; + typeDropdownOpen = false; + typeSearchQuery = ""; + isEditing = true; loadCWMembers(); loadEditContacts(); @@ -591,15 +640,21 @@ body.expectedCloseDate = editExpectedCloseDate || null; // Compare primary sales rep - const origRepId = opportunity.primarySalesRep?.id ?? null; + const origRepId = (() => { + const parsed = Number(opportunity.primarySalesRep?.id); + return Number.isFinite(parsed) ? parsed : null; + })(); if (editRepId !== origRepId && editRepId !== null) - body.primarySalesRep = { id: editRepId }; + body.primarySalesRep = { id: Number(editRepId) }; // Compare secondary sales rep - const origSecRepId = opportunity.secondarySalesRep?.id ?? null; + const origSecRepId = (() => { + const parsed = Number(opportunity.secondarySalesRep?.id); + return Number.isFinite(parsed) ? parsed : null; + })(); if (editSecRepId !== origSecRepId) { body.secondarySalesRep = - editSecRepId !== null ? { id: editSecRepId } : null; + editSecRepId !== null ? { id: Number(editSecRepId) } : null; } // Compare contact @@ -619,6 +674,11 @@ if (editCompanyId !== origCompanyId && editCompanyId !== null) body.company = { id: editCompanyId }; + // Compare type + const origTypeId = (opportunity.type?.id as number) ?? null; + if (editTypeId !== origTypeId && editTypeId !== null) + body.type = { id: editTypeId }; + if (Object.keys(body).length === 0) { isEditing = false; isDirty = false; @@ -653,7 +713,7 @@ : (opportunity?.company?.cw_Data?.allContacts ?? []); $: matchedContact = opportunity?.contact?.id ? (allContacts.find( - (c) => String(c.cwId) === String(opportunity?.contact?.id), + (c) => String(c.cwId) === String(opportunity?.contact?.id) ) ?? allContacts[0]) : (allContacts[0] ?? null); $: contactPhone = @@ -692,7 +752,7 @@ } function formatClosedBy( - closedBy: SalesOpportunity["closedBy"] | undefined, + closedBy: SalesOpportunity["closedBy"] | undefined ): string | null { if (!closedBy) return null; if (typeof closedBy === "string") return closedBy; @@ -739,6 +799,23 @@ if (heat === "heat-hot") s += " box-shadow: 0 0 4px rgba(239,68,68,0.6);"; return s; } + + function repDisplayName( + rep?: { + name?: string; + identifier?: string; + } | null + ): string | null { + if (!rep) return null; + return rep.name || rep.identifier || null; + } + + function repInitial( + rep?: { name?: string; identifier?: string } | null + ): string { + const display = repDisplayName(rep); + return display ? display.charAt(0).toUpperCase() : "?"; + } /** Map probability percent to a tier for styling */ function probabilityTier(percent: number): string { if (percent >= 75) return "prob-high"; @@ -1493,6 +1570,107 @@ /> + +
+ Opportunity Type +
+ + {#if typeDropdownOpen} +
+
+ + + + +
+
+ {#if opportunityTypes.length === 0} +
No types available
+ {:else if filteredTypes.length === 0} +
No types found
+ {:else} + {#each filteredTypes as t (t.id)} + + {/each} + {/if} +
+
+ {/if} +
+
+