From b1f6462ac348724a792b43c89a4f46f8521427bb Mon Sep 17 00:00:00 2001 From: Jackson Roberts Date: Fri, 27 Feb 2026 14:38:22 -0600 Subject: [PATCH] Fix UserController permission serialization and include current updates --- API_ROUTES.md | 673 ++++ PERMISSIONS.md | 19 + generated/prisma/browser.ts | 5 + generated/prisma/client.ts | 5 + generated/prisma/internal/class.ts | 14 +- generated/prisma/internal/prismaNamespace.ts | 131 +- .../prisma/internal/prismaNamespaceBrowser.ts | 54 + generated/prisma/models.ts | 1 + generated/prisma/models/CatalogItem.ts | 43 +- generated/prisma/models/Company.ts | 128 + generated/prisma/models/Opportunity.ts | 2928 +++++++++++++++++ .../migration.sql | 57 + prisma/schema.prisma | 73 +- src/api/procurement/[id]/fetch.ts | 25 + src/api/procurement/[id]/fetchLinked.ts | 25 + src/api/procurement/[id]/link.ts | 28 + src/api/procurement/[id]/refreshInventory.ts | 25 + src/api/procurement/[id]/unlink.ts | 28 + src/api/procurement/count.ts | 24 + src/api/procurement/fetchAll.ts | 43 + src/api/procurement/index.ts | 9 + src/api/routers/procurementRouter.ts | 7 + src/api/routers/salesRouter.ts | 7 + src/api/sales/[id]/contacts.ts | 41 + src/api/sales/[id]/fetch.ts | 24 + src/api/sales/[id]/forecasts.ts | 39 + src/api/sales/[id]/notes.ts | 34 + src/api/sales/[id]/refresh.ts | 25 + src/api/sales/count.ts | 24 + src/api/sales/fetchAll.ts | 43 + src/api/sales/index.ts | 9 + src/api/server.ts | 2 + src/controllers/CatalogItemController.ts | 218 ++ src/controllers/CompanyController.ts | 38 +- src/controllers/OpportunityController.ts | 290 ++ src/controllers/UserController.ts | 51 +- src/index.ts | 7 + src/managers/companies.ts | 11 +- src/managers/opportunities.ts | 138 + src/managers/procurement.ts | 171 + .../opportunities/fetchAllOpportunities.ts | 28 + .../fetchCompanyOpportunities.ts | 31 + .../opportunities/fetchOpportunity.ts | 30 + .../cw-utils/opportunities/opportunities.ts | 145 + .../opportunities/opportunity.types.ts | 144 + .../processOpportunityResponse.ts | 88 + .../opportunities/refreshOpportunities.ts | 110 + .../cw-utils/procurement/refreshCatalog.ts | 2 + src/modules/globalEvents.ts | 19 + src/types/PermissionNodes.ts | 66 + 50 files changed, 6150 insertions(+), 30 deletions(-) create mode 100644 generated/prisma/models/Opportunity.ts create mode 100644 prisma/migrations/20260226050000_add_opportunity/migration.sql create mode 100644 src/api/procurement/[id]/fetch.ts create mode 100644 src/api/procurement/[id]/fetchLinked.ts create mode 100644 src/api/procurement/[id]/link.ts create mode 100644 src/api/procurement/[id]/refreshInventory.ts create mode 100644 src/api/procurement/[id]/unlink.ts create mode 100644 src/api/procurement/count.ts create mode 100644 src/api/procurement/fetchAll.ts create mode 100644 src/api/procurement/index.ts create mode 100644 src/api/routers/procurementRouter.ts create mode 100644 src/api/routers/salesRouter.ts create mode 100644 src/api/sales/[id]/contacts.ts create mode 100644 src/api/sales/[id]/fetch.ts create mode 100644 src/api/sales/[id]/forecasts.ts create mode 100644 src/api/sales/[id]/notes.ts create mode 100644 src/api/sales/[id]/refresh.ts create mode 100644 src/api/sales/count.ts create mode 100644 src/api/sales/fetchAll.ts create mode 100644 src/api/sales/index.ts create mode 100644 src/controllers/CatalogItemController.ts create mode 100644 src/controllers/OpportunityController.ts create mode 100644 src/managers/opportunities.ts create mode 100644 src/managers/procurement.ts create mode 100644 src/modules/cw-utils/opportunities/fetchAllOpportunities.ts create mode 100644 src/modules/cw-utils/opportunities/fetchCompanyOpportunities.ts create mode 100644 src/modules/cw-utils/opportunities/fetchOpportunity.ts create mode 100644 src/modules/cw-utils/opportunities/opportunities.ts create mode 100644 src/modules/cw-utils/opportunities/opportunity.types.ts create mode 100644 src/modules/cw-utils/opportunities/processOpportunityResponse.ts create mode 100644 src/modules/cw-utils/opportunities/refreshOpportunities.ts diff --git a/API_ROUTES.md b/API_ROUTES.md index df5a3ab..e1d81b5 100644 --- a/API_ROUTES.md +++ b/API_ROUTES.md @@ -2204,6 +2204,679 @@ A fun Easter egg endpoint that returns HTTP 418 (I'm a teapot). --- +## Procurement Routes + +### Get All Catalog Items + +**GET** `/procurement/items` + +Fetch a paginated list of catalog items. Supports search. + +**Authentication Required:** Yes + +**Required Permissions:** `procurement.catalog.fetch.many` + +**Query Parameters:** + +- `page` (optional, default `1`) — Page number +- `rpp` (optional, default `30`) — Records per page +- `search` (optional) — Search by name, description, part number, vendor SKU, or manufacturer +- `includeInactive` (optional, default `false`) — Include inactive catalog items in results + +**Response:** + +```json +{ + "status": 200, + "message": "Catalog items fetched successfully!", + "data": [ + { + "id": "clx...", + "cwCatalogId": 123, + "name": "Dell OptiPlex 7020", + "description": "Dell OptiPlex 7020 SFF Desktop", + "customerDescription": "Business Desktop Computer", + "internalNotes": null, + "manufacturer": "Dell", + "manufactureCwId": 45, + "partNumber": "OPT7020-SFF", + "vendorName": "Dell Direct", + "vendorSku": "DELL-OPT7020", + "vendorCwId": 12, + "price": 899.99, + "cost": 650.0, + "inactive": false, + "salesTaxable": true, + "onHand": 5, + "cwLastUpdated": "2026-02-25T10:00:00.000Z", + "createdAt": "2026-01-15T00:00:00.000Z", + "updatedAt": "2026-02-25T10:00:00.000Z" + } + ], + "meta": { + "pagination": { + "previousPage": null, + "currentPage": 1, + "nextPage": 2, + "totalPages": 10, + "totalRecords": 300, + "listedRecords": 30 + } + }, + "successful": true +} +``` + +--- + +### Get Catalog Item + +**GET** `/procurement/items/:identifier` + +Fetch a single catalog item by its internal ID or ConnectWise catalog ID. + +**Authentication Required:** Yes + +**Required Permissions:** `procurement.catalog.fetch` + +**Path Parameters:** + +- `identifier` — Internal ID (cuid) or ConnectWise catalog ID (numeric) + +**Query Parameters:** + +- `includeLinkedItems` (optional, default `false`) — Include linked catalog items in the response + +**Response:** + +```json +{ + "status": 200, + "message": "Catalog item fetched successfully!", + "data": { + "id": "clx...", + "cwCatalogId": 123, + "name": "Dell OptiPlex 7020", + "description": "Dell OptiPlex 7020 SFF Desktop", + "customerDescription": "Business Desktop Computer", + "internalNotes": null, + "manufacturer": "Dell", + "manufactureCwId": 45, + "partNumber": "OPT7020-SFF", + "vendorName": "Dell Direct", + "vendorSku": "DELL-OPT7020", + "vendorCwId": 12, + "price": 899.99, + "cost": 650.0, + "inactive": false, + "salesTaxable": true, + "onHand": 5, + "cwLastUpdated": "2026-02-25T10:00:00.000Z", + "linkedItems": [ + { + "id": "clx...", + "cwCatalogId": 456, + "name": "Dell Warranty - 3 Year" + } + ], + "createdAt": "2026-01-15T00:00:00.000Z", + "updatedAt": "2026-02-25T10:00:00.000Z" + }, + "successful": true +} +``` + +--- + +### Get Catalog Item Count + +**GET** `/procurement/count` + +Get the total number of catalog items. + +**Authentication Required:** Yes + +**Required Permissions:** `procurement.catalog.fetch.many` + +**Query Parameters:** + +- `activeOnly` (optional, default `false`) — Only count active (non-inactive) items + +**Response:** + +```json +{ + "status": 200, + "message": "Catalog item count fetched successfully!", + "data": { + "count": 300 + }, + "successful": true +} +``` + +--- + +### Refresh Catalog Item Inventory + +**POST** `/procurement/items/:identifier/refresh-inventory` + +Refresh the on-hand inventory count for a catalog item by fetching the latest data from ConnectWise. + +**Authentication Required:** Yes + +**Required Permissions:** `procurement.catalog.inventory.refresh` + +**Path Parameters:** + +- `identifier` — Internal ID (cuid) or ConnectWise catalog ID (numeric) + +**Response:** + +```json +{ + "status": 200, + "message": "Inventory refreshed successfully!", + "data": { + "id": "clx...", + "cwCatalogId": 123, + "name": "Dell OptiPlex 7020", + "onHand": 7, + "price": 899.99, + "cost": 650.0, + "inactive": false, + "createdAt": "2026-01-15T00:00:00.000Z", + "updatedAt": "2026-02-26T12:00:00.000Z" + }, + "successful": true +} +``` + +--- + +### Get Linked Catalog Items + +**GET** `/procurement/items/:identifier/linked` + +Fetch all catalog items linked to a specific item. + +**Authentication Required:** Yes + +**Required Permissions:** `procurement.catalog.fetch` + +**Path Parameters:** + +- `identifier` — Internal ID (cuid), CW identifier string, or CW catalog ID (numeric) + +**Response:** + +```json +{ + "status": 200, + "message": "Linked catalog items fetched successfully!", + "data": [ + { + "id": "clx...", + "cwCatalogId": 456, + "identifier": "DELL-WAR-3YR", + "name": "Dell Warranty - 3 Year", + "description": "Dell 3 Year ProSupport Warranty", + "price": 199.99, + "cost": 120.0, + "inactive": false, + "onHand": 0, + "createdAt": "2026-01-15T00:00:00.000Z", + "updatedAt": "2026-02-25T10:00:00.000Z" + } + ], + "successful": true +} +``` + +--- + +### Link Catalog Items + +**POST** `/procurement/items/:identifier/link` + +Link a target catalog item to the specified source item. The source item is identified by the URL parameter and the target by the request body. + +**Authentication Required:** Yes + +**Required Permissions:** `procurement.catalog.link` + +**Path Parameters:** + +- `identifier` — Internal ID (cuid), CW identifier string, or CW catalog ID (numeric) of the source item + +**Request Body:** + +```json +{ + "targetId": "clx..." +} +``` + +**Response:** + +```json +{ + "status": 200, + "message": "Catalog item linked successfully!", + "data": { + "id": "clx...", + "cwCatalogId": 123, + "identifier": "OPT7020-SFF", + "name": "Dell OptiPlex 7020", + "linkedItems": [ + { + "id": "clx...", + "cwCatalogId": 456, + "identifier": "DELL-WAR-3YR", + "name": "Dell Warranty - 3 Year" + } + ], + "createdAt": "2026-01-15T00:00:00.000Z", + "updatedAt": "2026-02-26T12:00:00.000Z" + }, + "successful": true +} +``` + +--- + +### Unlink Catalog Items + +**POST** `/procurement/items/:identifier/unlink` + +Remove the link between a source catalog item and a target catalog item. + +**Authentication Required:** Yes + +**Required Permissions:** `procurement.catalog.link` + +**Path Parameters:** + +- `identifier` — Internal ID (cuid), CW identifier string, or CW catalog ID (numeric) of the source item + +**Request Body:** + +```json +{ + "targetId": "clx..." +} +``` + +**Response:** + +```json +{ + "status": 200, + "message": "Catalog item unlinked successfully!", + "data": { + "id": "clx...", + "cwCatalogId": 123, + "identifier": "OPT7020-SFF", + "name": "Dell OptiPlex 7020", + "linkedItems": [], + "createdAt": "2026-01-15T00:00:00.000Z", + "updatedAt": "2026-02-26T12:00:00.000Z" + }, + "successful": true +} +``` + +--- + +## Sales Routes + +Sales routes serve opportunity data stored locally and synced from ConnectWise. List, search, and count operations read from the local database. Sub-resource routes (forecasts, notes, contacts) fetch live data from ConnectWise using the opportunity's CW ID. + +### Get All Opportunities + +**GET** `/sales/opportunities` + +Fetch a paginated list of opportunities. Supports search. + +**Authentication Required:** Yes + +**Required Permissions:** `sales.opportunity.fetch.many` + +**Query Parameters:** + +- `page` (optional, default `1`) — Page number +- `rpp` (optional, default `30`) — Records per page +- `search` (optional) — Search by opportunity name +- `includeClosed` (optional, default `false`) — Include closed opportunities in results + +**Response:** + +```json +{ + "status": 200, + "message": "Opportunities fetched successfully!", + "data": [ + { + "id": "clx...", + "cwOpportunityId": 456, + "name": "Acme Corp Network Refresh", + "notes": "Full network redesign and hardware refresh", + "type": { "id": 1, "name": "New" }, + "stage": { "id": 3, "name": "Proposal" }, + "status": { "id": 1, "name": "Open" }, + "priority": { "id": 2, "name": "High" }, + "rating": { "id": 1, "name": "Hot" }, + "source": "Referral", + "campaign": null, + "primarySalesRep": { + "id": 10, + "identifier": "JDoe", + "name": "John Doe" + }, + "secondarySalesRep": null, + "company": { "id": 100, "name": "Acme Corp" }, + "contact": { "id": 200, "name": "Jane Smith" }, + "site": { "id": 50, "name": "Main Office" }, + "customerPO": null, + "totalSalesTax": 0, + "location": { "id": 1, "name": "Murray" }, + "department": { "id": 5, "name": "Sales" }, + "expectedCloseDate": "2026-04-15T00:00:00.000Z", + "pipelineChangeDate": "2026-02-20T00:00:00.000Z", + "dateBecameLead": "2026-01-10T00:00:00.000Z", + "closedDate": null, + "closedFlag": false, + "closedBy": null, + "companyId": "clx...", + "cwLastUpdated": "2026-02-26T10:00:00.000Z", + "createdAt": "2026-02-01T00:00:00.000Z", + "updatedAt": "2026-02-26T10:00:00.000Z" + } + ], + "meta": { + "pagination": { + "previousPage": null, + "currentPage": 1, + "nextPage": 2, + "totalPages": 5, + "totalRecords": 150, + "listedRecords": 30 + } + }, + "successful": true +} +``` + +--- + +### Get Opportunity Count + +**GET** `/sales/opportunities/count` + +Get the total number of opportunities. + +**Authentication Required:** Yes + +**Required Permissions:** `sales.opportunity.fetch.many` + +**Query Parameters:** + +- `openOnly` (optional, default `false`) — Only count open (non-closed) opportunities + +**Response:** + +```json +{ + "status": 200, + "message": "Opportunity count fetched successfully!", + "data": { + "count": 150 + }, + "successful": true +} +``` + +--- + +### Get Opportunity + +**GET** `/sales/opportunities/:identifier` + +Fetch a single opportunity by its internal ID or ConnectWise opportunity ID. + +**Authentication Required:** Yes + +**Required Permissions:** `sales.opportunity.fetch` + +**Path Parameters:** + +- `identifier` — Internal ID (cuid) or ConnectWise opportunity ID (numeric) + +**Response:** + +```json +{ + "status": 200, + "message": "Opportunity fetched successfully!", + "data": { + "id": "clx...", + "cwOpportunityId": 456, + "name": "Acme Corp Network Refresh", + "notes": "Full network redesign and hardware refresh", + "type": { "id": 1, "name": "New" }, + "stage": { "id": 3, "name": "Proposal" }, + "status": { "id": 1, "name": "Open" }, + "priority": { "id": 2, "name": "High" }, + "rating": { "id": 1, "name": "Hot" }, + "source": "Referral", + "campaign": null, + "primarySalesRep": { + "id": 10, + "identifier": "JDoe", + "name": "John Doe" + }, + "secondarySalesRep": null, + "company": { "id": 100, "name": "Acme Corp" }, + "contact": { "id": 200, "name": "Jane Smith" }, + "site": { "id": 50, "name": "Main Office" }, + "customerPO": null, + "totalSalesTax": 0, + "location": { "id": 1, "name": "Murray" }, + "department": { "id": 5, "name": "Sales" }, + "expectedCloseDate": "2026-04-15T00:00:00.000Z", + "pipelineChangeDate": "2026-02-20T00:00:00.000Z", + "dateBecameLead": "2026-01-10T00:00:00.000Z", + "closedDate": null, + "closedFlag": false, + "closedBy": null, + "companyId": "clx...", + "cwLastUpdated": "2026-02-26T10:00:00.000Z", + "createdAt": "2026-02-01T00:00:00.000Z", + "updatedAt": "2026-02-26T10:00:00.000Z" + }, + "successful": true +} +``` + +--- + +### Refresh Opportunity + +**POST** `/sales/opportunities/:identifier/refresh` + +Refresh an opportunity's local data by fetching the latest from ConnectWise. + +**Authentication Required:** Yes + +**Required Permissions:** `sales.opportunity.refresh` + +**Path Parameters:** + +- `identifier` — Internal ID (cuid) or ConnectWise opportunity ID (numeric) + +**Response:** + +```json +{ + "status": 200, + "message": "Opportunity refreshed from ConnectWise successfully!", + "data": { + "id": "clx...", + "cwOpportunityId": 456, + "name": "Acme Corp Network Refresh", + "notes": "Updated notes from CW", + "type": { "id": 1, "name": "New" }, + "stage": { "id": 4, "name": "Negotiation" }, + "status": { "id": 1, "name": "Open" }, + "priority": { "id": 2, "name": "High" }, + "rating": { "id": 1, "name": "Hot" }, + "source": "Referral", + "campaign": null, + "primarySalesRep": { + "id": 10, + "identifier": "JDoe", + "name": "John Doe" + }, + "secondarySalesRep": null, + "company": { "id": 100, "name": "Acme Corp" }, + "contact": { "id": 200, "name": "Jane Smith" }, + "site": { "id": 50, "name": "Main Office" }, + "customerPO": null, + "totalSalesTax": 0, + "location": { "id": 1, "name": "Murray" }, + "department": { "id": 5, "name": "Sales" }, + "expectedCloseDate": "2026-04-15T00:00:00.000Z", + "pipelineChangeDate": "2026-02-25T00:00:00.000Z", + "dateBecameLead": "2026-01-10T00:00:00.000Z", + "closedDate": null, + "closedFlag": false, + "closedBy": null, + "companyId": "clx...", + "cwLastUpdated": "2026-02-26T14:00:00.000Z", + "createdAt": "2026-02-01T00:00:00.000Z", + "updatedAt": "2026-02-26T14:00:00.000Z" + }, + "successful": true +} +``` + +--- + +### Get Opportunity Forecasts + +**GET** `/sales/opportunities/:identifier/forecasts` + +Fetch forecast/revenue items for an opportunity. Data is fetched live from ConnectWise using the opportunity's CW ID. + +**Authentication Required:** Yes + +**Required Permissions:** `sales.opportunity.fetch` + +**Path Parameters:** + +- `identifier` — Internal ID (cuid) or ConnectWise opportunity ID (numeric) + +**Response:** + +```json +{ + "status": 200, + "message": "Opportunity forecasts fetched successfully!", + "data": [ + { + "id": 1, + "forecastType": "Revenue", + "forecastMonth": "2026-03-01T00:00:00Z", + "revenue": 50000.0, + "cost": 30000.0, + "forecastPercentage": 75, + "status": { "id": 1, "name": "Open" }, + "includedFlag": true, + "linkedFlag": false, + "recurringFlag": false + } + ], + "successful": true +} +``` + +--- + +### Get Opportunity Notes + +**GET** `/sales/opportunities/:identifier/notes` + +Fetch notes for an opportunity. Data is fetched live from ConnectWise using the opportunity's CW ID. + +**Authentication Required:** Yes + +**Required Permissions:** `sales.opportunity.fetch` + +**Path Parameters:** + +- `identifier` — Internal ID (cuid) or ConnectWise opportunity ID (numeric) + +**Response:** + +```json +{ + "status": 200, + "message": "Opportunity notes fetched successfully!", + "data": [ + { + "id": 1, + "text": "Client expressed interest in a full network refresh.", + "type": { "id": 2, "name": "Discussion" }, + "flagged": false, + "enteredBy": "JDoe" + } + ], + "successful": true +} +``` + +--- + +### Get Opportunity Contacts + +**GET** `/sales/opportunities/:identifier/contacts` + +Fetch contacts associated with an opportunity. Data is fetched live from ConnectWise using the opportunity's CW ID. + +**Authentication Required:** Yes + +**Required Permissions:** `sales.opportunity.fetch` + +**Path Parameters:** + +- `identifier` — Internal ID (cuid) or ConnectWise opportunity ID (numeric) + +**Response:** + +```json +{ + "status": 200, + "message": "Opportunity contacts fetched successfully!", + "data": [ + { + "id": 1, + "contact": { "id": 200, "name": "Jane Smith" }, + "company": { + "id": 100, + "identifier": "AcmeCorp", + "name": "Acme Corp" + }, + "role": { "id": 1, "name": "Decision Maker" }, + "notes": "Primary point of contact for this deal", + "referralFlag": false + } + ], + "successful": true +} +``` + +--- + ## UniFi Routes All UniFi routes require the `unifi.access` permission in addition to their route-specific permission. This acts as a gate for the entire UniFi API. diff --git a/PERMISSIONS.md b/PERMISSIONS.md index e2f7df0..da151fe 100644 --- a/PERMISSIONS.md +++ b/PERMISSIONS.md @@ -115,6 +115,25 @@ Admin-specific UI permissions that control visibility and data loading for admin - **Combine with API permissions**: A user with an admin UI permission should also have the corresponding API permission (e.g., `role.list`) to actually load data. - **Use wildcards for flexibility**: Grant `ui.navigation.*.view` to allow all navigation sections. +### Procurement Permissions + +| Permission Node | Description | Used In | Dependencies | +| --------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | +| `procurement.catalog.fetch` | Fetch a single catalog item | [src/api/procurement/[id]/fetch.ts](src/api/procurement/[id]/fetch.ts) | | +| `procurement.catalog.fetch.many` | Fetch multiple catalog items or count | [src/api/procurement/fetchAll.ts](src/api/procurement/fetchAll.ts), [src/api/procurement/count.ts](src/api/procurement/count.ts) | | +| `procurement.catalog.inventory.refresh` | Refresh on-hand inventory for a catalog item from ConnectWise | [src/api/procurement/[id]/refreshInventory.ts](src/api/procurement/[id]/refreshInventory.ts) | `procurement.catalog.fetch` | +| `procurement.catalog.link` | Link or unlink catalog items to each other | [src/api/procurement/[id]/link.ts](src/api/procurement/[id]/link.ts), [src/api/procurement/[id]/unlink.ts](src/api/procurement/[id]/unlink.ts) | `procurement.catalog.fetch` | + +### Sales Permissions + +Permissions for accessing and managing sales opportunities. Opportunities are synced from ConnectWise and stored locally; sub-resources (forecasts, notes, contacts) are fetched live from CW. + +| Permission Node | Description | Used In | Dependencies | +| ------------------------------ | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------- | +| `sales.opportunity.fetch` | Fetch a single opportunity and its CW sub-resources (forecasts, notes, contacts) | [src/api/sales/[id]/fetch.ts](src/api/sales/[id]/fetch.ts), [src/api/sales/[id]/forecasts.ts](src/api/sales/[id]/forecasts.ts), [src/api/sales/[id]/notes.ts](src/api/sales/[id]/notes.ts), [src/api/sales/[id]/contacts.ts](src/api/sales/[id]/contacts.ts) | | +| `sales.opportunity.fetch.many` | Fetch multiple opportunities (paginated/searchable) or get opportunity count | [src/api/sales/fetchAll.ts](src/api/sales/fetchAll.ts), [src/api/sales/count.ts](src/api/sales/count.ts) | | +| `sales.opportunity.refresh` | Refresh a single opportunity's local data from ConnectWise | [src/api/sales/[id]/refresh.ts](src/api/sales/[id]/refresh.ts) | `sales.opportunity.fetch` | + ### UniFi Permissions Permissions for accessing and managing UniFi network infrastructure. The `unifi.access` permission is a gate permission required for **all** UniFi routes. diff --git a/generated/prisma/browser.ts b/generated/prisma/browser.ts index 6f0db70..ad940dd 100644 --- a/generated/prisma/browser.ts +++ b/generated/prisma/browser.ts @@ -47,6 +47,11 @@ export type Company = Prisma.CompanyModel * */ export type CatalogItem = Prisma.CatalogItemModel +/** + * Model Opportunity + * + */ +export type Opportunity = Prisma.OpportunityModel /** * Model CredentialType * diff --git a/generated/prisma/client.ts b/generated/prisma/client.ts index 8a1a1df..fd4f968 100644 --- a/generated/prisma/client.ts +++ b/generated/prisma/client.ts @@ -69,6 +69,11 @@ export type Company = Prisma.CompanyModel * */ export type CatalogItem = Prisma.CatalogItemModel +/** + * Model Opportunity + * + */ +export type Opportunity = Prisma.OpportunityModel /** * Model CredentialType * diff --git a/generated/prisma/internal/class.ts b/generated/prisma/internal/class.ts index 121bc22..5182de3 100644 --- a/generated/prisma/internal/class.ts +++ b/generated/prisma/internal/class.ts @@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = { "clientVersion": "7.3.0", "engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735", "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 userId String @unique\n token String?\n\n sessions Session[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\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\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CatalogItem {\n id String @id @default(cuid())\n cwCatalogId Int @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 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 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", + "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 userId String @unique\n token String?\n\n sessions Session[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\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 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 // 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\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 cwLastUpdated 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", "runtimeDataModel": { "models": {}, "enums": {}, @@ -28,7 +28,7 @@ const config: runtime.GetPrismaClientConfig = { } } -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\":\"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\"}],\"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\":\"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\":\"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\":\"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},\"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}},\"enums\":{},\"types\":{}}") +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\":\"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\"}],\"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\":\"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\":\"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\":\"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\":\"cwLastUpdated\",\"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}},\"enums\":{},\"types\":{}}") async function decodeBase64AsWasm(wasmBase64: string): Promise { const { Buffer } = await import('node:buffer') @@ -236,6 +236,16 @@ export interface PrismaClient< */ get catalogItem(): Prisma.CatalogItemDelegate; + /** + * `prisma.opportunity`: Exposes CRUD operations for the **Opportunity** model. + * Example usage: + * ```ts + * // Fetch zero or more Opportunities + * const opportunities = await prisma.opportunity.findMany() + * ``` + */ + get opportunity(): Prisma.OpportunityDelegate; + /** * `prisma.credentialType`: Exposes CRUD operations for the **CredentialType** model. * Example usage: diff --git a/generated/prisma/internal/prismaNamespace.ts b/generated/prisma/internal/prismaNamespace.ts index 41c828f..e0c0fcf 100644 --- a/generated/prisma/internal/prismaNamespace.ts +++ b/generated/prisma/internal/prismaNamespace.ts @@ -390,6 +390,7 @@ export const ModelName = { UnifiSite: 'UnifiSite', Company: 'Company', CatalogItem: 'CatalogItem', + Opportunity: 'Opportunity', CredentialType: 'CredentialType', SecureValue: 'SecureValue', Credential: 'Credential' @@ -408,7 +409,7 @@ export type TypeMap + fields: Prisma.OpportunityFieldRefs + operations: { + findUnique: { + args: Prisma.OpportunityFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.OpportunityFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.OpportunityFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.OpportunityFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.OpportunityFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.OpportunityCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.OpportunityCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.OpportunityCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.OpportunityDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.OpportunityUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.OpportunityDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.OpportunityUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.OpportunityUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.OpportunityUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.OpportunityAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.OpportunityGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.OpportunityCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } CredentialType: { payload: Prisma.$CredentialTypePayload fields: Prisma.CredentialTypeFieldRefs @@ -1186,6 +1261,7 @@ export type CompanyScalarFieldEnum = (typeof CompanyScalarFieldEnum)[keyof typeo export const CatalogItemScalarFieldEnum = { id: 'id', cwCatalogId: 'cwCatalogId', + identifier: 'identifier', name: 'name', description: 'description', customerDescription: 'customerDescription', @@ -1209,6 +1285,58 @@ export const CatalogItemScalarFieldEnum = { export type CatalogItemScalarFieldEnum = (typeof CatalogItemScalarFieldEnum)[keyof typeof CatalogItemScalarFieldEnum] +export const OpportunityScalarFieldEnum = { + id: 'id', + cwOpportunityId: 'cwOpportunityId', + 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', + locationName: 'locationName', + locationCwId: 'locationCwId', + departmentName: 'departmentName', + departmentCwId: 'departmentCwId', + expectedCloseDate: 'expectedCloseDate', + pipelineChangeDate: 'pipelineChangeDate', + dateBecameLead: 'dateBecameLead', + closedDate: 'closedDate', + closedFlag: 'closedFlag', + closedByName: 'closedByName', + closedByCwId: 'closedByCwId', + companyId: 'companyId', + cwLastUpdated: 'cwLastUpdated', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type OpportunityScalarFieldEnum = (typeof OpportunityScalarFieldEnum)[keyof typeof OpportunityScalarFieldEnum] + + export const CredentialTypeScalarFieldEnum = { id: 'id', name: 'name', @@ -1473,6 +1601,7 @@ export type GlobalOmitConfig = { unifiSite?: Prisma.UnifiSiteOmit company?: Prisma.CompanyOmit catalogItem?: Prisma.CatalogItemOmit + opportunity?: Prisma.OpportunityOmit credentialType?: Prisma.CredentialTypeOmit secureValue?: Prisma.SecureValueOmit credential?: Prisma.CredentialOmit diff --git a/generated/prisma/internal/prismaNamespaceBrowser.ts b/generated/prisma/internal/prismaNamespaceBrowser.ts index 226cc3c..c91a7d5 100644 --- a/generated/prisma/internal/prismaNamespaceBrowser.ts +++ b/generated/prisma/internal/prismaNamespaceBrowser.ts @@ -57,6 +57,7 @@ export const ModelName = { UnifiSite: 'UnifiSite', Company: 'Company', CatalogItem: 'CatalogItem', + Opportunity: 'Opportunity', CredentialType: 'CredentialType', SecureValue: 'SecureValue', Credential: 'Credential' @@ -147,6 +148,7 @@ export type CompanyScalarFieldEnum = (typeof CompanyScalarFieldEnum)[keyof typeo export const CatalogItemScalarFieldEnum = { id: 'id', cwCatalogId: 'cwCatalogId', + identifier: 'identifier', name: 'name', description: 'description', customerDescription: 'customerDescription', @@ -170,6 +172,58 @@ export const CatalogItemScalarFieldEnum = { export type CatalogItemScalarFieldEnum = (typeof CatalogItemScalarFieldEnum)[keyof typeof CatalogItemScalarFieldEnum] +export const OpportunityScalarFieldEnum = { + id: 'id', + cwOpportunityId: 'cwOpportunityId', + 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', + locationName: 'locationName', + locationCwId: 'locationCwId', + departmentName: 'departmentName', + departmentCwId: 'departmentCwId', + expectedCloseDate: 'expectedCloseDate', + pipelineChangeDate: 'pipelineChangeDate', + dateBecameLead: 'dateBecameLead', + closedDate: 'closedDate', + closedFlag: 'closedFlag', + closedByName: 'closedByName', + closedByCwId: 'closedByCwId', + companyId: 'companyId', + cwLastUpdated: 'cwLastUpdated', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +} as const + +export type OpportunityScalarFieldEnum = (typeof OpportunityScalarFieldEnum)[keyof typeof OpportunityScalarFieldEnum] + + export const CredentialTypeScalarFieldEnum = { id: 'id', name: 'name', diff --git a/generated/prisma/models.ts b/generated/prisma/models.ts index 031d062..5a67c0a 100644 --- a/generated/prisma/models.ts +++ b/generated/prisma/models.ts @@ -14,6 +14,7 @@ export type * from './models/Role.ts' export type * from './models/UnifiSite.ts' export type * from './models/Company.ts' export type * from './models/CatalogItem.ts' +export type * from './models/Opportunity.ts' export type * from './models/CredentialType.ts' export type * from './models/SecureValue.ts' export type * from './models/Credential.ts' diff --git a/generated/prisma/models/CatalogItem.ts b/generated/prisma/models/CatalogItem.ts index 274cf69..0cae1d0 100644 --- a/generated/prisma/models/CatalogItem.ts +++ b/generated/prisma/models/CatalogItem.ts @@ -47,6 +47,7 @@ export type CatalogItemSumAggregateOutputType = { export type CatalogItemMinAggregateOutputType = { id: string | null cwCatalogId: number | null + identifier: string | null name: string | null description: string | null customerDescription: string | null @@ -70,6 +71,7 @@ export type CatalogItemMinAggregateOutputType = { export type CatalogItemMaxAggregateOutputType = { id: string | null cwCatalogId: number | null + identifier: string | null name: string | null description: string | null customerDescription: string | null @@ -93,6 +95,7 @@ export type CatalogItemMaxAggregateOutputType = { export type CatalogItemCountAggregateOutputType = { id: number cwCatalogId: number + identifier: number name: number description: number customerDescription: number @@ -136,6 +139,7 @@ export type CatalogItemSumAggregateInputType = { export type CatalogItemMinAggregateInputType = { id?: true cwCatalogId?: true + identifier?: true name?: true description?: true customerDescription?: true @@ -159,6 +163,7 @@ export type CatalogItemMinAggregateInputType = { export type CatalogItemMaxAggregateInputType = { id?: true cwCatalogId?: true + identifier?: true name?: true description?: true customerDescription?: true @@ -182,6 +187,7 @@ export type CatalogItemMaxAggregateInputType = { export type CatalogItemCountAggregateInputType = { id?: true cwCatalogId?: true + identifier?: true name?: true description?: true customerDescription?: true @@ -292,6 +298,7 @@ export type CatalogItemGroupByArgs | 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 @@ -363,6 +371,7 @@ export type CatalogItemWhereInput = { export type CatalogItemOrderByWithRelationInput = { id?: Prisma.SortOrder cwCatalogId?: Prisma.SortOrder + identifier?: Prisma.SortOrderInput | Prisma.SortOrder name?: Prisma.SortOrder description?: Prisma.SortOrderInput | Prisma.SortOrder customerDescription?: Prisma.SortOrderInput | Prisma.SortOrder @@ -388,6 +397,7 @@ export type CatalogItemOrderByWithRelationInput = { export type CatalogItemWhereUniqueInput = Prisma.AtLeast<{ id?: string cwCatalogId?: number + identifier?: string AND?: Prisma.CatalogItemWhereInput | Prisma.CatalogItemWhereInput[] OR?: Prisma.CatalogItemWhereInput[] NOT?: Prisma.CatalogItemWhereInput | Prisma.CatalogItemWhereInput[] @@ -411,11 +421,12 @@ export type CatalogItemWhereUniqueInput = Prisma.AtLeast<{ updatedAt?: Prisma.DateTimeFilter<"CatalogItem"> | Date | string linkedItems?: Prisma.CatalogItemListRelationFilter linkedTo?: Prisma.CatalogItemListRelationFilter -}, "id" | "cwCatalogId"> +}, "id" | "cwCatalogId" | "identifier"> export type CatalogItemOrderByWithAggregationInput = { id?: Prisma.SortOrder cwCatalogId?: Prisma.SortOrder + identifier?: Prisma.SortOrderInput | Prisma.SortOrder name?: Prisma.SortOrder description?: Prisma.SortOrderInput | Prisma.SortOrder customerDescription?: Prisma.SortOrderInput | Prisma.SortOrder @@ -447,6 +458,7 @@ export type CatalogItemScalarWhereWithAggregatesInput = { NOT?: Prisma.CatalogItemScalarWhereWithAggregatesInput | Prisma.CatalogItemScalarWhereWithAggregatesInput[] id?: Prisma.StringWithAggregatesFilter<"CatalogItem"> | string cwCatalogId?: Prisma.IntWithAggregatesFilter<"CatalogItem"> | number + identifier?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null name?: Prisma.StringWithAggregatesFilter<"CatalogItem"> | string description?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null customerDescription?: Prisma.StringNullableWithAggregatesFilter<"CatalogItem"> | string | null @@ -470,6 +482,7 @@ export type CatalogItemScalarWhereWithAggregatesInput = { export type CatalogItemCreateInput = { id?: string cwCatalogId: number + identifier?: string | null name: string description?: string | null customerDescription?: string | null @@ -495,6 +508,7 @@ export type CatalogItemCreateInput = { export type CatalogItemUncheckedCreateInput = { id?: string cwCatalogId: number + identifier?: string | null name: string description?: string | null customerDescription?: string | null @@ -520,6 +534,7 @@ export type CatalogItemUncheckedCreateInput = { export type CatalogItemUpdateInput = { id?: Prisma.StringFieldUpdateOperationsInput | string cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -545,6 +560,7 @@ export type CatalogItemUpdateInput = { export type CatalogItemUncheckedUpdateInput = { id?: Prisma.StringFieldUpdateOperationsInput | string cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -570,6 +586,7 @@ export type CatalogItemUncheckedUpdateInput = { export type CatalogItemCreateManyInput = { id?: string cwCatalogId: number + identifier?: string | null name: string description?: string | null customerDescription?: string | null @@ -593,6 +610,7 @@ export type CatalogItemCreateManyInput = { export type CatalogItemUpdateManyMutationInput = { id?: Prisma.StringFieldUpdateOperationsInput | string cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -616,6 +634,7 @@ export type CatalogItemUpdateManyMutationInput = { export type CatalogItemUncheckedUpdateManyInput = { id?: Prisma.StringFieldUpdateOperationsInput | string cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -649,6 +668,7 @@ export type CatalogItemOrderByRelationAggregateInput = { export type CatalogItemCountOrderByAggregateInput = { id?: Prisma.SortOrder cwCatalogId?: Prisma.SortOrder + identifier?: Prisma.SortOrder name?: Prisma.SortOrder description?: Prisma.SortOrder customerDescription?: Prisma.SortOrder @@ -681,6 +701,7 @@ export type CatalogItemAvgOrderByAggregateInput = { export type CatalogItemMaxOrderByAggregateInput = { id?: Prisma.SortOrder cwCatalogId?: Prisma.SortOrder + identifier?: Prisma.SortOrder name?: Prisma.SortOrder description?: Prisma.SortOrder customerDescription?: Prisma.SortOrder @@ -704,6 +725,7 @@ export type CatalogItemMaxOrderByAggregateInput = { export type CatalogItemMinOrderByAggregateInput = { id?: Prisma.SortOrder cwCatalogId?: Prisma.SortOrder + identifier?: Prisma.SortOrder name?: Prisma.SortOrder description?: Prisma.SortOrder customerDescription?: Prisma.SortOrder @@ -828,6 +850,7 @@ export type CatalogItemUncheckedUpdateManyWithoutLinkedItemsNestedInput = { export type CatalogItemCreateWithoutLinkedToInput = { id?: string cwCatalogId: number + identifier?: string | null name: string description?: string | null customerDescription?: string | null @@ -852,6 +875,7 @@ export type CatalogItemCreateWithoutLinkedToInput = { export type CatalogItemUncheckedCreateWithoutLinkedToInput = { id?: string cwCatalogId: number + identifier?: string | null name: string description?: string | null customerDescription?: string | null @@ -881,6 +905,7 @@ export type CatalogItemCreateOrConnectWithoutLinkedToInput = { export type CatalogItemCreateWithoutLinkedItemsInput = { id?: string cwCatalogId: number + identifier?: string | null name: string description?: string | null customerDescription?: string | null @@ -905,6 +930,7 @@ export type CatalogItemCreateWithoutLinkedItemsInput = { export type CatalogItemUncheckedCreateWithoutLinkedItemsInput = { id?: string cwCatalogId: number + identifier?: string | null name: string description?: string | null customerDescription?: string | null @@ -953,6 +979,7 @@ export type 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 @@ -992,6 +1019,7 @@ export type CatalogItemUpdateManyWithWhereWithoutLinkedItemsInput = { export type CatalogItemUpdateWithoutLinkedToInput = { id?: Prisma.StringFieldUpdateOperationsInput | string cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1016,6 +1044,7 @@ export type CatalogItemUpdateWithoutLinkedToInput = { export type CatalogItemUncheckedUpdateWithoutLinkedToInput = { id?: Prisma.StringFieldUpdateOperationsInput | string cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1040,6 +1069,7 @@ export type CatalogItemUncheckedUpdateWithoutLinkedToInput = { export type CatalogItemUncheckedUpdateManyWithoutLinkedToInput = { id?: Prisma.StringFieldUpdateOperationsInput | string cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1063,6 +1093,7 @@ export type CatalogItemUncheckedUpdateManyWithoutLinkedToInput = { export type CatalogItemUpdateWithoutLinkedItemsInput = { id?: Prisma.StringFieldUpdateOperationsInput | string cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1087,6 +1118,7 @@ export type CatalogItemUpdateWithoutLinkedItemsInput = { export type CatalogItemUncheckedUpdateWithoutLinkedItemsInput = { id?: Prisma.StringFieldUpdateOperationsInput | string cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1111,6 +1143,7 @@ export type CatalogItemUncheckedUpdateWithoutLinkedItemsInput = { export type CatalogItemUncheckedUpdateManyWithoutLinkedItemsInput = { id?: Prisma.StringFieldUpdateOperationsInput | string cwCatalogId?: Prisma.IntFieldUpdateOperationsInput | number + identifier?: Prisma.NullableStringFieldUpdateOperationsInput | string | null name?: Prisma.StringFieldUpdateOperationsInput | string description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null customerDescription?: Prisma.NullableStringFieldUpdateOperationsInput | string | null @@ -1174,6 +1207,7 @@ export type CatalogItemCountOutputTypeCountLinkedToArgs = runtime.Types.Extensions.GetSelect<{ id?: boolean cwCatalogId?: boolean + identifier?: boolean name?: boolean description?: boolean customerDescription?: boolean @@ -1200,6 +1234,7 @@ export type CatalogItemSelect = runtime.Types.Extensions.GetSelect<{ id?: boolean cwCatalogId?: boolean + identifier?: boolean name?: boolean description?: boolean customerDescription?: boolean @@ -1223,6 +1258,7 @@ export type CatalogItemSelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ id?: boolean cwCatalogId?: boolean + identifier?: boolean name?: boolean description?: boolean customerDescription?: boolean @@ -1246,6 +1282,7 @@ export type CatalogItemSelectUpdateManyAndReturn = runtime.Types.Extensions.GetOmit<"id" | "cwCatalogId" | "name" | "description" | "customerDescription" | "internalNotes" | "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" | "cwCatalogId" | "identifier" | "name" | "description" | "customerDescription" | "internalNotes" | "manufacturer" | "manufactureCwId" | "partNumber" | "vendorName" | "vendorSku" | "vendorCwId" | "price" | "cost" | "inactive" | "salesTaxable" | "onHand" | "cwLastUpdated" | "createdAt" | "updatedAt", ExtArgs["result"]["catalogItem"]> export type CatalogItemInclude = { linkedItems?: boolean | Prisma.CatalogItem$linkedItemsArgs linkedTo?: boolean | Prisma.CatalogItem$linkedToArgs @@ -1284,6 +1321,7 @@ export type $CatalogItemPayload readonly cwCatalogId: Prisma.FieldRef<"CatalogItem", 'Int'> + readonly identifier: Prisma.FieldRef<"CatalogItem", 'String'> readonly name: Prisma.FieldRef<"CatalogItem", 'String'> readonly description: Prisma.FieldRef<"CatalogItem", 'String'> readonly customerDescription: Prisma.FieldRef<"CatalogItem", 'String'> diff --git a/generated/prisma/models/Company.ts b/generated/prisma/models/Company.ts index 8f2bb11..925ee53 100644 --- a/generated/prisma/models/Company.ts +++ b/generated/prisma/models/Company.ts @@ -226,6 +226,7 @@ export type CompanyWhereInput = { updatedAt?: Prisma.DateTimeFilter<"Company"> | Date | string credentials?: Prisma.CredentialListRelationFilter unifiSites?: Prisma.UnifiSiteListRelationFilter + opportunities?: Prisma.OpportunityListRelationFilter } export type CompanyOrderByWithRelationInput = { @@ -237,6 +238,7 @@ export type CompanyOrderByWithRelationInput = { updatedAt?: Prisma.SortOrder credentials?: Prisma.CredentialOrderByRelationAggregateInput unifiSites?: Prisma.UnifiSiteOrderByRelationAggregateInput + opportunities?: Prisma.OpportunityOrderByRelationAggregateInput } export type CompanyWhereUniqueInput = Prisma.AtLeast<{ @@ -251,6 +253,7 @@ export type CompanyWhereUniqueInput = Prisma.AtLeast<{ updatedAt?: Prisma.DateTimeFilter<"Company"> | Date | string credentials?: Prisma.CredentialListRelationFilter unifiSites?: Prisma.UnifiSiteListRelationFilter + opportunities?: Prisma.OpportunityListRelationFilter }, "id" | "cw_CompanyId" | "cw_Identifier"> export type CompanyOrderByWithAggregationInput = { @@ -288,6 +291,7 @@ export type CompanyCreateInput = { updatedAt?: Date | string credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput } export type CompanyUncheckedCreateInput = { @@ -299,6 +303,7 @@ export type CompanyUncheckedCreateInput = { updatedAt?: Date | string credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput } export type CompanyUpdateInput = { @@ -310,6 +315,7 @@ export type CompanyUpdateInput = { updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput } export type CompanyUncheckedUpdateInput = { @@ -321,6 +327,7 @@ export type CompanyUncheckedUpdateInput = { updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput } export type CompanyCreateManyInput = { @@ -419,6 +426,22 @@ export type IntFieldUpdateOperationsInput = { divide?: number } +export type CompanyCreateNestedOneWithoutOpportunitiesInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutOpportunitiesInput + connect?: Prisma.CompanyWhereUniqueInput +} + +export type CompanyUpdateOneWithoutOpportunitiesNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutOpportunitiesInput + upsert?: Prisma.CompanyUpsertWithoutOpportunitiesInput + disconnect?: Prisma.CompanyWhereInput | boolean + delete?: Prisma.CompanyWhereInput | boolean + connect?: Prisma.CompanyWhereUniqueInput + update?: Prisma.XOR, Prisma.CompanyUncheckedUpdateWithoutOpportunitiesInput> +} + export type CompanyCreateNestedOneWithoutCredentialsInput = { create?: Prisma.XOR connectOrCreate?: Prisma.CompanyCreateOrConnectWithoutCredentialsInput @@ -441,6 +464,7 @@ export type CompanyCreateWithoutUnifiSitesInput = { createdAt?: Date | string updatedAt?: Date | string credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput } export type CompanyUncheckedCreateWithoutUnifiSitesInput = { @@ -451,6 +475,7 @@ export type CompanyUncheckedCreateWithoutUnifiSitesInput = { createdAt?: Date | string updatedAt?: Date | string credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput } export type CompanyCreateOrConnectWithoutUnifiSitesInput = { @@ -477,6 +502,7 @@ export type CompanyUpdateWithoutUnifiSitesInput = { createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput } export type CompanyUncheckedUpdateWithoutUnifiSitesInput = { @@ -487,6 +513,67 @@ export type CompanyUncheckedUpdateWithoutUnifiSitesInput = { createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput +} + +export type CompanyCreateWithoutOpportunitiesInput = { + id?: string + name: string + cw_CompanyId: number + cw_Identifier: string + createdAt?: Date | string + updatedAt?: Date | string + credentials?: Prisma.CredentialCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput +} + +export type CompanyUncheckedCreateWithoutOpportunitiesInput = { + id?: string + name: string + cw_CompanyId: number + cw_Identifier: string + createdAt?: Date | string + updatedAt?: Date | string + credentials?: Prisma.CredentialUncheckedCreateNestedManyWithoutCompanyInput + unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput +} + +export type CompanyCreateOrConnectWithoutOpportunitiesInput = { + where: Prisma.CompanyWhereUniqueInput + create: Prisma.XOR +} + +export type CompanyUpsertWithoutOpportunitiesInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.CompanyWhereInput +} + +export type CompanyUpdateToOneWithWhereWithoutOpportunitiesInput = { + where?: Prisma.CompanyWhereInput + data: Prisma.XOR +} + +export type CompanyUpdateWithoutOpportunitiesInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number + cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + credentials?: Prisma.CredentialUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput +} + +export type CompanyUncheckedUpdateWithoutOpportunitiesInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + cw_CompanyId?: Prisma.IntFieldUpdateOperationsInput | number + cw_Identifier?: Prisma.StringFieldUpdateOperationsInput | string + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + credentials?: Prisma.CredentialUncheckedUpdateManyWithoutCompanyNestedInput + unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput } export type CompanyCreateWithoutCredentialsInput = { @@ -497,6 +584,7 @@ export type CompanyCreateWithoutCredentialsInput = { createdAt?: Date | string updatedAt?: Date | string unifiSites?: Prisma.UnifiSiteCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityCreateNestedManyWithoutCompanyInput } export type CompanyUncheckedCreateWithoutCredentialsInput = { @@ -507,6 +595,7 @@ export type CompanyUncheckedCreateWithoutCredentialsInput = { createdAt?: Date | string updatedAt?: Date | string unifiSites?: Prisma.UnifiSiteUncheckedCreateNestedManyWithoutCompanyInput + opportunities?: Prisma.OpportunityUncheckedCreateNestedManyWithoutCompanyInput } export type CompanyCreateOrConnectWithoutCredentialsInput = { @@ -533,6 +622,7 @@ export type CompanyUpdateWithoutCredentialsInput = { createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string unifiSites?: Prisma.UnifiSiteUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUpdateManyWithoutCompanyNestedInput } export type CompanyUncheckedUpdateWithoutCredentialsInput = { @@ -543,6 +633,7 @@ export type CompanyUncheckedUpdateWithoutCredentialsInput = { createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string unifiSites?: Prisma.UnifiSiteUncheckedUpdateManyWithoutCompanyNestedInput + opportunities?: Prisma.OpportunityUncheckedUpdateManyWithoutCompanyNestedInput } @@ -553,11 +644,13 @@ export type CompanyUncheckedUpdateWithoutCredentialsInput = { export type CompanyCountOutputType = { credentials: number unifiSites: number + opportunities: number } export type CompanyCountOutputTypeSelect = { credentials?: boolean | CompanyCountOutputTypeCountCredentialsArgs unifiSites?: boolean | CompanyCountOutputTypeCountUnifiSitesArgs + opportunities?: boolean | CompanyCountOutputTypeCountOpportunitiesArgs } /** @@ -584,6 +677,13 @@ export type CompanyCountOutputTypeCountUnifiSitesArgs = { + where?: Prisma.OpportunityWhereInput +} + export type CompanySelect = runtime.Types.Extensions.GetSelect<{ id?: boolean @@ -594,6 +694,7 @@ export type CompanySelect unifiSites?: boolean | Prisma.Company$unifiSitesArgs + opportunities?: boolean | Prisma.Company$opportunitiesArgs _count?: boolean | Prisma.CompanyCountOutputTypeDefaultArgs }, ExtArgs["result"]["company"]> @@ -628,6 +729,7 @@ export type CompanyOmit = { credentials?: boolean | Prisma.Company$credentialsArgs unifiSites?: boolean | Prisma.Company$unifiSitesArgs + opportunities?: boolean | Prisma.Company$opportunitiesArgs _count?: boolean | Prisma.CompanyCountOutputTypeDefaultArgs } export type CompanyIncludeCreateManyAndReturn = {} @@ -638,6 +740,7 @@ export type $CompanyPayload[] unifiSites: Prisma.$UnifiSitePayload[] + opportunities: Prisma.$OpportunityPayload[] } scalars: runtime.Types.Extensions.GetPayloadResult<{ id: string @@ -1042,6 +1145,7 @@ export interface Prisma__CompanyClient = {}>(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> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -1512,6 +1616,30 @@ export type Company$unifiSitesArgs = { + /** + * 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[] +} + /** * Company without action */ diff --git a/generated/prisma/models/Opportunity.ts b/generated/prisma/models/Opportunity.ts new file mode 100644 index 0000000..9bfb1f3 --- /dev/null +++ b/generated/prisma/models/Opportunity.ts @@ -0,0 +1,2928 @@ + +/* !!! This is code generated by Prisma. Do not edit directly. !!! */ +/* eslint-disable */ +// biome-ignore-all lint: generated file +// @ts-nocheck +/* + * This file exports the `Opportunity` model and its related types. + * + * 🟢 You can import this file directly. + */ +import type * as runtime from "@prisma/client/runtime/client" +import type * as $Enums from "../enums.ts" +import type * as Prisma from "../internal/prismaNamespace.ts" + +/** + * Model Opportunity + * + */ +export type OpportunityModel = runtime.Types.Result.DefaultSelection + +export type AggregateOpportunity = { + _count: OpportunityCountAggregateOutputType | null + _avg: OpportunityAvgAggregateOutputType | null + _sum: OpportunitySumAggregateOutputType | null + _min: OpportunityMinAggregateOutputType | null + _max: OpportunityMaxAggregateOutputType | null +} + +export type OpportunityAvgAggregateOutputType = { + cwOpportunityId: number | null + typeCwId: number | null + stageCwId: number | null + statusCwId: number | null + priorityCwId: number | null + ratingCwId: number | null + campaignCwId: number | null + primarySalesRepCwId: number | null + secondarySalesRepCwId: number | null + companyCwId: number | null + contactCwId: number | null + siteCwId: number | null + totalSalesTax: number | null + locationCwId: number | null + departmentCwId: number | null + closedByCwId: number | null +} + +export type OpportunitySumAggregateOutputType = { + cwOpportunityId: number | null + typeCwId: number | null + stageCwId: number | null + statusCwId: number | null + priorityCwId: number | null + ratingCwId: number | null + campaignCwId: number | null + primarySalesRepCwId: number | null + secondarySalesRepCwId: number | null + companyCwId: number | null + contactCwId: number | null + siteCwId: number | null + totalSalesTax: number | null + locationCwId: number | null + departmentCwId: number | null + closedByCwId: number | null +} + +export type OpportunityMinAggregateOutputType = { + id: string | null + cwOpportunityId: number | null + name: string | null + 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 | null + locationName: string | null + locationCwId: number | null + departmentName: string | null + departmentCwId: number | null + expectedCloseDate: Date | null + pipelineChangeDate: Date | null + dateBecameLead: Date | null + closedDate: Date | null + closedFlag: boolean | null + closedByName: string | null + closedByCwId: number | null + companyId: string | null + cwLastUpdated: Date | null + createdAt: Date | null + updatedAt: Date | null +} + +export type OpportunityMaxAggregateOutputType = { + id: string | null + cwOpportunityId: number | null + name: string | null + 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 | null + locationName: string | null + locationCwId: number | null + departmentName: string | null + departmentCwId: number | null + expectedCloseDate: Date | null + pipelineChangeDate: Date | null + dateBecameLead: Date | null + closedDate: Date | null + closedFlag: boolean | null + closedByName: string | null + closedByCwId: number | null + companyId: string | null + cwLastUpdated: Date | null + createdAt: Date | null + updatedAt: Date | null +} + +export type OpportunityCountAggregateOutputType = { + id: number + cwOpportunityId: number + name: number + notes: number + typeName: number + typeCwId: number + stageName: number + stageCwId: number + statusName: number + statusCwId: number + priorityName: number + priorityCwId: number + ratingName: number + ratingCwId: number + source: number + campaignName: number + campaignCwId: number + primarySalesRepName: number + primarySalesRepIdentifier: number + primarySalesRepCwId: number + secondarySalesRepName: number + secondarySalesRepIdentifier: number + secondarySalesRepCwId: number + companyCwId: number + companyName: number + contactCwId: number + contactName: number + siteCwId: number + siteName: number + customerPO: number + totalSalesTax: number + locationName: number + locationCwId: number + departmentName: number + departmentCwId: number + expectedCloseDate: number + pipelineChangeDate: number + dateBecameLead: number + closedDate: number + closedFlag: number + closedByName: number + closedByCwId: number + companyId: number + cwLastUpdated: number + createdAt: number + updatedAt: number + _all: number +} + + +export type OpportunityAvgAggregateInputType = { + cwOpportunityId?: true + typeCwId?: true + stageCwId?: true + statusCwId?: true + priorityCwId?: true + ratingCwId?: true + campaignCwId?: true + primarySalesRepCwId?: true + secondarySalesRepCwId?: true + companyCwId?: true + contactCwId?: true + siteCwId?: true + totalSalesTax?: true + locationCwId?: true + departmentCwId?: true + closedByCwId?: true +} + +export type OpportunitySumAggregateInputType = { + cwOpportunityId?: true + typeCwId?: true + stageCwId?: true + statusCwId?: true + priorityCwId?: true + ratingCwId?: true + campaignCwId?: true + primarySalesRepCwId?: true + secondarySalesRepCwId?: true + companyCwId?: true + contactCwId?: true + siteCwId?: true + totalSalesTax?: true + locationCwId?: true + departmentCwId?: true + closedByCwId?: true +} + +export type OpportunityMinAggregateInputType = { + id?: true + cwOpportunityId?: true + name?: true + notes?: true + typeName?: true + typeCwId?: true + stageName?: true + stageCwId?: true + statusName?: true + statusCwId?: true + priorityName?: true + priorityCwId?: true + ratingName?: true + ratingCwId?: true + source?: true + campaignName?: true + campaignCwId?: true + primarySalesRepName?: true + primarySalesRepIdentifier?: true + primarySalesRepCwId?: true + secondarySalesRepName?: true + secondarySalesRepIdentifier?: true + secondarySalesRepCwId?: true + companyCwId?: true + companyName?: true + contactCwId?: true + contactName?: true + siteCwId?: true + siteName?: true + customerPO?: true + totalSalesTax?: true + locationName?: true + locationCwId?: true + departmentName?: true + departmentCwId?: true + expectedCloseDate?: true + pipelineChangeDate?: true + dateBecameLead?: true + closedDate?: true + closedFlag?: true + closedByName?: true + closedByCwId?: true + companyId?: true + cwLastUpdated?: true + createdAt?: true + updatedAt?: true +} + +export type OpportunityMaxAggregateInputType = { + id?: true + cwOpportunityId?: true + name?: true + notes?: true + typeName?: true + typeCwId?: true + stageName?: true + stageCwId?: true + statusName?: true + statusCwId?: true + priorityName?: true + priorityCwId?: true + ratingName?: true + ratingCwId?: true + source?: true + campaignName?: true + campaignCwId?: true + primarySalesRepName?: true + primarySalesRepIdentifier?: true + primarySalesRepCwId?: true + secondarySalesRepName?: true + secondarySalesRepIdentifier?: true + secondarySalesRepCwId?: true + companyCwId?: true + companyName?: true + contactCwId?: true + contactName?: true + siteCwId?: true + siteName?: true + customerPO?: true + totalSalesTax?: true + locationName?: true + locationCwId?: true + departmentName?: true + departmentCwId?: true + expectedCloseDate?: true + pipelineChangeDate?: true + dateBecameLead?: true + closedDate?: true + closedFlag?: true + closedByName?: true + closedByCwId?: true + companyId?: true + cwLastUpdated?: true + createdAt?: true + updatedAt?: true +} + +export type OpportunityCountAggregateInputType = { + id?: true + cwOpportunityId?: true + name?: true + notes?: true + typeName?: true + typeCwId?: true + stageName?: true + stageCwId?: true + statusName?: true + statusCwId?: true + priorityName?: true + priorityCwId?: true + ratingName?: true + ratingCwId?: true + source?: true + campaignName?: true + campaignCwId?: true + primarySalesRepName?: true + primarySalesRepIdentifier?: true + primarySalesRepCwId?: true + secondarySalesRepName?: true + secondarySalesRepIdentifier?: true + secondarySalesRepCwId?: true + companyCwId?: true + companyName?: true + contactCwId?: true + contactName?: true + siteCwId?: true + siteName?: true + customerPO?: true + totalSalesTax?: true + locationName?: true + locationCwId?: true + departmentName?: true + departmentCwId?: true + expectedCloseDate?: true + pipelineChangeDate?: true + dateBecameLead?: true + closedDate?: true + closedFlag?: true + closedByName?: true + closedByCwId?: true + companyId?: true + cwLastUpdated?: true + createdAt?: true + updatedAt?: true + _all?: true +} + +export type OpportunityAggregateArgs = { + /** + * Filter which Opportunity to aggregate. + */ + where?: Prisma.OpportunityWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Opportunities to fetch. + */ + orderBy?: Prisma.OpportunityOrderByWithRelationInput | Prisma.OpportunityOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: Prisma.OpportunityWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Opportunities from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Opportunities. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned Opportunities + **/ + _count?: true | OpportunityCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to average + **/ + _avg?: OpportunityAvgAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to sum + **/ + _sum?: OpportunitySumAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: OpportunityMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: OpportunityMaxAggregateInputType +} + +export type GetOpportunityAggregateType = { + [P in keyof T & keyof AggregateOpportunity]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType +} + + + + +export type OpportunityGroupByArgs = { + where?: Prisma.OpportunityWhereInput + orderBy?: Prisma.OpportunityOrderByWithAggregationInput | Prisma.OpportunityOrderByWithAggregationInput[] + by: Prisma.OpportunityScalarFieldEnum[] | Prisma.OpportunityScalarFieldEnum + having?: Prisma.OpportunityScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: OpportunityCountAggregateInputType | true + _avg?: OpportunityAvgAggregateInputType + _sum?: OpportunitySumAggregateInputType + _min?: OpportunityMinAggregateInputType + _max?: OpportunityMaxAggregateInputType +} + +export type OpportunityGroupByOutputType = { + id: string + cwOpportunityId: number + 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 + locationName: string | null + locationCwId: number | null + departmentName: string | null + departmentCwId: 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 + cwLastUpdated: Date | null + createdAt: Date + updatedAt: Date + _count: OpportunityCountAggregateOutputType | null + _avg: OpportunityAvgAggregateOutputType | null + _sum: OpportunitySumAggregateOutputType | null + _min: OpportunityMinAggregateOutputType | null + _max: OpportunityMaxAggregateOutputType | null +} + +type GetOpportunityGroupByPayload = Prisma.PrismaPromise< + Array< + Prisma.PickEnumerable & + { + [P in ((keyof T) & (keyof OpportunityGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType + } + > + > + + + +export type OpportunityWhereInput = { + AND?: Prisma.OpportunityWhereInput | Prisma.OpportunityWhereInput[] + OR?: Prisma.OpportunityWhereInput[] + NOT?: Prisma.OpportunityWhereInput | Prisma.OpportunityWhereInput[] + 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 + 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 + cwLastUpdated?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null + createdAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string + company?: Prisma.XOR | null +} + +export type OpportunityOrderByWithRelationInput = { + id?: Prisma.SortOrder + cwOpportunityId?: 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 + locationName?: Prisma.SortOrderInput | Prisma.SortOrder + locationCwId?: Prisma.SortOrderInput | Prisma.SortOrder + departmentName?: Prisma.SortOrderInput | Prisma.SortOrder + departmentCwId?: 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 + cwLastUpdated?: Prisma.SortOrderInput | Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder + company?: Prisma.CompanyOrderByWithRelationInput +} + +export type OpportunityWhereUniqueInput = Prisma.AtLeast<{ + id?: string + cwOpportunityId?: number + 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 + 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 + cwLastUpdated?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null + createdAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string + company?: Prisma.XOR | null +}, "id" | "cwOpportunityId"> + +export type OpportunityOrderByWithAggregationInput = { + id?: Prisma.SortOrder + cwOpportunityId?: 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 + locationName?: Prisma.SortOrderInput | Prisma.SortOrder + locationCwId?: Prisma.SortOrderInput | Prisma.SortOrder + departmentName?: Prisma.SortOrderInput | Prisma.SortOrder + departmentCwId?: 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 + cwLastUpdated?: Prisma.SortOrderInput | Prisma.SortOrder + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder + _count?: Prisma.OpportunityCountOrderByAggregateInput + _avg?: Prisma.OpportunityAvgOrderByAggregateInput + _max?: Prisma.OpportunityMaxOrderByAggregateInput + _min?: Prisma.OpportunityMinOrderByAggregateInput + _sum?: Prisma.OpportunitySumOrderByAggregateInput +} + +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 + 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 + locationName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null + locationCwId?: Prisma.IntNullableWithAggregatesFilter<"Opportunity"> | number | null + departmentName?: Prisma.StringNullableWithAggregatesFilter<"Opportunity"> | string | null + departmentCwId?: 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 + cwLastUpdated?: Prisma.DateTimeNullableWithAggregatesFilter<"Opportunity"> | Date | string | null + createdAt?: Prisma.DateTimeWithAggregatesFilter<"Opportunity"> | Date | string + updatedAt?: Prisma.DateTimeWithAggregatesFilter<"Opportunity"> | Date | string +} + +export type OpportunityCreateInput = { + id?: string + cwOpportunityId: number + 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 + locationName?: string | null + locationCwId?: number | null + departmentName?: string | null + departmentCwId?: 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 + cwLastUpdated?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string + company?: Prisma.CompanyCreateNestedOneWithoutOpportunitiesInput +} + +export type OpportunityUncheckedCreateInput = { + id?: string + cwOpportunityId: number + 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 + locationName?: string | null + locationCwId?: number | null + departmentName?: string | null + departmentCwId?: 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 + cwLastUpdated?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + 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 + locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + departmentCwId?: 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 + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + company?: Prisma.CompanyUpdateOneWithoutOpportunitiesNestedInput +} + +export type OpportunityUncheckedUpdateInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + 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 + locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + departmentCwId?: 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 + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityCreateManyInput = { + id?: string + cwOpportunityId: number + 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 + locationName?: string | null + locationCwId?: number | null + departmentName?: string | null + departmentCwId?: 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 + cwLastUpdated?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateManyMutationInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + 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 + locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + departmentCwId?: 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 + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityUncheckedUpdateManyInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + 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 + locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + departmentCwId?: 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 + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityListRelationFilter = { + every?: Prisma.OpportunityWhereInput + some?: Prisma.OpportunityWhereInput + none?: Prisma.OpportunityWhereInput +} + +export type OpportunityOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + +export type OpportunityCountOrderByAggregateInput = { + id?: Prisma.SortOrder + cwOpportunityId?: 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 + locationName?: Prisma.SortOrder + locationCwId?: Prisma.SortOrder + departmentName?: Prisma.SortOrder + departmentCwId?: 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 + 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 + locationCwId?: Prisma.SortOrder + departmentCwId?: Prisma.SortOrder + closedByCwId?: Prisma.SortOrder +} + +export type OpportunityMaxOrderByAggregateInput = { + id?: Prisma.SortOrder + cwOpportunityId?: 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 + locationName?: Prisma.SortOrder + locationCwId?: Prisma.SortOrder + departmentName?: Prisma.SortOrder + departmentCwId?: 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 + createdAt?: Prisma.SortOrder + updatedAt?: Prisma.SortOrder +} + +export type OpportunityMinOrderByAggregateInput = { + id?: Prisma.SortOrder + cwOpportunityId?: 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 + locationName?: Prisma.SortOrder + locationCwId?: Prisma.SortOrder + departmentName?: Prisma.SortOrder + departmentCwId?: 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 + 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 + locationCwId?: Prisma.SortOrder + departmentCwId?: Prisma.SortOrder + closedByCwId?: Prisma.SortOrder +} + +export type OpportunityCreateNestedManyWithoutCompanyInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutCompanyInput[] | Prisma.OpportunityUncheckedCreateWithoutCompanyInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutCompanyInput | Prisma.OpportunityCreateOrConnectWithoutCompanyInput[] + createMany?: Prisma.OpportunityCreateManyCompanyInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUncheckedCreateNestedManyWithoutCompanyInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutCompanyInput[] | Prisma.OpportunityUncheckedCreateWithoutCompanyInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutCompanyInput | Prisma.OpportunityCreateOrConnectWithoutCompanyInput[] + createMany?: Prisma.OpportunityCreateManyCompanyInputEnvelope + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] +} + +export type OpportunityUpdateManyWithoutCompanyNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutCompanyInput[] | Prisma.OpportunityUncheckedCreateWithoutCompanyInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutCompanyInput | Prisma.OpportunityCreateOrConnectWithoutCompanyInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutCompanyInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutCompanyInput[] + createMany?: Prisma.OpportunityCreateManyCompanyInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutCompanyInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutCompanyInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutCompanyInput | Prisma.OpportunityUpdateManyWithWhereWithoutCompanyInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityUncheckedUpdateManyWithoutCompanyNestedInput = { + create?: Prisma.XOR | Prisma.OpportunityCreateWithoutCompanyInput[] | Prisma.OpportunityUncheckedCreateWithoutCompanyInput[] + connectOrCreate?: Prisma.OpportunityCreateOrConnectWithoutCompanyInput | Prisma.OpportunityCreateOrConnectWithoutCompanyInput[] + upsert?: Prisma.OpportunityUpsertWithWhereUniqueWithoutCompanyInput | Prisma.OpportunityUpsertWithWhereUniqueWithoutCompanyInput[] + createMany?: Prisma.OpportunityCreateManyCompanyInputEnvelope + set?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + disconnect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + delete?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + connect?: Prisma.OpportunityWhereUniqueInput | Prisma.OpportunityWhereUniqueInput[] + update?: Prisma.OpportunityUpdateWithWhereUniqueWithoutCompanyInput | Prisma.OpportunityUpdateWithWhereUniqueWithoutCompanyInput[] + updateMany?: Prisma.OpportunityUpdateManyWithWhereWithoutCompanyInput | Prisma.OpportunityUpdateManyWithWhereWithoutCompanyInput[] + deleteMany?: Prisma.OpportunityScalarWhereInput | Prisma.OpportunityScalarWhereInput[] +} + +export type OpportunityCreateWithoutCompanyInput = { + id?: string + cwOpportunityId: number + 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 + locationName?: string | null + locationCwId?: number | null + departmentName?: string | null + departmentCwId?: 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 + cwLastUpdated?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUncheckedCreateWithoutCompanyInput = { + id?: string + cwOpportunityId: number + 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 + locationName?: string | null + locationCwId?: number | null + departmentName?: string | null + departmentCwId?: 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 + cwLastUpdated?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityCreateOrConnectWithoutCompanyInput = { + where: Prisma.OpportunityWhereUniqueInput + create: Prisma.XOR +} + +export type OpportunityCreateManyCompanyInputEnvelope = { + data: Prisma.OpportunityCreateManyCompanyInput | Prisma.OpportunityCreateManyCompanyInput[] + skipDuplicates?: boolean +} + +export type OpportunityUpsertWithWhereUniqueWithoutCompanyInput = { + where: Prisma.OpportunityWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type OpportunityUpdateWithWhereUniqueWithoutCompanyInput = { + where: Prisma.OpportunityWhereUniqueInput + data: Prisma.XOR +} + +export type OpportunityUpdateManyWithWhereWithoutCompanyInput = { + where: Prisma.OpportunityScalarWhereInput + 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 + 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 + cwLastUpdated?: Prisma.DateTimeNullableFilter<"Opportunity"> | Date | string | null + createdAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string + updatedAt?: Prisma.DateTimeFilter<"Opportunity"> | Date | string +} + +export type OpportunityCreateManyCompanyInput = { + id?: string + cwOpportunityId: number + 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 + locationName?: string | null + locationCwId?: number | null + departmentName?: string | null + departmentCwId?: 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 + cwLastUpdated?: Date | string | null + createdAt?: Date | string + updatedAt?: Date | string +} + +export type OpportunityUpdateWithoutCompanyInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + 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 + locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + departmentCwId?: 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 + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityUncheckedUpdateWithoutCompanyInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + 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 + locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + departmentCwId?: 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 + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + +export type OpportunityUncheckedUpdateManyWithoutCompanyInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + cwOpportunityId?: Prisma.IntFieldUpdateOperationsInput | number + 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 + locationName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + locationCwId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null + departmentName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + departmentCwId?: 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 + cwLastUpdated?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string +} + + + +export type OpportunitySelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean + cwOpportunityId?: 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 + locationName?: boolean + locationCwId?: boolean + departmentName?: boolean + departmentCwId?: boolean + expectedCloseDate?: boolean + pipelineChangeDate?: boolean + dateBecameLead?: boolean + closedDate?: boolean + closedFlag?: boolean + closedByName?: boolean + closedByCwId?: boolean + companyId?: boolean + cwLastUpdated?: boolean + createdAt?: boolean + updatedAt?: boolean + company?: boolean | Prisma.Opportunity$companyArgs +}, ExtArgs["result"]["opportunity"]> + +export type OpportunitySelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ + id?: boolean + cwOpportunityId?: 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 + locationName?: boolean + locationCwId?: boolean + departmentName?: boolean + departmentCwId?: boolean + expectedCloseDate?: boolean + pipelineChangeDate?: boolean + dateBecameLead?: boolean + closedDate?: boolean + closedFlag?: boolean + closedByName?: boolean + closedByCwId?: boolean + companyId?: boolean + cwLastUpdated?: boolean + createdAt?: boolean + updatedAt?: boolean + company?: boolean | Prisma.Opportunity$companyArgs +}, ExtArgs["result"]["opportunity"]> + +export type OpportunitySelectUpdateManyAndReturn = runtime.Types.Extensions.GetSelect<{ + id?: boolean + cwOpportunityId?: 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 + locationName?: boolean + locationCwId?: boolean + departmentName?: boolean + departmentCwId?: boolean + expectedCloseDate?: boolean + pipelineChangeDate?: boolean + dateBecameLead?: boolean + closedDate?: boolean + closedFlag?: boolean + closedByName?: boolean + closedByCwId?: boolean + companyId?: boolean + cwLastUpdated?: boolean + createdAt?: boolean + updatedAt?: boolean + company?: boolean | Prisma.Opportunity$companyArgs +}, ExtArgs["result"]["opportunity"]> + +export type OpportunitySelectScalar = { + id?: boolean + cwOpportunityId?: 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 + locationName?: boolean + locationCwId?: boolean + departmentName?: boolean + departmentCwId?: boolean + expectedCloseDate?: boolean + pipelineChangeDate?: boolean + dateBecameLead?: boolean + closedDate?: boolean + closedFlag?: boolean + closedByName?: boolean + closedByCwId?: boolean + companyId?: boolean + cwLastUpdated?: 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" | "locationName" | "locationCwId" | "departmentName" | "departmentCwId" | "expectedCloseDate" | "pipelineChangeDate" | "dateBecameLead" | "closedDate" | "closedFlag" | "closedByName" | "closedByCwId" | "companyId" | "cwLastUpdated" | "createdAt" | "updatedAt", ExtArgs["result"]["opportunity"]> +export type OpportunityInclude = { + company?: boolean | Prisma.Opportunity$companyArgs +} +export type OpportunityIncludeCreateManyAndReturn = { + company?: boolean | Prisma.Opportunity$companyArgs +} +export type OpportunityIncludeUpdateManyAndReturn = { + company?: boolean | Prisma.Opportunity$companyArgs +} + +export type $OpportunityPayload = { + name: "Opportunity" + objects: { + company: Prisma.$CompanyPayload | null + } + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: string + cwOpportunityId: number + 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 + locationName: string | null + locationCwId: number | null + departmentName: string | null + departmentCwId: 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 + cwLastUpdated: Date | null + createdAt: Date + updatedAt: Date + }, ExtArgs["result"]["opportunity"]> + composites: {} +} + +export type OpportunityGetPayload = runtime.Types.Result.GetResult + +export type OpportunityCountArgs = + Omit & { + select?: OpportunityCountAggregateInputType | true + } + +export interface OpportunityDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['Opportunity'], meta: { name: 'Opportunity' } } + /** + * Find zero or one Opportunity that matches the filter. + * @param {OpportunityFindUniqueArgs} args - Arguments to find a Opportunity + * @example + * // Get one Opportunity + * const opportunity = await prisma.opportunity.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__OpportunityClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find one Opportunity that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {OpportunityFindUniqueOrThrowArgs} args - Arguments to find a Opportunity + * @example + * // Get one Opportunity + * const opportunity = await prisma.opportunity.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__OpportunityClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find the first Opportunity that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {OpportunityFindFirstArgs} args - Arguments to find a Opportunity + * @example + * // Get one Opportunity + * const opportunity = await prisma.opportunity.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__OpportunityClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find the first Opportunity that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {OpportunityFindFirstOrThrowArgs} args - Arguments to find a Opportunity + * @example + * // Get one Opportunity + * const opportunity = await prisma.opportunity.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__OpportunityClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find zero or more Opportunities that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {OpportunityFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all Opportunities + * const opportunities = await prisma.opportunity.findMany() + * + * // Get first 10 Opportunities + * const opportunities = await prisma.opportunity.findMany({ take: 10 }) + * + * // Only select the `id` + * const opportunityWithIdOnly = await prisma.opportunity.findMany({ select: { id: true } }) + * + */ + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>> + + /** + * Create a Opportunity. + * @param {OpportunityCreateArgs} args - Arguments to create a Opportunity. + * @example + * // Create one Opportunity + * const Opportunity = await prisma.opportunity.create({ + * data: { + * // ... data to create a Opportunity + * } + * }) + * + */ + create(args: Prisma.SelectSubset>): Prisma.Prisma__OpportunityClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Create many Opportunities. + * @param {OpportunityCreateManyArgs} args - Arguments to create many Opportunities. + * @example + * // Create many Opportunities + * const opportunity = await prisma.opportunity.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Create many Opportunities and returns the data saved in the database. + * @param {OpportunityCreateManyAndReturnArgs} args - Arguments to create many Opportunities. + * @example + * // Create many Opportunities + * const opportunity = await prisma.opportunity.createManyAndReturn({ + * data: [ + * // ... provide data here + * ] + * }) + * + * // Create many Opportunities and only return the `id` + * const opportunityWithIdOnly = await prisma.opportunity.createManyAndReturn({ + * select: { id: true }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + createManyAndReturn(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "createManyAndReturn", GlobalOmitOptions>> + + /** + * Delete a Opportunity. + * @param {OpportunityDeleteArgs} args - Arguments to delete one Opportunity. + * @example + * // Delete one Opportunity + * const Opportunity = await prisma.opportunity.delete({ + * where: { + * // ... filter to delete one Opportunity + * } + * }) + * + */ + delete(args: Prisma.SelectSubset>): Prisma.Prisma__OpportunityClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Update one Opportunity. + * @param {OpportunityUpdateArgs} args - Arguments to update one Opportunity. + * @example + * // Update one Opportunity + * const opportunity = await prisma.opportunity.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: Prisma.SelectSubset>): Prisma.Prisma__OpportunityClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Delete zero or more Opportunities. + * @param {OpportunityDeleteManyArgs} args - Arguments to filter Opportunities to delete. + * @example + * // Delete a few Opportunities + * const { count } = await prisma.opportunity.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more Opportunities. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {OpportunityUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many Opportunities + * const opportunity = await prisma.opportunity.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more Opportunities and returns the data updated in the database. + * @param {OpportunityUpdateManyAndReturnArgs} args - Arguments to update many Opportunities. + * @example + * // Update many Opportunities + * const opportunity = await prisma.opportunity.updateManyAndReturn({ + * where: { + * // ... provide filter here + * }, + * data: [ + * // ... provide data here + * ] + * }) + * + * // Update zero or more Opportunities and only return the `id` + * const opportunityWithIdOnly = await prisma.opportunity.updateManyAndReturn({ + * select: { id: true }, + * where: { + * // ... provide filter here + * }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + updateManyAndReturn(args: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "updateManyAndReturn", GlobalOmitOptions>> + + /** + * Create or update one Opportunity. + * @param {OpportunityUpsertArgs} args - Arguments to update or create a Opportunity. + * @example + * // Update or create a Opportunity + * const opportunity = await prisma.opportunity.upsert({ + * create: { + * // ... data to create a Opportunity + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the Opportunity we want to update + * } + * }) + */ + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__OpportunityClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + + /** + * Count the number of Opportunities. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {OpportunityCountArgs} args - Arguments to filter Opportunities to count. + * @example + * // Count the number of Opportunities + * const count = await prisma.opportunity.count({ + * where: { + * // ... the filter for the Opportunities we want to count + * } + * }) + **/ + count( + args?: Prisma.Subset, + ): Prisma.PrismaPromise< + T extends runtime.Types.Utils.Record<'select', any> + ? T['select'] extends true + ? number + : Prisma.GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a Opportunity. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {OpportunityAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Prisma.Subset): Prisma.PrismaPromise> + + /** + * Group by Opportunity. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {OpportunityGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends OpportunityGroupByArgs, + HasSelectOrTake extends Prisma.Or< + Prisma.Extends<'skip', Prisma.Keys>, + Prisma.Extends<'take', Prisma.Keys> + >, + OrderByArg extends Prisma.True extends HasSelectOrTake + ? { orderBy: OpportunityGroupByArgs['orderBy'] } + : { orderBy?: OpportunityGroupByArgs['orderBy'] }, + OrderFields extends Prisma.ExcludeUnderscoreKeys>>, + ByFields extends Prisma.MaybeTupleToUnion, + ByValid extends Prisma.Has, + HavingFields extends Prisma.GetHavingFields, + HavingValid extends Prisma.Has, + ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, + InputErrors extends ByEmpty extends Prisma.True + ? `Error: "by" must not be empty.` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetOpportunityGroupByPayload : Prisma.PrismaPromise +/** + * Fields of the Opportunity model + */ +readonly fields: OpportunityFieldRefs; +} + +/** + * The delegate class that acts as a "Promise-like" for Opportunity. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ +export interface Prisma__OpportunityClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + company = {}>(args?: Prisma.Subset>): Prisma.Prisma__CompanyClient, 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. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise +} + + + + +/** + * Fields of the Opportunity model + */ +export interface OpportunityFieldRefs { + readonly id: Prisma.FieldRef<"Opportunity", 'String'> + readonly cwOpportunityId: Prisma.FieldRef<"Opportunity", 'Int'> + 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 locationName: Prisma.FieldRef<"Opportunity", 'String'> + readonly locationCwId: Prisma.FieldRef<"Opportunity", 'Int'> + readonly departmentName: Prisma.FieldRef<"Opportunity", 'String'> + readonly departmentCwId: 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 cwLastUpdated: Prisma.FieldRef<"Opportunity", 'DateTime'> + readonly createdAt: Prisma.FieldRef<"Opportunity", 'DateTime'> + readonly updatedAt: Prisma.FieldRef<"Opportunity", 'DateTime'> +} + + +// Custom InputTypes +/** + * Opportunity findUnique + */ +export type OpportunityFindUniqueArgs = { + /** + * 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 + /** + * Filter, which Opportunity to fetch. + */ + where: Prisma.OpportunityWhereUniqueInput +} + +/** + * Opportunity findUniqueOrThrow + */ +export type OpportunityFindUniqueOrThrowArgs = { + /** + * 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 + /** + * Filter, which Opportunity to fetch. + */ + where: Prisma.OpportunityWhereUniqueInput +} + +/** + * Opportunity findFirst + */ +export type OpportunityFindFirstArgs = { + /** + * 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 + /** + * Filter, which Opportunity to fetch. + */ + where?: Prisma.OpportunityWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Opportunities to fetch. + */ + orderBy?: Prisma.OpportunityOrderByWithRelationInput | Prisma.OpportunityOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for Opportunities. + */ + cursor?: Prisma.OpportunityWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Opportunities from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Opportunities. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of Opportunities. + */ + distinct?: Prisma.OpportunityScalarFieldEnum | Prisma.OpportunityScalarFieldEnum[] +} + +/** + * Opportunity findFirstOrThrow + */ +export type OpportunityFindFirstOrThrowArgs = { + /** + * 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 + /** + * Filter, which Opportunity to fetch. + */ + where?: Prisma.OpportunityWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Opportunities to fetch. + */ + orderBy?: Prisma.OpportunityOrderByWithRelationInput | Prisma.OpportunityOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for Opportunities. + */ + cursor?: Prisma.OpportunityWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Opportunities from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Opportunities. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of Opportunities. + */ + distinct?: Prisma.OpportunityScalarFieldEnum | Prisma.OpportunityScalarFieldEnum[] +} + +/** + * Opportunity findMany + */ +export type OpportunityFindManyArgs = { + /** + * 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 + /** + * Filter, which Opportunities to fetch. + */ + where?: Prisma.OpportunityWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Opportunities to fetch. + */ + orderBy?: Prisma.OpportunityOrderByWithRelationInput | Prisma.OpportunityOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing Opportunities. + */ + cursor?: Prisma.OpportunityWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Opportunities from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Opportunities. + */ + skip?: number + distinct?: Prisma.OpportunityScalarFieldEnum | Prisma.OpportunityScalarFieldEnum[] +} + +/** + * Opportunity create + */ +export type OpportunityCreateArgs = { + /** + * 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 + /** + * The data needed to create a Opportunity. + */ + data: Prisma.XOR +} + +/** + * Opportunity createMany + */ +export type OpportunityCreateManyArgs = { + /** + * The data used to create many Opportunities. + */ + data: Prisma.OpportunityCreateManyInput | Prisma.OpportunityCreateManyInput[] + skipDuplicates?: boolean +} + +/** + * Opportunity createManyAndReturn + */ +export type OpportunityCreateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the Opportunity + */ + select?: Prisma.OpportunitySelectCreateManyAndReturn | null + /** + * Omit specific fields from the Opportunity + */ + omit?: Prisma.OpportunityOmit | null + /** + * The data used to create many Opportunities. + */ + data: Prisma.OpportunityCreateManyInput | Prisma.OpportunityCreateManyInput[] + skipDuplicates?: boolean + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.OpportunityIncludeCreateManyAndReturn | null +} + +/** + * Opportunity update + */ +export type OpportunityUpdateArgs = { + /** + * 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 + /** + * The data needed to update a Opportunity. + */ + data: Prisma.XOR + /** + * Choose, which Opportunity to update. + */ + where: Prisma.OpportunityWhereUniqueInput +} + +/** + * Opportunity updateMany + */ +export type OpportunityUpdateManyArgs = { + /** + * The data used to update Opportunities. + */ + data: Prisma.XOR + /** + * Filter which Opportunities to update + */ + where?: Prisma.OpportunityWhereInput + /** + * Limit how many Opportunities to update. + */ + limit?: number +} + +/** + * Opportunity updateManyAndReturn + */ +export type OpportunityUpdateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the Opportunity + */ + select?: Prisma.OpportunitySelectUpdateManyAndReturn | null + /** + * Omit specific fields from the Opportunity + */ + omit?: Prisma.OpportunityOmit | null + /** + * The data used to update Opportunities. + */ + data: Prisma.XOR + /** + * Filter which Opportunities to update + */ + where?: Prisma.OpportunityWhereInput + /** + * Limit how many Opportunities to update. + */ + limit?: number + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.OpportunityIncludeUpdateManyAndReturn | null +} + +/** + * Opportunity upsert + */ +export type OpportunityUpsertArgs = { + /** + * 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 + /** + * The filter to search for the Opportunity to update in case it exists. + */ + where: Prisma.OpportunityWhereUniqueInput + /** + * In case the Opportunity found by the `where` argument doesn't exist, create a new Opportunity with this data. + */ + create: Prisma.XOR + /** + * In case the Opportunity was found with the provided `where` argument, update it with this data. + */ + update: Prisma.XOR +} + +/** + * Opportunity delete + */ +export type OpportunityDeleteArgs = { + /** + * 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 + /** + * Filter which Opportunity to delete. + */ + where: Prisma.OpportunityWhereUniqueInput +} + +/** + * Opportunity deleteMany + */ +export type OpportunityDeleteManyArgs = { + /** + * Filter which Opportunities to delete + */ + where?: Prisma.OpportunityWhereInput + /** + * Limit how many Opportunities to delete. + */ + limit?: number +} + +/** + * Opportunity.company + */ +export type Opportunity$companyArgs = { + /** + * 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 +} + +/** + * Opportunity without action + */ +export type OpportunityDefaultArgs = { + /** + * 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 +} diff --git a/prisma/migrations/20260226050000_add_opportunity/migration.sql b/prisma/migrations/20260226050000_add_opportunity/migration.sql new file mode 100644 index 0000000..338e1ca --- /dev/null +++ b/prisma/migrations/20260226050000_add_opportunity/migration.sql @@ -0,0 +1,57 @@ +-- CreateTable +CREATE TABLE "Opportunity" ( + "id" TEXT NOT NULL, + "cwOpportunityId" INTEGER NOT NULL, + "name" TEXT NOT NULL, + "notes" TEXT, + "typeName" TEXT, + "typeCwId" INTEGER, + "stageName" TEXT, + "stageCwId" INTEGER, + "statusName" TEXT, + "statusCwId" INTEGER, + "priorityName" TEXT, + "priorityCwId" INTEGER, + "ratingName" TEXT, + "ratingCwId" INTEGER, + "source" TEXT, + "campaignName" TEXT, + "campaignCwId" INTEGER, + "primarySalesRepName" TEXT, + "primarySalesRepIdentifier" TEXT, + "primarySalesRepCwId" INTEGER, + "secondarySalesRepName" TEXT, + "secondarySalesRepIdentifier" TEXT, + "secondarySalesRepCwId" INTEGER, + "companyCwId" INTEGER, + "companyName" TEXT, + "contactCwId" INTEGER, + "contactName" TEXT, + "siteCwId" INTEGER, + "siteName" TEXT, + "customerPO" TEXT, + "totalSalesTax" DOUBLE PRECISION NOT NULL DEFAULT 0, + "locationName" TEXT, + "locationCwId" INTEGER, + "departmentName" TEXT, + "departmentCwId" INTEGER, + "expectedCloseDate" TIMESTAMP(3), + "pipelineChangeDate" TIMESTAMP(3), + "dateBecameLead" TIMESTAMP(3), + "closedDate" TIMESTAMP(3), + "closedFlag" BOOLEAN NOT NULL DEFAULT false, + "closedByName" TEXT, + "closedByCwId" INTEGER, + "companyId" TEXT, + "cwLastUpdated" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Opportunity_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Opportunity_cwOpportunityId_key" ON "Opportunity"("cwOpportunityId"); + +-- AddForeignKey +ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 11cc770..bf06079 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -75,8 +75,9 @@ model Company { cw_CompanyId Int @unique cw_Identifier String @unique - credentials Credential[] - unifiSites UnifiSite[] + credentials Credential[] + unifiSites UnifiSite[] + opportunities Opportunity[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -85,6 +86,7 @@ model Company { model CatalogItem { id String @id @default(cuid()) cwCatalogId Int @unique + identifier String? @unique name String description String? customerDescription String? @@ -115,6 +117,73 @@ model CatalogItem { updatedAt DateTime @updatedAt } +model Opportunity { + id String @id @default(cuid()) + cwOpportunityId Int @unique + name String + notes String? + + // Stage / status / priority / type / rating stored as JSON references + // so we don't need separate lookup tables for CW enums + typeName String? + typeCwId Int? + stageName String? + stageCwId Int? + statusName String? + statusCwId Int? + priorityName String? + priorityCwId Int? + ratingName String? + ratingCwId Int? + source String? + campaignName String? + campaignCwId Int? + + // Sales rep references + primarySalesRepName String? + primarySalesRepIdentifier String? + primarySalesRepCwId Int? + secondarySalesRepName String? + secondarySalesRepIdentifier String? + secondarySalesRepCwId Int? + + // Company / contact / site + companyCwId Int? + companyName String? + contactCwId Int? + contactName String? + siteCwId Int? + siteName String? + customerPO String? + + // Financials + totalSalesTax Float @default(0) + + // Location / department + locationName String? + locationCwId Int? + departmentName String? + departmentCwId Int? + + // Dates + expectedCloseDate DateTime? + pipelineChangeDate DateTime? + dateBecameLead DateTime? + closedDate DateTime? + closedFlag Boolean @default(false) + closedByName String? + closedByCwId Int? + + // Internal relation to Company (optional, linked by cwCompanyId) + companyId String? + company Company? @relation(fields: [companyId], references: [id]) + + cwLastUpdated DateTime? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + model CredentialType { id String @id @default(cuid()) name String @unique diff --git a/src/api/procurement/[id]/fetch.ts b/src/api/procurement/[id]/fetch.ts new file mode 100644 index 0000000..5f6cc04 --- /dev/null +++ b/src/api/procurement/[id]/fetch.ts @@ -0,0 +1,25 @@ +import { createRoute } from "../../../modules/api-utils/createRoute"; +import { procurement } from "../../../managers/procurement"; +import { apiResponse } from "../../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../../middleware/authorization"; + +/* /v1/procurement/items/:identifier */ +export default createRoute( + "get", + ["/items/:identifier"], + async (c) => { + const identifier = c.req.param("identifier"); + const includeLinkedItems = c.req.query("includeLinkedItems") === "true"; + + const item = await procurement.fetchItem(identifier); + + const response = apiResponse.successful( + "Catalog item fetched successfully!", + item.toJson({ includeLinkedItems }), + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["procurement.catalog.fetch"] }), +); diff --git a/src/api/procurement/[id]/fetchLinked.ts b/src/api/procurement/[id]/fetchLinked.ts new file mode 100644 index 0000000..36a0900 --- /dev/null +++ b/src/api/procurement/[id]/fetchLinked.ts @@ -0,0 +1,25 @@ +import { createRoute } from "../../../modules/api-utils/createRoute"; +import { procurement } from "../../../managers/procurement"; +import { apiResponse } from "../../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../../middleware/authorization"; + +/* GET /v1/procurement/items/:identifier/linked */ +export default createRoute( + "get", + ["/items/:identifier/linked"], + async (c) => { + const identifier = c.req.param("identifier"); + const item = await procurement.fetchItem(identifier); + + const linkedItems = item.getLinkedItems().map((linked) => linked.toJson()); + + const response = apiResponse.successful( + "Linked catalog items fetched successfully!", + linkedItems, + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["procurement.catalog.fetch"] }), +); diff --git a/src/api/procurement/[id]/link.ts b/src/api/procurement/[id]/link.ts new file mode 100644 index 0000000..abe717b --- /dev/null +++ b/src/api/procurement/[id]/link.ts @@ -0,0 +1,28 @@ +import { createRoute } from "../../../modules/api-utils/createRoute"; +import { procurement } from "../../../managers/procurement"; +import { apiResponse } from "../../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../../middleware/authorization"; +import { z } from "zod"; + +/* POST /v1/procurement/items/:identifier/link */ +export default createRoute( + "post", + ["/items/:identifier/link"], + async (c) => { + const identifier = c.req.param("identifier"); + const body = await c.req.json(); + const schema = z.object({ targetId: z.string() }).strict(); + const { targetId } = schema.parse(body); + + const item = await procurement.linkItems(identifier, targetId); + + const response = apiResponse.successful( + "Catalog item linked successfully!", + item.toJson({ includeLinkedItems: true }), + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["procurement.catalog.link"] }), +); diff --git a/src/api/procurement/[id]/refreshInventory.ts b/src/api/procurement/[id]/refreshInventory.ts new file mode 100644 index 0000000..644c3dc --- /dev/null +++ b/src/api/procurement/[id]/refreshInventory.ts @@ -0,0 +1,25 @@ +import { createRoute } from "../../../modules/api-utils/createRoute"; +import { procurement } from "../../../managers/procurement"; +import { apiResponse } from "../../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../../middleware/authorization"; + +/* /v1/procurement/items/:identifier/refresh-inventory */ +export default createRoute( + "post", + ["/items/:identifier/refresh-inventory"], + async (c) => { + const identifier = c.req.param("identifier"); + const item = await procurement.fetchItem(identifier); + + await item.refreshInventory(); + + const response = apiResponse.successful( + "Inventory refreshed successfully!", + item.toJson(), + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["procurement.catalog.inventory.refresh"] }), +); diff --git a/src/api/procurement/[id]/unlink.ts b/src/api/procurement/[id]/unlink.ts new file mode 100644 index 0000000..ed0b88a --- /dev/null +++ b/src/api/procurement/[id]/unlink.ts @@ -0,0 +1,28 @@ +import { createRoute } from "../../../modules/api-utils/createRoute"; +import { procurement } from "../../../managers/procurement"; +import { apiResponse } from "../../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../../middleware/authorization"; +import { z } from "zod"; + +/* POST /v1/procurement/items/:identifier/unlink */ +export default createRoute( + "post", + ["/items/:identifier/unlink"], + async (c) => { + const identifier = c.req.param("identifier"); + const body = await c.req.json(); + const schema = z.object({ targetId: z.string() }).strict(); + const { targetId } = schema.parse(body); + + const item = await procurement.unlinkItems(identifier, targetId); + + const response = apiResponse.successful( + "Catalog item unlinked successfully!", + item.toJson({ includeLinkedItems: true }), + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["procurement.catalog.link"] }), +); diff --git a/src/api/procurement/count.ts b/src/api/procurement/count.ts new file mode 100644 index 0000000..424d70f --- /dev/null +++ b/src/api/procurement/count.ts @@ -0,0 +1,24 @@ +import { createRoute } from "../../modules/api-utils/createRoute"; +import { procurement } from "../../managers/procurement"; +import { apiResponse } from "../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../middleware/authorization"; + +/* /v1/procurement/count */ +export default createRoute( + "get", + ["/count"], + async (c) => { + const activeOnly = c.req.query("activeOnly") === "true"; + + const count = await procurement.count({ activeOnly }); + + const response = apiResponse.successful( + "Catalog item count fetched successfully!", + { count }, + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["procurement.catalog.fetch.many"] }), +); diff --git a/src/api/procurement/fetchAll.ts b/src/api/procurement/fetchAll.ts new file mode 100644 index 0000000..e46ab29 --- /dev/null +++ b/src/api/procurement/fetchAll.ts @@ -0,0 +1,43 @@ +import { createRoute } from "../../modules/api-utils/createRoute"; +import { procurement } from "../../managers/procurement"; +import { apiResponse } from "../../modules/api-utils/apiResponse"; +import { ContentfulStatusCode } from "hono/utils/http-status"; +import { authMiddleware } from "../middleware/authorization"; + +/* /v1/procurement/items */ +export default createRoute( + "get", + ["/items"], + async (c) => { + const page = Number(c.req.query("page") ?? 1); + const rpp = Number(c.req.query("rpp") ?? 30); + const search = c.req.query("search") as string; + const includeInactive = c.req.query("includeInactive") === "true"; + + const data = search + ? await procurement.search(search, page, rpp, { includeInactive }) + : await procurement.fetchPages(page, rpp, { includeInactive }); + + const totalRecords = await procurement.count({ + activeOnly: !includeInactive, + }); + + const response = apiResponse.successful( + "Catalog items fetched successfully!", + data.map((item) => item.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: ["procurement.catalog.fetch.many"] }), +); diff --git a/src/api/procurement/index.ts b/src/api/procurement/index.ts new file mode 100644 index 0000000..b555e3b --- /dev/null +++ b/src/api/procurement/index.ts @@ -0,0 +1,9 @@ +import { default as fetchAll } from "./fetchAll"; +import { default as fetch } from "./[id]/fetch"; +import { default as refreshInventory } from "./[id]/refreshInventory"; +import { default as link } from "./[id]/link"; +import { default as unlink } from "./[id]/unlink"; +import { default as fetchLinked } from "./[id]/fetchLinked"; +import { default as count } from "./count"; + +export { count, fetch, fetchAll, fetchLinked, link, refreshInventory, unlink }; diff --git a/src/api/routers/procurementRouter.ts b/src/api/routers/procurementRouter.ts new file mode 100644 index 0000000..ce93b1b --- /dev/null +++ b/src/api/routers/procurementRouter.ts @@ -0,0 +1,7 @@ +import { Hono } from "hono"; +import * as procurementRoutes from "../procurement"; + +const procurementRouter = new Hono(); +Object.values(procurementRoutes).map((r) => procurementRouter.route("/", r)); + +export default procurementRouter; diff --git a/src/api/routers/salesRouter.ts b/src/api/routers/salesRouter.ts new file mode 100644 index 0000000..f76e21d --- /dev/null +++ b/src/api/routers/salesRouter.ts @@ -0,0 +1,7 @@ +import { Hono } from "hono"; +import * as salesRoutes from "../sales"; + +const salesRouter = new Hono(); +Object.values(salesRoutes).map((r) => salesRouter.route("/", r)); + +export default salesRouter; diff --git a/src/api/sales/[id]/contacts.ts b/src/api/sales/[id]/contacts.ts new file mode 100644 index 0000000..02c9945 --- /dev/null +++ b/src/api/sales/[id]/contacts.ts @@ -0,0 +1,41 @@ +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 { opportunityCw } from "../../../modules/cw-utils/opportunities/opportunities"; + +/* GET /v1/sales/opportunities/:identifier/contacts */ +export default createRoute( + "get", + ["/opportunities/:identifier/contacts"], + async (c) => { + const identifier = c.req.param("identifier"); + const item = await opportunities.fetchItem(identifier); + + const contacts = await opportunityCw.fetchContacts(item.cwOpportunityId); + + const data = contacts.map((ct) => ({ + id: ct.id, + contact: ct.contact ? { id: ct.contact.id, name: ct.contact.name } : null, + company: ct.company + ? { + id: ct.company.id, + identifier: ct.company.identifier, + name: ct.company.name, + } + : null, + role: ct.role ? { id: ct.role.id, name: ct.role.name } : null, + notes: ct.notes, + referralFlag: ct.referralFlag, + })); + + const response = apiResponse.successful( + "Opportunity contacts fetched successfully!", + data, + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["sales.opportunity.fetch"] }), +); diff --git a/src/api/sales/[id]/fetch.ts b/src/api/sales/[id]/fetch.ts new file mode 100644 index 0000000..0da0fb9 --- /dev/null +++ b/src/api/sales/[id]/fetch.ts @@ -0,0 +1,24 @@ +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/:identifier */ +export default createRoute( + "get", + ["/opportunities/:identifier"], + async (c) => { + const identifier = c.req.param("identifier"); + + const item = await opportunities.fetchItem(identifier); + + const response = apiResponse.successful( + "Opportunity fetched successfully!", + item.toJson(), + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["sales.opportunity.fetch"] }), +); diff --git a/src/api/sales/[id]/forecasts.ts b/src/api/sales/[id]/forecasts.ts new file mode 100644 index 0000000..b0abf5b --- /dev/null +++ b/src/api/sales/[id]/forecasts.ts @@ -0,0 +1,39 @@ +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 { opportunityCw } from "../../../modules/cw-utils/opportunities/opportunities"; + +/* GET /v1/sales/opportunities/:identifier/forecasts */ +export default createRoute( + "get", + ["/opportunities/:identifier/forecasts"], + async (c) => { + const identifier = c.req.param("identifier"); + const item = await opportunities.fetchItem(identifier); + + const forecasts = await opportunityCw.fetchForecasts(item.cwOpportunityId); + + const data = forecasts.map((f) => ({ + id: f.id, + forecastType: f.forecastType, + forecastMonth: f.forecastMonth, + revenue: f.revenue, + cost: f.cost, + forecastPercentage: f.forecastPercentage, + status: f.status ? { id: f.status.id, name: f.status.name } : null, + includedFlag: f.includedFlag, + linkedFlag: f.linkedFlag, + recurringFlag: f.recurringFlag, + })); + + const response = apiResponse.successful( + "Opportunity forecasts fetched successfully!", + data, + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["sales.opportunity.fetch"] }), +); diff --git a/src/api/sales/[id]/notes.ts b/src/api/sales/[id]/notes.ts new file mode 100644 index 0000000..a1a4d08 --- /dev/null +++ b/src/api/sales/[id]/notes.ts @@ -0,0 +1,34 @@ +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 { opportunityCw } from "../../../modules/cw-utils/opportunities/opportunities"; + +/* GET /v1/sales/opportunities/:identifier/notes */ +export default createRoute( + "get", + ["/opportunities/:identifier/notes"], + async (c) => { + const identifier = c.req.param("identifier"); + const item = await opportunities.fetchItem(identifier); + + const notes = await opportunityCw.fetchNotes(item.cwOpportunityId); + + const data = notes.map((n) => ({ + id: n.id, + text: n.text, + type: n.type ? { id: n.type.id, name: n.type.name } : null, + flagged: n.flagged, + enteredBy: n.enteredBy, + })); + + const response = apiResponse.successful( + "Opportunity notes fetched successfully!", + data, + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["sales.opportunity.fetch"] }), +); diff --git a/src/api/sales/[id]/refresh.ts b/src/api/sales/[id]/refresh.ts new file mode 100644 index 0000000..b29fab0 --- /dev/null +++ b/src/api/sales/[id]/refresh.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"; + +/* POST /v1/sales/opportunities/:identifier/refresh */ +export default createRoute( + "post", + ["/opportunities/:identifier/refresh"], + async (c) => { + const identifier = c.req.param("identifier"); + const item = await opportunities.fetchItem(identifier); + + const refreshed = await item.refreshFromCW(); + + const response = apiResponse.successful( + "Opportunity refreshed from ConnectWise successfully!", + refreshed.toJson(), + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["sales.opportunity.refresh"] }), +); diff --git a/src/api/sales/count.ts b/src/api/sales/count.ts new file mode 100644 index 0000000..5437c33 --- /dev/null +++ b/src/api/sales/count.ts @@ -0,0 +1,24 @@ +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/count */ +export default createRoute( + "get", + ["/opportunities/count"], + async (c) => { + const openOnly = c.req.query("openOnly") === "true"; + + const count = await opportunities.count({ openOnly }); + + const response = apiResponse.successful( + "Opportunity count fetched successfully!", + { count }, + ); + + return c.json(response, response.status as ContentfulStatusCode); + }, + authMiddleware({ permissions: ["sales.opportunity.fetch.many"] }), +); diff --git a/src/api/sales/fetchAll.ts b/src/api/sales/fetchAll.ts new file mode 100644 index 0000000..fc328c5 --- /dev/null +++ b/src/api/sales/fetchAll.ts @@ -0,0 +1,43 @@ +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 */ +export default createRoute( + "get", + ["/opportunities"], + async (c) => { + const page = Number(c.req.query("page") ?? 1); + const rpp = Number(c.req.query("rpp") ?? 30); + const search = c.req.query("search") as string; + const includeClosed = c.req.query("includeClosed") === "true"; + + const data = search + ? await opportunities.search(search, page, rpp, { includeClosed }) + : await opportunities.fetchPages(page, rpp, { includeClosed }); + + const totalRecords = await opportunities.count({ + openOnly: !includeClosed, + }); + + const response = apiResponse.successful( + "Opportunities fetched successfully!", + data.map((item) => item.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: ["sales.opportunity.fetch.many"] }), +); diff --git a/src/api/sales/index.ts b/src/api/sales/index.ts new file mode 100644 index 0000000..5f30444 --- /dev/null +++ b/src/api/sales/index.ts @@ -0,0 +1,9 @@ +import { default as fetchAll } from "./fetchAll"; +import { default as count } from "./count"; +import { default as fetch } from "./[id]/fetch"; +import { default as refresh } from "./[id]/refresh"; +import { default as forecasts } from "./[id]/forecasts"; +import { default as notes } from "./[id]/notes"; +import { default as contacts } from "./[id]/contacts"; + +export { count, fetch, fetchAll, forecasts, notes, contacts, refresh }; diff --git a/src/api/server.ts b/src/api/server.ts index c686e58..0153666 100644 --- a/src/api/server.ts +++ b/src/api/server.ts @@ -55,6 +55,8 @@ 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); app.route("/v1", v1); export default app; diff --git a/src/controllers/CatalogItemController.ts b/src/controllers/CatalogItemController.ts new file mode 100644 index 0000000..0e6ad46 --- /dev/null +++ b/src/controllers/CatalogItemController.ts @@ -0,0 +1,218 @@ +import { CatalogItem } 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"; + +/** + * Catalog Item Controller + * + * This class encapsulates a catalog item entity and provides domain methods + * for accessing, refreshing, and serializing catalog item data. It bridges + * the internal database representation with ConnectWise catalog data. + */ +export class CatalogItemController { + 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 manufacturer: string | null; + public manufactureCwId: number | null; + public partNumber: string | null; + + public vendorName: string | null; + public vendorSku: string | null; + public vendorCwId: number | null; + + public price: number; + public cost: number; + + public inactive: boolean; + public salesTaxable: boolean; + + public onHand: number; + public cwLastUpdated: Date | null; + + private _linkedItems: CatalogItemController[]; + + public readonly createdAt: Date; + public updatedAt: Date; + + constructor( + itemData: CatalogItem & { + linkedItems?: CatalogItem[]; + }, + ) { + this.id = itemData.id; + 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.manufacturer = itemData.manufacturer; + this.manufactureCwId = itemData.manufactureCwId; + this.partNumber = itemData.partNumber; + this.vendorName = itemData.vendorName; + this.vendorSku = itemData.vendorSku; + this.vendorCwId = itemData.vendorCwId; + this.price = itemData.price; + this.cost = itemData.cost; + this.inactive = itemData.inactive; + this.salesTaxable = itemData.salesTaxable; + this.onHand = itemData.onHand; + this.cwLastUpdated = itemData.cwLastUpdated; + this.createdAt = itemData.createdAt; + this.updatedAt = itemData.updatedAt; + + this._linkedItems = (itemData.linkedItems ?? []).map( + (linked) => new CatalogItemController(linked), + ); + } + + /** + * Refresh Inventory + * + * Fetches the latest on-hand inventory count from ConnectWise + * and updates both the controller state and the database. + * + * @returns {Promise} - The updated controller + */ + public async refreshInventory(): Promise { + const onHand = await catalogCw.fetchInventoryOnHand(this.cwCatalogId); + + if (onHand !== this.onHand) { + await prisma.catalogItem.update({ + where: { id: this.id }, + data: { onHand }, + }); + this.onHand = onHand; + } + + return this; + } + + /** + * Fetch Linked Items + * + * Returns the linked catalog items as an array of controllers. + * + * @returns {CatalogItemController[]} - Array of linked item controllers + */ + public getLinkedItems(): CatalogItemController[] { + return this._linkedItems; + } + + /** + * Link Item + * + * Links another catalog item to this item. The relationship is bidirectional + * via the Prisma implicit many-to-many. + * + * @param targetId - The internal ID of the catalog item to link + * @returns {Promise} - The updated controller + */ + public async linkItem(targetId: string): Promise { + if (targetId === this.id) { + throw new GenericError({ + message: "Cannot link a catalog item to itself", + name: "InvalidLinkTarget", + cause: `Item '${this.id}' cannot be linked to itself`, + status: 400, + }); + } + + const target = await prisma.catalogItem.findFirst({ + where: { id: targetId }, + }); + + if (!target) { + throw new GenericError({ + message: "Target catalog item not found", + name: "CatalogItemNotFound", + cause: `No catalog item exists with ID '${targetId}'`, + status: 404, + }); + } + + const updated = await prisma.catalogItem.update({ + where: { id: this.id }, + data: { + linkedItems: { connect: { id: targetId } }, + }, + include: { linkedItems: true }, + }); + + this._linkedItems = (updated.linkedItems ?? []).map( + (linked) => new CatalogItemController(linked), + ); + + return this; + } + + /** + * Unlink Item + * + * Removes the link between this catalog item and another. + * + * @param targetId - The internal ID of the catalog item to unlink + * @returns {Promise} - The updated controller + */ + public async unlinkItem(targetId: string): Promise { + const updated = await prisma.catalogItem.update({ + where: { id: this.id }, + data: { + linkedItems: { disconnect: { id: targetId } }, + }, + include: { linkedItems: true }, + }); + + this._linkedItems = (updated.linkedItems ?? []).map( + (linked) => new CatalogItemController(linked), + ); + + return this; + } + + /** + * To JSON + * + * Serializes the catalog item into a safe, API-friendly object. + * + * @param opts - Options to control output + * @returns - A JSON-safe representation of the catalog item + */ + public toJson(opts?: { includeLinkedItems?: boolean }): Record { + return { + id: this.id, + cwCatalogId: this.cwCatalogId, + identifier: this.identifier, + name: this.name, + description: this.description, + customerDescription: this.customerDescription, + internalNotes: this.internalNotes, + manufacturer: this.manufacturer, + manufactureCwId: this.manufactureCwId, + partNumber: this.partNumber, + vendorName: this.vendorName, + vendorSku: this.vendorSku, + vendorCwId: this.vendorCwId, + price: this.price, + cost: this.cost, + inactive: this.inactive, + salesTaxable: this.salesTaxable, + onHand: this.onHand, + cwLastUpdated: this.cwLastUpdated, + linkedItems: opts?.includeLinkedItems + ? this._linkedItems.map((item) => item.toJson()) + : undefined, + createdAt: this.createdAt, + updatedAt: this.updatedAt, + }; + } +} diff --git a/src/controllers/CompanyController.ts b/src/controllers/CompanyController.ts index d1cd2ba..6280eeb 100644 --- a/src/controllers/CompanyController.ts +++ b/src/controllers/CompanyController.ts @@ -18,7 +18,7 @@ export class CompanyController { public readonly cw_CompanyId: number; public readonly cw_Data?: { company: CWCompany; - defaultContact: Contact; + defaultContact: Contact | null; allContacts: Contact[]; }; @@ -96,23 +96,25 @@ export class CompanyController { }, primaryContact: !opts?.includePrimaryContact ? undefined - : { - 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 - ); - })(), - }, + : 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) => ({ diff --git a/src/controllers/OpportunityController.ts b/src/controllers/OpportunityController.ts new file mode 100644 index 0000000..a4b3b07 --- /dev/null +++ b/src/controllers/OpportunityController.ts @@ -0,0 +1,290 @@ +import { Opportunity } from "../../generated/prisma/client"; +import { prisma } from "../constants"; +import { fetchOpportunity } from "../modules/cw-utils/opportunities/fetchOpportunity"; +import { CWOpportunity } from "../modules/cw-utils/opportunities/opportunity.types"; + +/** + * Opportunity Controller + * + * Domain model class that encapsulates an Opportunity entity and provides + * methods for accessing, refreshing from ConnectWise, and serializing + * opportunity data. + */ +export class OpportunityController { + public readonly id: string; + public readonly cwOpportunityId: number; + public name: string; + public notes: string | null; + + public typeName: string | null; + public typeCwId: number | null; + public stageName: string | null; + public stageCwId: number | null; + public statusName: string | null; + public statusCwId: number | null; + public priorityName: string | null; + public priorityCwId: number | null; + public ratingName: string | null; + public ratingCwId: number | null; + public source: string | null; + public campaignName: string | null; + public campaignCwId: number | null; + + public primarySalesRepName: string | null; + public primarySalesRepIdentifier: string | null; + public primarySalesRepCwId: number | null; + public secondarySalesRepName: string | null; + public secondarySalesRepIdentifier: string | null; + public secondarySalesRepCwId: number | null; + + public companyCwId: number | null; + public companyName: string | null; + public contactCwId: number | null; + public contactName: string | null; + public siteCwId: number | null; + public siteName: string | null; + public customerPO: string | null; + + public totalSalesTax: number; + + public locationName: string | null; + public locationCwId: number | null; + public departmentName: string | null; + public departmentCwId: number | null; + + public expectedCloseDate: Date | null; + public pipelineChangeDate: Date | null; + public dateBecameLead: Date | null; + public closedDate: Date | null; + public closedFlag: boolean; + public closedByName: string | null; + public closedByCwId: number | null; + + public companyId: string | null; + public cwLastUpdated: Date | null; + + public readonly createdAt: Date; + public updatedAt: Date; + + constructor(data: Opportunity) { + this.id = data.id; + this.cwOpportunityId = data.cwOpportunityId; + this.name = data.name; + this.notes = data.notes; + + this.typeName = data.typeName; + this.typeCwId = data.typeCwId; + this.stageName = data.stageName; + this.stageCwId = data.stageCwId; + this.statusName = data.statusName; + this.statusCwId = data.statusCwId; + this.priorityName = data.priorityName; + this.priorityCwId = data.priorityCwId; + this.ratingName = data.ratingName; + this.ratingCwId = data.ratingCwId; + this.source = data.source; + this.campaignName = data.campaignName; + this.campaignCwId = data.campaignCwId; + + this.primarySalesRepName = data.primarySalesRepName; + this.primarySalesRepIdentifier = data.primarySalesRepIdentifier; + this.primarySalesRepCwId = data.primarySalesRepCwId; + this.secondarySalesRepName = data.secondarySalesRepName; + this.secondarySalesRepIdentifier = data.secondarySalesRepIdentifier; + this.secondarySalesRepCwId = data.secondarySalesRepCwId; + + this.companyCwId = data.companyCwId; + this.companyName = data.companyName; + this.contactCwId = data.contactCwId; + this.contactName = data.contactName; + this.siteCwId = data.siteCwId; + this.siteName = data.siteName; + this.customerPO = data.customerPO; + + this.totalSalesTax = data.totalSalesTax; + + this.locationName = data.locationName; + this.locationCwId = data.locationCwId; + this.departmentName = data.departmentName; + this.departmentCwId = data.departmentCwId; + + this.expectedCloseDate = data.expectedCloseDate; + this.pipelineChangeDate = data.pipelineChangeDate; + this.dateBecameLead = data.dateBecameLead; + this.closedDate = data.closedDate; + this.closedFlag = data.closedFlag; + this.closedByName = data.closedByName; + this.closedByCwId = data.closedByCwId; + + this.companyId = data.companyId; + this.cwLastUpdated = data.cwLastUpdated; + + this.createdAt = data.createdAt; + this.updatedAt = data.updatedAt; + } + + /** + * Refresh from ConnectWise + * + * Fetches the latest opportunity data from CW and updates + * the local database record and controller state. + */ + public async refreshFromCW(): Promise { + const cwData = await fetchOpportunity(this.cwOpportunityId); + const mapped = OpportunityController.mapCwToDb(cwData); + + const updated = await prisma.opportunity.update({ + where: { id: this.id }, + data: mapped, + }); + + return new OpportunityController(updated); + } + + /** + * Fetch raw CW data + * + * Returns the raw ConnectWise opportunity object without updating the DB. + */ + public async fetchCwData(): Promise { + return fetchOpportunity(this.cwOpportunityId); + } + + /** + * Map CW Opportunity → Prisma create/update payload + * + * Static helper used by both the controller and the refresh sync. + */ + public static mapCwToDb(item: CWOpportunity) { + return { + name: item.name, + notes: item.notes ?? null, + + typeName: item.type?.name ?? null, + typeCwId: item.type?.id ?? null, + stageName: item.stage?.name ?? null, + stageCwId: item.stage?.id ?? null, + statusName: item.status?.name ?? null, + statusCwId: item.status?.id ?? null, + priorityName: item.priority?.name ?? null, + priorityCwId: item.priority?.id ?? null, + ratingName: item.rating?.name ?? null, + ratingCwId: item.rating?.id ?? null, + source: item.source ?? null, + campaignName: item.campaign?.name ?? null, + campaignCwId: item.campaign?.id ?? null, + + primarySalesRepName: item.primarySalesRep?.name ?? null, + primarySalesRepIdentifier: item.primarySalesRep?.identifier ?? null, + primarySalesRepCwId: item.primarySalesRep?.id ?? null, + secondarySalesRepName: item.secondarySalesRep?.name ?? null, + secondarySalesRepIdentifier: item.secondarySalesRep?.identifier ?? null, + secondarySalesRepCwId: item.secondarySalesRep?.id ?? null, + + companyCwId: item.company?.id ?? null, + companyName: item.company?.name ?? null, + contactCwId: item.contact?.id ?? null, + contactName: item.contact?.name ?? null, + siteCwId: item.site?.id ?? null, + siteName: item.site?.name ?? null, + customerPO: item.customerPO ?? null, + + totalSalesTax: item.totalSalesTax ?? 0, + + locationName: item.location?.name ?? null, + locationCwId: item.location?.id ?? null, + departmentName: item.department?.name ?? null, + departmentCwId: item.department?.id ?? null, + + expectedCloseDate: item.expectedCloseDate + ? new Date(item.expectedCloseDate) + : null, + pipelineChangeDate: item.pipelineChangeDate + ? new Date(item.pipelineChangeDate) + : null, + dateBecameLead: item.dateBecameLead + ? new Date(item.dateBecameLead) + : null, + closedDate: item.closedDate ? new Date(item.closedDate) : null, + closedFlag: item.closedFlag ?? false, + closedByName: item.closedBy?.name ?? null, + closedByCwId: item.closedBy?.id ?? null, + + cwLastUpdated: item._info?.lastUpdated + ? new Date(item._info.lastUpdated) + : new Date(), + }; + } + + /** + * To JSON + * + * Serializes the opportunity into a safe, API-friendly object. + */ + public toJson(): Record { + return { + id: this.id, + cwOpportunityId: this.cwOpportunityId, + name: this.name, + notes: this.notes, + type: this.typeCwId ? { id: this.typeCwId, name: this.typeName } : null, + stage: this.stageCwId + ? { id: this.stageCwId, name: this.stageName } + : null, + status: this.statusCwId + ? { id: this.statusCwId, name: this.statusName } + : null, + priority: this.priorityCwId + ? { id: this.priorityCwId, name: this.priorityName } + : null, + rating: this.ratingCwId + ? { id: this.ratingCwId, name: this.ratingName } + : null, + source: this.source, + campaign: this.campaignCwId + ? { id: this.campaignCwId, name: this.campaignName } + : null, + primarySalesRep: this.primarySalesRepCwId + ? { + id: this.primarySalesRepCwId, + identifier: this.primarySalesRepIdentifier, + name: this.primarySalesRepName, + } + : null, + secondarySalesRep: this.secondarySalesRepCwId + ? { + id: this.secondarySalesRepCwId, + identifier: this.secondarySalesRepIdentifier, + name: this.secondarySalesRepName, + } + : null, + company: this.companyCwId + ? { id: this.companyCwId, name: this.companyName } + : null, + contact: this.contactCwId + ? { id: this.contactCwId, name: this.contactName } + : null, + site: this.siteCwId ? { id: this.siteCwId, name: this.siteName } : null, + customerPO: this.customerPO, + totalSalesTax: this.totalSalesTax, + location: this.locationCwId + ? { id: this.locationCwId, name: this.locationName } + : null, + department: this.departmentCwId + ? { id: this.departmentCwId, name: this.departmentName } + : null, + expectedCloseDate: this.expectedCloseDate, + pipelineChangeDate: this.pipelineChangeDate, + dateBecameLead: this.dateBecameLead, + closedDate: this.closedDate, + closedFlag: this.closedFlag, + closedBy: this.closedByCwId + ? { id: this.closedByCwId, name: this.closedByName } + : null, + companyId: this.companyId, + cwLastUpdated: this.cwLastUpdated, + createdAt: this.createdAt, + updatedAt: this.updatedAt, + }; + } +} diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index b6a32f1..f3e04a1 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -178,6 +178,46 @@ export default class UserController { return decoded.permissions; } + /** + * Read Role Permissions + * + * Verifies and decodes a role permissions JWT and returns the permission nodes. + * Returns an empty array if verification fails. + * + * @param role - Role record containing the signed permissions token + * @returns {string[]} The role permission nodes + */ + private _readRolePermissions(role: Role): string[] { + try { + const decoded = jwt.verify(role.permissions, permissionsPrivateKey, { + algorithms: ["RS256"], + issuer: "roles", + subject: role.id, + }) as DecodedPermissionsBlock; + + return decoded.permissions; + } catch { + return []; + } + } + + /** + * Read All Permissions + * + * Aggregates the user's direct permissions and all permissions from their assigned roles + * into a single deduplicated array. + * + * @returns {Promise} Combined array of all permission nodes + */ + public async readAllPermissions(): Promise { + const directPermissions = this.readPermissions(); + const rolePermissions = this._roles + .map((role) => this._readRolePermissions(role)) + .flatMap((permissions) => permissions); + + return [...new Set([...directPermissions, ...rolePermissions])]; + } + /** * Fetch Roles * @@ -262,7 +302,16 @@ export default class UserController { : this._roles.size > 0 ? this._roles.map((v) => v.moniker) : undefined, - permissions: opts?.safeReturn ? undefined : this.readPermissions(), + permissions: opts?.safeReturn + ? undefined + : (() => { + const directPermissions = this.readPermissions(); + const rolePermissions = this._roles + .map((role) => this._readRolePermissions(role)) + .flatMap((permissions) => permissions); + + return [...new Set([...directPermissions, ...rolePermissions])]; + })(), login: opts?.safeReturn ? undefined : this.login, email: opts?.safeReturn ? undefined : this.email, image: this.image, diff --git a/src/index.ts b/src/index.ts index d01791e..e7e6ec2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ 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 { refreshOpportunities } from "./modules/cw-utils/opportunities/refreshOpportunities"; import { events, setupEventDebugger } from "./modules/globalEvents"; import { signPermissions } from "./modules/permission-utils/signPermissions"; import { RoleController } from "./controllers/RoleController"; @@ -65,6 +66,12 @@ setInterval( 2 * 60 * 1000, ); +// Refresh opportunities every minute +await refreshOpportunities(); +setInterval(() => { + return refreshOpportunities(); +}, 60 * 1000); + await unifiSites.syncSites(); setInterval(() => { return unifiSites.syncSites(); diff --git a/src/managers/companies.ts b/src/managers/companies.ts index 56032b4..3aad4e4 100644 --- a/src/managers/companies.ts +++ b/src/managers/companies.ts @@ -15,16 +15,19 @@ export const companies = { const freshCwData: { data: Company } = await connectWiseApi.get( `/company/companies/${search.cw_CompanyId}`, ); - const defaultContactData = await connectWiseApi.get( - (freshCwData.data as Company).defaultContact._info.contact_href, - ); + + 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, + defaultContact: defaultContactData?.data ?? null, allContacts: allContactsData.data, }); }, diff --git a/src/managers/opportunities.ts b/src/managers/opportunities.ts new file mode 100644 index 0000000..c92f9a6 --- /dev/null +++ b/src/managers/opportunities.ts @@ -0,0 +1,138 @@ +import { prisma } from "../constants"; +import { OpportunityController } from "../controllers/OpportunityController"; +import GenericError from "../Errors/GenericError"; + +export const opportunities = { + /** + * Fetch Opportunity + * + * Fetch an opportunity by its internal ID or ConnectWise opportunity ID + * and return an OpportunityController instance. + * + * @param identifier - The internal ID (string) or CW opportunity ID (number) + * @returns {Promise} + */ + async fetchItem(identifier: string | number): Promise { + const isNumeric = + typeof identifier === "number" || /^\d+$/.test(String(identifier)); + + const item = await prisma.opportunity.findFirst({ + where: isNumeric + ? { cwOpportunityId: Number(identifier) } + : { id: identifier as string }, + }); + + if (!item) { + throw new GenericError({ + message: "Opportunity not found", + name: "OpportunityNotFound", + cause: `No opportunity exists with identifier '${identifier}'`, + status: 404, + }); + } + + return new OpportunityController(item); + }, + + /** + * Fetch All Opportunities (Paginated) + * + * @param page - Page number (1-based) + * @param rpp - Records per page + * @param opts - Optional filters + * @returns {Promise} + */ + async fetchPages( + page: number, + rpp: number, + opts?: { includeClosed?: boolean }, + ): Promise { + const skip = (Math.max(page, 1) - 1) * rpp; + + const items = await prisma.opportunity.findMany({ + where: opts?.includeClosed ? undefined : { closedFlag: false }, + skip, + take: rpp, + orderBy: { expectedCloseDate: "asc" }, + }); + + return items.map((item) => new OpportunityController(item)); + }, + + /** + * Search Opportunities + * + * Search opportunities by name, company name, contact name, notes, + * sales rep, or status with pagination support. + * + * @param query - Search query string + * @param page - Page number (1-based) + * @param rpp - Records per page + * @param opts - Optional filters + * @returns {Promise} + */ + async search( + query: string, + page: number, + rpp: number, + opts?: { includeClosed?: boolean }, + ): Promise { + const skip = (Math.max(page, 1) - 1) * rpp; + + const items = await prisma.opportunity.findMany({ + where: { + ...(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" } }, + ], + }, + skip, + take: rpp, + orderBy: { expectedCloseDate: "asc" }, + }); + + return items.map((item) => new OpportunityController(item)); + }, + + /** + * Count Opportunities + * + * @param opts - Optional filters + * @returns {Promise} + */ + async count(opts?: { openOnly?: boolean }): Promise { + return prisma.opportunity.count({ + where: opts?.openOnly ? { closedFlag: false } : undefined, + }); + }, + + /** + * Fetch Opportunities by Company + * + * Fetch all opportunities for a company by its internal company ID. + * + * @param companyId - The internal company ID + * @param opts - Optional filters + * @returns {Promise} + */ + async fetchByCompany( + companyId: string, + opts?: { includeClosed?: boolean }, + ): Promise { + const items = await prisma.opportunity.findMany({ + where: { + companyId, + ...(opts?.includeClosed ? {} : { closedFlag: false }), + }, + orderBy: { expectedCloseDate: "asc" }, + }); + + return items.map((item) => new OpportunityController(item)); + }, +}; diff --git a/src/managers/procurement.ts b/src/managers/procurement.ts new file mode 100644 index 0000000..cd11871 --- /dev/null +++ b/src/managers/procurement.ts @@ -0,0 +1,171 @@ +import { prisma } from "../constants"; +import { CatalogItemController } from "../controllers/CatalogItemController"; +import GenericError from "../Errors/GenericError"; + +/** + * Standard include clause used by catalog item queries. + * Includes one level of linked items. + */ +const catalogItemInclude = { + linkedItems: true, +} as const; + +export const procurement = { + /** + * Fetch Catalog Item + * + * Fetch a catalog item by its internal ID or ConnectWise catalog ID + * and return a CatalogItemController instance. + * + * @param identifier - The internal ID (string) or CW catalog ID (number) + * @returns {Promise} - The catalog item controller + */ + async fetchItem(identifier: string | number): Promise { + const isNumeric = + typeof identifier === "number" || /^\d+$/.test(String(identifier)); + + const item = await prisma.catalogItem.findFirst({ + where: isNumeric + ? { cwCatalogId: Number(identifier) } + : { + OR: [ + { id: identifier as string }, + { identifier: identifier as string }, + ], + }, + include: catalogItemInclude, + }); + + if (!item) { + throw new GenericError({ + message: "Catalog item not found", + name: "CatalogItemNotFound", + cause: `No catalog item exists with identifier '${identifier}'`, + status: 404, + }); + } + + return new CatalogItemController(item); + }, + + /** + * Fetch All Catalog Items (Paginated) + * + * Fetch pages of catalog items for pagination. + * + * @param page - Page number (1-based) + * @param rpp - Records per page + * @returns {Promise} - Array of catalog item controllers + */ + async fetchPages( + page: number, + rpp: number, + opts?: { includeInactive?: boolean }, + ): Promise { + const skip = (Math.max(page, 1) - 1) * rpp; + const take = rpp; + + const items = await prisma.catalogItem.findMany({ + where: opts?.includeInactive ? undefined : { inactive: false }, + skip, + take, + include: catalogItemInclude, + orderBy: { name: "asc" }, + }); + + return items.map((item) => new CatalogItemController(item)); + }, + + /** + * Search Catalog Items + * + * Search catalog items by name, description, part number, or vendor SKU + * with pagination support. + * + * @param query - Search query string + * @param page - Page number (1-based) + * @param rpp - Records per page + * @returns {Promise} - Array of matching catalog item controllers + */ + async search( + query: string, + page: number, + rpp: number, + opts?: { includeInactive?: boolean }, + ): Promise { + const skip = (Math.max(page, 1) - 1) * rpp; + const take = rpp; + + const items = await prisma.catalogItem.findMany({ + where: { + ...(opts?.includeInactive ? {} : { inactive: false }), + OR: [ + { identifier: { contains: query, mode: "insensitive" } }, + { name: { contains: query, mode: "insensitive" } }, + { description: { contains: query, mode: "insensitive" } }, + { partNumber: { contains: query, mode: "insensitive" } }, + { vendorSku: { contains: query, mode: "insensitive" } }, + { manufacturer: { contains: query, mode: "insensitive" } }, + ], + }, + skip, + take, + include: catalogItemInclude, + orderBy: { name: "asc" }, + }); + + return items.map((item) => new CatalogItemController(item)); + }, + + /** + * Count Catalog Items + * + * Returns the total number of catalog items in the database. + * + * @param opts - Optional filters + * @returns {Promise} - Total count + */ + async count(opts?: { activeOnly?: boolean }): Promise { + return prisma.catalogItem.count({ + where: opts?.activeOnly ? { inactive: false } : undefined, + }); + }, + + /** + * Link Catalog Items + * + * Links a target catalog item to a source catalog item. + * + * @param sourceIdentifier - The source item's internal ID, identifier, or CW catalog ID + * @param targetIdentifier - The target item's internal ID, identifier, or CW catalog ID + * @returns {Promise} - The updated source controller with linked items + */ + async linkItems( + sourceIdentifier: string | number, + targetIdentifier: string | number, + ): Promise { + const source = await procurement.fetchItem(sourceIdentifier); + const target = await procurement.fetchItem(targetIdentifier); + + return source.linkItem(target.id); + }, + + /** + * Unlink Catalog Items + * + * Removes the link between a source catalog item and a target catalog item. + * + * @param sourceIdentifier - The source item's internal ID, identifier, or CW catalog ID + * @param targetIdentifier - The target item's internal ID, identifier, or CW catalog ID + * @returns {Promise} - The updated source controller + */ + async unlinkItems( + sourceIdentifier: string | number, + targetIdentifier: string | number, + ): Promise { + const source = await procurement.fetchItem(sourceIdentifier); + const target = await procurement.fetchItem(targetIdentifier); + + return source.unlinkItem(target.id); + }, +}; diff --git a/src/modules/cw-utils/opportunities/fetchAllOpportunities.ts b/src/modules/cw-utils/opportunities/fetchAllOpportunities.ts new file mode 100644 index 0000000..d6a4ca5 --- /dev/null +++ b/src/modules/cw-utils/opportunities/fetchAllOpportunities.ts @@ -0,0 +1,28 @@ +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/src/modules/cw-utils/opportunities/fetchCompanyOpportunities.ts b/src/modules/cw-utils/opportunities/fetchCompanyOpportunities.ts new file mode 100644 index 0000000..5fd73fb --- /dev/null +++ b/src/modules/cw-utils/opportunities/fetchCompanyOpportunities.ts @@ -0,0 +1,31 @@ +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/src/modules/cw-utils/opportunities/fetchOpportunity.ts b/src/modules/cw-utils/opportunities/fetchOpportunity.ts new file mode 100644 index 0000000..3f19e5b --- /dev/null +++ b/src/modules/cw-utils/opportunities/fetchOpportunity.ts @@ -0,0 +1,30 @@ +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/src/modules/cw-utils/opportunities/opportunities.ts b/src/modules/cw-utils/opportunities/opportunities.ts new file mode 100644 index 0000000..2ef372b --- /dev/null +++ b/src/modules/cw-utils/opportunities/opportunities.ts @@ -0,0 +1,145 @@ +import { Collection } from "@discordjs/collection"; +import { connectWiseApi } from "../../../constants"; +import { + CWOpportunity, + CWOpportunitySummary, + CWForecastItem, + CWOpportunityNote, + CWOpportunityContact, +} from "./opportunity.types"; + +export const opportunityCw = { + /** + * Count Opportunities + * + * Returns the total number of opportunities in ConnectWise. + * Optionally accepts CW conditions string for filtered counts. + */ + countItems: async (conditions?: string): Promise => { + const query = conditions + ? `/sales/opportunities/count?conditions=${encodeURIComponent(conditions)}` + : "/sales/opportunities/count"; + const response = await connectWiseApi.get(query); + return response.data.count; + }, + + /** + * Fetch All Opportunity Summaries + * + * Lightweight fetch returning only id and _info (for lastUpdated comparison). + * Paginates through all opportunities. + */ + fetchAllSummaries: async (): Promise< + Collection + > => { + const allItems = new Collection(); + const pageSize = 1000; + + const count = await opportunityCw.countItems(); + const totalPages = Math.ceil(count / pageSize); + + for (let page = 0; page < totalPages; page++) { + const response = await connectWiseApi.get( + `/sales/opportunities?page=${page + 1}&pageSize=${pageSize}&fields=id,_info`, + ); + const items: CWOpportunitySummary[] = response.data; + + for (const item of items) { + allItems.set(item.id, item); + } + } + + return allItems; + }, + + /** + * Fetch All Opportunities (Full) + * + * Fetches all opportunities with complete data. Paginates through + * the full list. + */ + fetchAll: async ( + conditions?: string, + ): Promise> => { + const allItems = new Collection(); + const pageSize = 1000; + + const count = await opportunityCw.countItems(conditions); + const totalPages = Math.ceil(count / pageSize); + + for (let page = 0; page < totalPages; page++) { + const conditionsParam = conditions + ? `&conditions=${encodeURIComponent(conditions)}` + : ""; + const response = await connectWiseApi.get( + `/sales/opportunities?page=${page + 1}&pageSize=${pageSize}${conditionsParam}`, + ); + const items: CWOpportunity[] = response.data; + + for (const item of items) { + allItems.set(item.id, item); + } + } + + return allItems; + }, + + /** + * Fetch Single Opportunity + * + * Fetches a single opportunity by its ConnectWise ID. + */ + fetch: async (id: number): Promise => { + const response = await connectWiseApi.get(`/sales/opportunities/${id}`); + return response.data; + }, + + /** + * Fetch Opportunities by Company + * + * Fetches all opportunities associated with a specific ConnectWise company ID. + */ + fetchByCompany: async ( + cwCompanyId: number, + ): Promise> => { + return opportunityCw.fetchAll(`company/id=${cwCompanyId}`); + }, + + /** + * Fetch Opportunity Forecasts + * + * Fetches forecast/revenue items for a given opportunity. + */ + fetchForecasts: async (opportunityId: number): Promise => { + const response = await connectWiseApi.get( + `/sales/opportunities/${opportunityId}/forecast`, + ); + return response.data; + }, + + /** + * Fetch Opportunity Notes + * + * Fetches notes associated with a given opportunity. + */ + fetchNotes: async (opportunityId: number): Promise => { + const response = await connectWiseApi.get( + `/sales/opportunities/${opportunityId}/notes`, + ); + return response.data; + }, + + /** + * Fetch Opportunity Contacts + * + * Fetches contacts associated with a given opportunity. + */ + fetchContacts: async ( + opportunityId: number, + ): Promise => { + const response = await connectWiseApi.get( + `/sales/opportunities/${opportunityId}/contacts`, + ); + return response.data; + }, +}; diff --git a/src/modules/cw-utils/opportunities/opportunity.types.ts b/src/modules/cw-utils/opportunities/opportunity.types.ts new file mode 100644 index 0000000..8ac7768 --- /dev/null +++ b/src/modules/cw-utils/opportunities/opportunity.types.ts @@ -0,0 +1,144 @@ +interface CWReference { + id: number; + name: string; + _info?: Record; +} + +interface CWMemberReference { + id: number; + identifier: string; + name: string; + _info?: Record; +} + +interface CWCompanyReference { + id: number; + identifier: string; + name: string; + _info?: Record; +} + +interface CWContactReference { + id: number; + name: string; + _info?: Record; +} + +interface CWSiteReference { + id: number; + name: string; + _info?: Record; +} + +interface CWCustomField { + id: number; + caption: string; + type: string; + entryMethod: string; + numberOfDecimals: number; + value: unknown; + connectWiseId: string; + rowNum: number; + userDefinedFieldRecId: number; + podId: string; +} + +export interface CWOpportunity { + id: number; + name: string; + expectedCloseDate: string; + type: CWReference; + stage: CWReference; + status: CWReference; + priority: CWReference; + notes: string; + source: string; + rating: CWReference; + campaign: CWReference; + primarySalesRep: CWMemberReference; + secondarySalesRep: CWMemberReference; + locationId: number; + businessUnitId: number; + company: CWCompanyReference; + contact: CWContactReference; + site: CWSiteReference; + customerPO: string; + pipelineChangeDate: string; + dateBecameLead: string; + closedDate: string; + closedBy: CWMemberReference; + totalSalesTax: number; + shipToCompany: CWCompanyReference; + shipToContact: CWContactReference; + shipToSite: CWSiteReference; + billToCompany: CWCompanyReference; + billToContact: CWContactReference; + billToSite: CWSiteReference; + billingTerms: CWReference; + taxCode: CWReference; + currency: CWReference; + companyLocationId: number; + location: CWReference; + department: CWReference; + closedFlag: boolean; + mobileGuid: string; + customFields: CWCustomField[]; + _info: CWOpportunityInfo; +} + +export interface CWOpportunityInfo { + lastUpdated: string; + updatedBy: string; + dateEntered: string; + enteredBy: string; + forecasts_href: string; + notes_href: string; + products_href: string; + contacts_href: string; + configurations_href: string; + team_href: string; + documents_href: string; + activities_href: string; +} + +export interface CWForecastItem { + id: number; + opportunity: CWReference; + forecastType: string; + forecastMonth: string; + revenue: number; + cost: number; + forecastPercentage: number; + status: CWReference; + includedFlag: boolean; + linkedFlag: boolean; + recurringFlag: boolean; + _info?: Record; +} + +export interface CWOpportunityNote { + id: number; + opportunity: CWReference; + text: string; + type: CWReference; + flagged: boolean; + enteredBy: string; + mobileGuid: string; + _info?: Record; +} + +export interface CWOpportunityContact { + id: number; + opportunity: CWReference; + contact: CWContactReference; + company: CWCompanyReference; + role: CWReference; + notes: string; + referralFlag: boolean; + _info?: Record; +} + +export interface CWOpportunitySummary { + id: number; + _info?: Record; +} diff --git a/src/modules/cw-utils/opportunities/processOpportunityResponse.ts b/src/modules/cw-utils/opportunities/processOpportunityResponse.ts new file mode 100644 index 0000000..67ff098 --- /dev/null +++ b/src/modules/cw-utils/opportunities/processOpportunityResponse.ts @@ -0,0 +1,88 @@ +import { CWOpportunity } from "./opportunity.types"; + +export type ProcessedOpportunity = ReturnType< + typeof processOpportunityResponse +>; + +/** + * Processes raw CW opportunity data into a cleaner, normalized shape + * suitable for API responses and internal consumption. + */ +export const processOpportunityResponse = (opportunity: CWOpportunity) => ({ + id: opportunity.id, + name: opportunity.name, + expectedCloseDate: opportunity.expectedCloseDate, + closedDate: opportunity.closedDate, + closedFlag: opportunity.closedFlag, + type: opportunity.type + ? { id: opportunity.type.id, name: opportunity.type.name } + : null, + stage: opportunity.stage + ? { id: opportunity.stage.id, name: opportunity.stage.name } + : null, + status: opportunity.status + ? { id: opportunity.status.id, name: opportunity.status.name } + : null, + priority: opportunity.priority + ? { id: opportunity.priority.id, name: opportunity.priority.name } + : null, + rating: opportunity.rating + ? { id: opportunity.rating.id, name: opportunity.rating.name } + : null, + source: opportunity.source, + notes: opportunity.notes, + customerPO: opportunity.customerPO, + company: opportunity.company + ? { + id: opportunity.company.id, + identifier: opportunity.company.identifier, + name: opportunity.company.name, + } + : null, + contact: opportunity.contact + ? { id: opportunity.contact.id, name: opportunity.contact.name } + : null, + site: opportunity.site + ? { id: opportunity.site.id, name: opportunity.site.name } + : null, + primarySalesRep: opportunity.primarySalesRep + ? { + id: opportunity.primarySalesRep.id, + identifier: opportunity.primarySalesRep.identifier, + name: opportunity.primarySalesRep.name, + } + : null, + secondarySalesRep: opportunity.secondarySalesRep + ? { + id: opportunity.secondarySalesRep.id, + identifier: opportunity.secondarySalesRep.identifier, + name: opportunity.secondarySalesRep.name, + } + : null, + closedBy: opportunity.closedBy + ? { + id: opportunity.closedBy.id, + identifier: opportunity.closedBy.identifier, + name: opportunity.closedBy.name, + } + : null, + campaign: opportunity.campaign + ? { id: opportunity.campaign.id, name: opportunity.campaign.name } + : null, + totalSalesTax: opportunity.totalSalesTax, + location: opportunity.location + ? { id: opportunity.location.id, name: opportunity.location.name } + : null, + department: opportunity.department + ? { id: opportunity.department.id, name: opportunity.department.name } + : null, + pipelineChangeDate: opportunity.pipelineChangeDate, + dateBecameLead: opportunity.dateBecameLead, + info: opportunity._info, +}); + +/** + * Processes an array of raw CW opportunities. + */ +export const processOpportunitiesResponse = (opportunities: CWOpportunity[]) => + opportunities.map(processOpportunityResponse); diff --git a/src/modules/cw-utils/opportunities/refreshOpportunities.ts b/src/modules/cw-utils/opportunities/refreshOpportunities.ts new file mode 100644 index 0000000..9ad383a --- /dev/null +++ b/src/modules/cw-utils/opportunities/refreshOpportunities.ts @@ -0,0 +1,110 @@ +import { prisma } from "../../../constants"; +import { events } from "../../globalEvents"; +import { opportunityCw } from "./opportunities"; +import { OpportunityController } from "../../../controllers/OpportunityController"; + +/** + * Refresh Opportunities + * + * Syncs local opportunity records with ConnectWise using 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 () => { + events.emit("cw:opportunities:refresh:check"); + + // 1. Fetch lightweight summaries from CW + const cwSummaries = await opportunityCw.fetchAllSummaries(); + + // 2. Fetch all DB items with their cwOpportunityId and cwLastUpdated + const dbItems = await prisma.opportunity.findMany({ + select: { cwOpportunityId: true, cwLastUpdated: true }, + }); + const dbMap = new Map( + dbItems.map((item) => [item.cwOpportunityId, item.cwLastUpdated]), + ); + + // 3. Determine stale / new IDs + 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; + + if (!dbLastUpdated || (cwLastUpdated && cwLastUpdated > dbLastUpdated)) { + staleIds.push(cwId); + } + } + + if (staleIds.length === 0) { + events.emit("cw:opportunities:refresh:skipped", { + totalCw: cwSummaries.size, + totalDb: dbItems.length, + staleCount: 0, + }); + return; + } + + events.emit("cw:opportunities:refresh:started", { + totalCw: cwSummaries.size, + totalDb: dbItems.length, + staleCount: staleIds.length, + }); + + // 4. Full-fetch all opportunities, filter to stale set + const staleIdSet = new Set(staleIds); + const allCwItems = await opportunityCw.fetchAll(); + const staleItems = new Map(); + + for (const [id, item] of allCwItems) { + if (staleIdSet.has(id)) { + staleItems.set(id, item); + } + } + + // 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 + const updatedCount = ( + await Promise.all( + staleIds.map(async (cwId) => { + const item = staleItems.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, + }); +}; diff --git a/src/modules/cw-utils/procurement/refreshCatalog.ts b/src/modules/cw-utils/procurement/refreshCatalog.ts index 8ed7134..ea8ef61 100644 --- a/src/modules/cw-utils/procurement/refreshCatalog.ts +++ b/src/modules/cw-utils/procurement/refreshCatalog.ts @@ -91,6 +91,7 @@ export const refreshCatalog = async () => { where: { cwCatalogId: cwId }, create: { cwCatalogId: cwId, + identifier: item.identifier, name: item.description, description: item.description, customerDescription: item.customerDescription, @@ -110,6 +111,7 @@ export const refreshCatalog = async () => { }, update: { name: item.description, + identifier: item.identifier, description: item.description, customerDescription: item.customerDescription, internalNotes: item.notes, diff --git a/src/modules/globalEvents.ts b/src/modules/globalEvents.ts index a32cce8..0ba01c8 100644 --- a/src/modules/globalEvents.ts +++ b/src/modules/globalEvents.ts @@ -158,6 +158,25 @@ interface EventTypes { totalItems: number; updatedCount: number; }) => void; + + // ConnectWise Opportunities Events + "cw:opportunities:refresh:check": () => void; + "cw:opportunities:refresh:started": (data: { + totalCw: number; + totalDb: number; + staleCount: number; + }) => void; + "cw:opportunities:refresh:completed": (data: { + totalCw: number; + totalDb: number; + staleCount: number; + itemsUpdated: number; + }) => void; + "cw:opportunities:refresh:skipped": (data: { + totalCw: number; + totalDb: number; + staleCount: number; + }) => void; } export const events = new Eventra(); diff --git a/src/types/PermissionNodes.ts b/src/types/PermissionNodes.ts index 0a2609b..fbd3e9a 100644 --- a/src/types/PermissionNodes.ts +++ b/src/types/PermissionNodes.ts @@ -341,6 +341,72 @@ export const PERMISSION_NODES = { ], }, + procurement: { + name: "Procurement Permissions", + description: + "Permissions for accessing and managing procurement catalog items", + permissions: [ + { + node: "procurement.catalog.fetch", + description: "Fetch a single catalog item", + usedIn: ["src/api/procurement/[id]/fetch.ts"], + }, + { + node: "procurement.catalog.fetch.many", + description: "Fetch multiple catalog items or count", + usedIn: [ + "src/api/procurement/fetchAll.ts", + "src/api/procurement/count.ts", + ], + }, + { + node: "procurement.catalog.inventory.refresh", + description: + "Refresh on-hand inventory for a catalog item from ConnectWise", + usedIn: ["src/api/procurement/[id]/refreshInventory.ts"], + dependencies: ["procurement.catalog.fetch"], + }, + { + node: "procurement.catalog.link", + description: "Link or unlink catalog items to each other", + usedIn: [ + "src/api/procurement/[id]/link.ts", + "src/api/procurement/[id]/unlink.ts", + ], + dependencies: ["procurement.catalog.fetch"], + }, + ], + }, + + sales: { + name: "Sales Permissions", + description: "Permissions for accessing and managing sales opportunities", + permissions: [ + { + node: "sales.opportunity.fetch", + description: + "Fetch a single opportunity and its sub-resources (forecasts, notes, contacts)", + usedIn: [ + "src/api/sales/[id]/fetch.ts", + "src/api/sales/[id]/forecasts.ts", + "src/api/sales/[id]/notes.ts", + "src/api/sales/[id]/contacts.ts", + ], + }, + { + node: "sales.opportunity.fetch.many", + description: "Fetch multiple opportunities or count", + usedIn: ["src/api/sales/fetchAll.ts", "src/api/sales/count.ts"], + }, + { + node: "sales.opportunity.refresh", + description: "Refresh a single opportunity from ConnectWise", + usedIn: ["src/api/sales/[id]/refresh.ts"], + dependencies: ["sales.opportunity.fetch"], + }, + ], + }, + unifi: { name: "UniFi Permissions", description: