From 9c3a19279a362738ffb67178374c131982aa0018 Mon Sep 17 00:00:00 2001 From: Nicholai Date: Wed, 4 Feb 2026 16:18:39 -0700 Subject: [PATCH] feat(schema): add auth, people, and financial tables (#26) Add users, organizations, teams, groups, and project members tables. Extend customers/vendors with netsuite fields. Add netsuite schema for invoices, bills, payments, and credit memos. Include all migrations, seeds, new UI primitives, and config updates. Co-authored-by: Nicholai --- .env.example | 24 + .gitignore | 6 + bun.lock | 92 + drizzle.config.ts | 2 +- drizzle/0005_concerned_midnight.sql | 130 ++ drizzle/0006_brainy_vulcan.sql | 85 + drizzle/0007_add_customer_fields.sql | 5 + drizzle/meta/0005_snapshot.json | 1559 ++++++++++++++++ drizzle/meta/0006_snapshot.json | 2151 +++++++++++++++++++++++ drizzle/meta/_journal.json | 21 + drizzle/seed-users.sql | 57 + seed.sql => drizzle/seed.sql | 0 package.json | 2 + src/app/globals.css | 10 + src/components/ui/badge-indicator.tsx | 32 + src/components/ui/carousel-pages.tsx | 81 + src/components/ui/command.tsx | 11 +- src/components/ui/date-picker.tsx | 62 + src/components/ui/responsive-dialog.tsx | 193 ++ src/components/ui/sheet.tsx | 14 +- src/components/ui/sidebar.tsx | 10 +- src/db/index.ts | 5 +- src/db/schema-netsuite.ts | 153 ++ src/db/schema.ts | 120 ++ src/lib/utils.ts | 7 + wrangler.jsonc | 5 +- 26 files changed, 4818 insertions(+), 19 deletions(-) create mode 100755 .env.example create mode 100755 drizzle/0005_concerned_midnight.sql create mode 100755 drizzle/0006_brainy_vulcan.sql create mode 100755 drizzle/0007_add_customer_fields.sql create mode 100755 drizzle/meta/0005_snapshot.json create mode 100755 drizzle/meta/0006_snapshot.json create mode 100755 drizzle/seed-users.sql rename seed.sql => drizzle/seed.sql (100%) create mode 100755 src/components/ui/badge-indicator.tsx create mode 100755 src/components/ui/carousel-pages.tsx create mode 100755 src/components/ui/date-picker.tsx create mode 100755 src/components/ui/responsive-dialog.tsx create mode 100755 src/db/schema-netsuite.ts diff --git a/.env.example b/.env.example new file mode 100755 index 0000000..86dd7fd --- /dev/null +++ b/.env.example @@ -0,0 +1,24 @@ +# WorkOS Authentication +# Get these from your WorkOS dashboard: https://dashboard.workos.com +WORKOS_API_KEY=your_workos_api_key_here +WORKOS_CLIENT_ID=your_workos_client_id_here +WORKOS_COOKIE_PASSWORD=your_random_32_character_string_here +WORKOS_REDIRECT_URI=http://localhost:3000 + +# NetSuite Integration +# OAuth 2.0 credentials from your NetSuite account +NETSUITE_ACCOUNT_ID=your_account_id_here +NETSUITE_CLIENT_ID=your_client_id_here +NETSUITE_CLIENT_SECRET=your_client_secret_here +NETSUITE_REDIRECT_URI=http://localhost:3000/api/netsuite/callback + +# Token encryption key (generate with: openssl rand -hex 32) +NETSUITE_TOKEN_ENCRYPTION_KEY=your_encryption_key_here + +# Optional: Max concurrent requests to NetSuite API (default: 15) +NETSUITE_CONCURRENCY_LIMIT=15 + +# Optional: For Automatic Github Deployments +GITHUB_TOKEN=your_github_repo_token_here +GITHUB_REPO=High-Performance-Structures/compass + diff --git a/.gitignore b/.gitignore index fa50182..66b8ce8 100755 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules/ # next.js .next/ out/ +.open-next/ # cloudflare .wrangler/ @@ -20,3 +21,8 @@ dist/ # misc .DS_Store *.tsbuildinfo + +# dev tools +.playwright-mcp +mobile-ui-references/ +.fuse_* diff --git a/bun.lock b/bun.lock index 743ca94..bb8ae1a 100755 --- a/bun.lock +++ b/bun.lock @@ -39,6 +39,8 @@ "@radix-ui/react-tooltip": "^1.2.8", "@tabler/icons-react": "^3.36.1", "@tanstack/react-table": "^8.21.3", + "@workos-inc/authkit-nextjs": "^2.13.0", + "@workos-inc/node": "^8.1.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -457,6 +459,12 @@ "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.15.0", "", { "dependencies": { "@ast-grep/napi": "0.40.0", "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.9.11", "cloudflare": "^4.4.1", "enquirer": "^2.4.1", "glob": "^12.0.0", "ts-tqdm": "^0.8.6", "yargs": "^18.0.0" }, "peerDependencies": { "next": "^14.2.35 || ~15.0.7 || ~15.1.11 || ~15.2.8 || ~15.3.8 || ~15.4.10 || ~15.5.9 || ^16.0.10", "wrangler": "^4.59.2" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-AZPaqk25XUBxtdkfjUZQBbY3ovifVLC4GgSRHuejqsIWfv8KjTRNFVdaCaaPmbLkrgymqxNhkbfJS5sD28AK/g=="], + "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.6.0", "", { "dependencies": { "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg=="], + + "@peculiar/json-schema": ["@peculiar/json-schema@1.1.12", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w=="], + + "@peculiar/webcrypto": ["@peculiar/webcrypto@1.5.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.8", "@peculiar/json-schema": "^1.1.12", "pvtsutils": "^1.3.5", "tslib": "^2.6.2", "webcrypto-core": "^1.8.0" } }, "sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg=="], + "@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="], "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="], @@ -727,6 +735,18 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@types/accepts": ["@types/accepts@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ=="], + + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], + + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + + "@types/content-disposition": ["@types/content-disposition@0.5.9", "", {}, "sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ=="], + + "@types/cookie": ["@types/cookie@0.5.4", "", {}, "sha512-7z/eR6O859gyWIAjuvBWFzNURmf2oPBmJlfVWkwehU5nzIyjwBsTh7WMmEEV4JFnHuQ3ex4oyTvfKzcyJVDBNA=="], + + "@types/cookies": ["@types/cookies@0.9.2", "", { "dependencies": { "@types/connect": "*", "@types/express": "*", "@types/keygrip": "*", "@types/node": "*" } }, "sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A=="], + "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], @@ -747,18 +767,42 @@ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/express": ["@types/express@4.17.25", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "^1" } }, "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw=="], + + "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.8", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA=="], + + "@types/http-assert": ["@types/http-assert@1.5.6", "", {}, "sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw=="], + + "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + "@types/keygrip": ["@types/keygrip@1.0.6", "", {}, "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ=="], + + "@types/koa": ["@types/koa@2.15.0", "", { "dependencies": { "@types/accepts": "*", "@types/content-disposition": "*", "@types/cookies": "*", "@types/http-assert": "*", "@types/http-errors": "*", "@types/keygrip": "*", "@types/koa-compose": "*", "@types/node": "*" } }, "sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g=="], + + "@types/koa-compose": ["@types/koa-compose@3.2.9", "", { "dependencies": { "@types/koa": "*" } }, "sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA=="], + + "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], + "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="], + "@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], + + "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + "@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + "@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], + + "@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.53.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/type-utils": "8.53.1", "@typescript-eslint/utils": "8.53.1", "@typescript-eslint/visitor-keys": "8.53.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.53.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.53.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", "@typescript-eslint/typescript-estree": "8.53.1", "@typescript-eslint/visitor-keys": "8.53.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg=="], @@ -817,6 +861,10 @@ "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="], + "@workos-inc/authkit-nextjs": ["@workos-inc/authkit-nextjs@2.13.0", "", { "dependencies": { "@workos-inc/node": "^7.72.0", "iron-session": "^8.0.1", "jose": "^5.2.3", "path-to-regexp": "^6.2.2" }, "peerDependencies": { "next": "^13.5.9 || ^14.2.26 || ^15.2.3 || ^16", "react": "^18.0 || ^19.0.0", "react-dom": "^18.0 || ^19.0.0" } }, "sha512-ppxzhfakPumHPPggYSROaAlgxfS7viFMPmWPG76Tp6Rh9G7YqkBSp7xtvMtM6gXOFFMvvEJRcKEta6YHeercTQ=="], + + "@workos-inc/node": ["@workos-inc/node@8.1.0", "", { "dependencies": { "iron-webcrypto": "^2.0.0", "jose": "~6.1.0" } }, "sha512-Ep2QSP43y4ZdJIOuL4Hjaq5f0u8Z0qZe7QWzrrBV6cHc/kcicDBcB0AanMP6eB9x3x6FaHfevLbkbjPF4+TCYQ=="], + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], @@ -857,6 +905,8 @@ "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + "asn1js": ["asn1js@3.0.7", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ=="], + "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], @@ -873,6 +923,8 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], @@ -883,6 +935,8 @@ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], @@ -1199,6 +1253,8 @@ "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], @@ -1215,6 +1271,10 @@ "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "iron-session": ["iron-session@8.0.4", "", { "dependencies": { "cookie": "^0.7.2", "iron-webcrypto": "^1.2.1", "uncrypto": "^0.1.3" } }, "sha512-9ivNnaKOd08osD0lJ3i6If23GFS2LsxyMU8Gf/uBUEgm8/8CC1hrrCHFDpMo3IFbpBgwoo/eairRsaD3c5itxA=="], + + "iron-webcrypto": ["iron-webcrypto@2.0.0", "", { "dependencies": { "uint8array-extras": "^1.5.0" } }, "sha512-rtffZKDUHciZElM8mjFCufBC7nVhCxHYyWHESqs89OioEDz4parOofd8/uhrejh/INhQFfYQfByS22LlezR9sQ=="], + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], @@ -1283,6 +1343,8 @@ "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], @@ -1305,6 +1367,8 @@ "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], + "leb": ["leb@1.0.0", "", {}, "sha512-Y3c3QZfvKWHX60BVOQPhLCvVGmDYWyJEiINE3drOog6KCyN2AOwvuQQzlS3uJg1J85kzpILXIUwRXULWavir+w=="], + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], @@ -1463,6 +1527,10 @@ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], + + "pvutils": ["pvutils@1.1.5", "", {}, "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA=="], + "qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], @@ -1643,8 +1711,12 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], + "undici": ["undici@7.18.2", "", {}, "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], @@ -1675,6 +1747,8 @@ "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="], + "webcrypto-core": ["webcrypto-core@1.8.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.13", "@peculiar/json-schema": "^1.1.12", "asn1js": "^3.0.5", "pvtsutils": "^1.3.5", "tslib": "^2.7.0" } }, "sha512-P+x1MvlNCXlKbLSOY4cYrdreqPG5hbzkmawbcXLKN/mf6DZW0SdNNkZ+sjwsqVkI4A4Ko2sPZmkZtCKY58w83A=="], + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], @@ -2363,6 +2437,10 @@ "@typescript-eslint/typescript-estree/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "@workos-inc/authkit-nextjs/@workos-inc/node": ["@workos-inc/node@7.82.0", "", { "dependencies": { "iron-session": "~6.3.1", "jose": "~5.6.3", "leb": "^1.0.0", "qs": "6.14.1" } }, "sha512-8h6XjIJf8nqNGYQMkWsjZ72WXMtzqrb4Azz39schXWoSRmwoK6tK+GpeAviJ9slddiJdOcp0Ht9/r+L6pGQMCg=="], + + "@workos-inc/node/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="], + "cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "cloudflare/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], @@ -2387,6 +2465,10 @@ "glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], + "iron-session/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "iron-session/iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], + "is-bun-module/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -2913,6 +2995,10 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "@workos-inc/authkit-nextjs/@workos-inc/node/iron-session": ["iron-session@6.3.1", "", { "dependencies": { "@peculiar/webcrypto": "^1.4.0", "@types/cookie": "^0.5.1", "@types/express": "^4.17.13", "@types/koa": "^2.13.5", "@types/node": "^17.0.41", "cookie": "^0.5.0", "iron-webcrypto": "^0.2.5" }, "peerDependencies": { "express": ">=4", "koa": ">=2", "next": ">=10" }, "optionalPeers": ["express", "koa", "next"] }, "sha512-3UJ7y2vk/WomAtEySmPgM6qtYF1cZ3tXuWX5GsVX4PJXAcs5y/sV9HuSfpjKS6HkTL/OhZcTDWJNLZ7w+Erx3A=="], + + "@workos-inc/authkit-nextjs/@workos-inc/node/jose": ["jose@5.6.3", "", {}, "sha512-1Jh//hEEwMhNYPDDLwXHa2ePWgWiFNNUadVmguAAw2IJ6sj9mNxV5tGXJNqlMkJAybF6Lgw1mISDxTePP/187g=="], + "cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "cloudflare/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], @@ -3131,6 +3217,12 @@ "@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.8", "", { "dependencies": { "@smithy/types": "^4.12.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw=="], + "@workos-inc/authkit-nextjs/@workos-inc/node/iron-session/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="], + + "@workos-inc/authkit-nextjs/@workos-inc/node/iron-session/cookie": ["cookie@0.5.0", "", {}, "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="], + + "@workos-inc/authkit-nextjs/@workos-inc/node/iron-session/iron-webcrypto": ["iron-webcrypto@0.2.8", "", { "dependencies": { "buffer": "^6" } }, "sha512-YPdCvjFMOBjXaYuDj5tiHst5CEk6Xw84Jo8Y2+jzhMceclAnb3+vNPP/CTtb5fO2ZEuXEaO4N+w62Vfko757KA=="], + "@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.8", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA=="], "@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.8", "", { "dependencies": { "@smithy/types": "^4.12.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw=="], diff --git a/drizzle.config.ts b/drizzle.config.ts index 77d2830..50ec6b0 100755 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from "drizzle-kit" export default defineConfig({ - schema: "./src/db/schema.ts", + schema: ["./src/db/schema.ts", "./src/db/schema-netsuite.ts"], out: "./drizzle", dialect: "sqlite", }) diff --git a/drizzle/0005_concerned_midnight.sql b/drizzle/0005_concerned_midnight.sql new file mode 100755 index 0000000..9def625 --- /dev/null +++ b/drizzle/0005_concerned_midnight.sql @@ -0,0 +1,130 @@ +CREATE TABLE `credit_memos` ( + `id` text PRIMARY KEY NOT NULL, + `netsuite_id` text, + `customer_id` text NOT NULL, + `project_id` text, + `memo_number` text, + `status` text DEFAULT 'draft' NOT NULL, + `issue_date` text NOT NULL, + `total` real DEFAULT 0 NOT NULL, + `amount_applied` real DEFAULT 0 NOT NULL, + `amount_remaining` real DEFAULT 0 NOT NULL, + `memo` text, + `line_items` text, + `created_at` text NOT NULL, + `updated_at` text NOT NULL, + FOREIGN KEY (`customer_id`) REFERENCES `customers`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE `invoices` ( + `id` text PRIMARY KEY NOT NULL, + `netsuite_id` text, + `customer_id` text NOT NULL, + `project_id` text, + `invoice_number` text, + `status` text DEFAULT 'draft' NOT NULL, + `issue_date` text NOT NULL, + `due_date` text, + `subtotal` real DEFAULT 0 NOT NULL, + `tax` real DEFAULT 0 NOT NULL, + `total` real DEFAULT 0 NOT NULL, + `amount_paid` real DEFAULT 0 NOT NULL, + `amount_due` real DEFAULT 0 NOT NULL, + `memo` text, + `line_items` text, + `created_at` text NOT NULL, + `updated_at` text NOT NULL, + FOREIGN KEY (`customer_id`) REFERENCES `customers`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE `netsuite_auth` ( + `id` text PRIMARY KEY NOT NULL, + `account_id` text NOT NULL, + `access_token_encrypted` text NOT NULL, + `refresh_token_encrypted` text NOT NULL, + `expires_in` integer NOT NULL, + `token_type` text NOT NULL, + `issued_at` integer NOT NULL, + `created_at` text NOT NULL, + `updated_at` text NOT NULL +); +--> statement-breakpoint +CREATE TABLE `netsuite_sync_log` ( + `id` text PRIMARY KEY NOT NULL, + `sync_type` text NOT NULL, + `record_type` text NOT NULL, + `direction` text NOT NULL, + `status` text NOT NULL, + `records_processed` integer DEFAULT 0 NOT NULL, + `records_failed` integer DEFAULT 0 NOT NULL, + `error_summary` text, + `started_at` text NOT NULL, + `completed_at` text, + `created_at` text NOT NULL +); +--> statement-breakpoint +CREATE TABLE `netsuite_sync_metadata` ( + `id` text PRIMARY KEY NOT NULL, + `local_table` text NOT NULL, + `local_record_id` text NOT NULL, + `netsuite_record_type` text NOT NULL, + `netsuite_internal_id` text, + `last_synced_at` text, + `last_modified_local` text, + `last_modified_remote` text, + `sync_status` text DEFAULT 'synced' NOT NULL, + `conflict_data` text, + `error_message` text, + `retry_count` integer DEFAULT 0 NOT NULL, + `created_at` text NOT NULL, + `updated_at` text NOT NULL +); +--> statement-breakpoint +CREATE TABLE `payments` ( + `id` text PRIMARY KEY NOT NULL, + `netsuite_id` text, + `customer_id` text, + `vendor_id` text, + `project_id` text, + `payment_type` text NOT NULL, + `amount` real NOT NULL, + `payment_date` text NOT NULL, + `payment_method` text, + `reference_number` text, + `memo` text, + `created_at` text NOT NULL, + `updated_at` text NOT NULL, + FOREIGN KEY (`customer_id`) REFERENCES `customers`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`vendor_id`) REFERENCES `vendors`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE `vendor_bills` ( + `id` text PRIMARY KEY NOT NULL, + `netsuite_id` text, + `vendor_id` text NOT NULL, + `project_id` text, + `bill_number` text, + `status` text DEFAULT 'pending' NOT NULL, + `bill_date` text NOT NULL, + `due_date` text, + `subtotal` real DEFAULT 0 NOT NULL, + `tax` real DEFAULT 0 NOT NULL, + `total` real DEFAULT 0 NOT NULL, + `amount_paid` real DEFAULT 0 NOT NULL, + `amount_due` real DEFAULT 0 NOT NULL, + `memo` text, + `line_items` text, + `created_at` text NOT NULL, + `updated_at` text NOT NULL, + FOREIGN KEY (`vendor_id`) REFERENCES `vendors`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +ALTER TABLE `customers` ADD `netsuite_id` text;--> statement-breakpoint +ALTER TABLE `customers` ADD `updated_at` text;--> statement-breakpoint +ALTER TABLE `projects` ADD `netsuite_job_id` text;--> statement-breakpoint +ALTER TABLE `vendors` ADD `netsuite_id` text;--> statement-breakpoint +ALTER TABLE `vendors` ADD `updated_at` text; \ No newline at end of file diff --git a/drizzle/0006_brainy_vulcan.sql b/drizzle/0006_brainy_vulcan.sql new file mode 100755 index 0000000..158dd05 --- /dev/null +++ b/drizzle/0006_brainy_vulcan.sql @@ -0,0 +1,85 @@ +CREATE TABLE `group_members` ( + `id` text PRIMARY KEY NOT NULL, + `group_id` text NOT NULL, + `user_id` text NOT NULL, + `joined_at` text NOT NULL, + FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `groups` ( + `id` text PRIMARY KEY NOT NULL, + `organization_id` text NOT NULL, + `name` text NOT NULL, + `description` text, + `color` text, + `created_at` text NOT NULL, + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `organization_members` ( + `id` text PRIMARY KEY NOT NULL, + `organization_id` text NOT NULL, + `user_id` text NOT NULL, + `role` text NOT NULL, + `joined_at` text NOT NULL, + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `organizations` ( + `id` text PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `slug` text NOT NULL, + `type` text NOT NULL, + `logo_url` text, + `is_active` integer DEFAULT true NOT NULL, + `created_at` text NOT NULL, + `updated_at` text NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `organizations_slug_unique` ON `organizations` (`slug`);--> statement-breakpoint +CREATE TABLE `project_members` ( + `id` text PRIMARY KEY NOT NULL, + `project_id` text NOT NULL, + `user_id` text NOT NULL, + `role` text NOT NULL, + `assigned_at` text NOT NULL, + FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `team_members` ( + `id` text PRIMARY KEY NOT NULL, + `team_id` text NOT NULL, + `user_id` text NOT NULL, + `joined_at` text NOT NULL, + FOREIGN KEY (`team_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `teams` ( + `id` text PRIMARY KEY NOT NULL, + `organization_id` text NOT NULL, + `name` text NOT NULL, + `description` text, + `created_at` text NOT NULL, + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `users` ( + `id` text PRIMARY KEY NOT NULL, + `email` text NOT NULL, + `first_name` text, + `last_name` text, + `display_name` text, + `avatar_url` text, + `role` text DEFAULT 'office' NOT NULL, + `is_active` integer DEFAULT true NOT NULL, + `last_login_at` text, + `created_at` text NOT NULL, + `updated_at` text NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint +ALTER TABLE `projects` ADD `organization_id` text REFERENCES organizations(id); \ No newline at end of file diff --git a/drizzle/0007_add_customer_fields.sql b/drizzle/0007_add_customer_fields.sql new file mode 100755 index 0000000..cac6671 --- /dev/null +++ b/drizzle/0007_add_customer_fields.sql @@ -0,0 +1,5 @@ +ALTER TABLE customers ADD COLUMN company text; +--> statement-breakpoint +ALTER TABLE customers ADD COLUMN address text; +--> statement-breakpoint +ALTER TABLE customers ADD COLUMN notes text; diff --git a/drizzle/meta/0005_snapshot.json b/drizzle/meta/0005_snapshot.json new file mode 100755 index 0000000..a424ebd --- /dev/null +++ b/drizzle/meta/0005_snapshot.json @@ -0,0 +1,1559 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "2c6803c6-ee81-4536-ab3f-8d2e4f27ec2e", + "prevId": "b6fe056d-1639-42cd-9360-80d10f7ebb3a", + "tables": { + "customers": { + "name": "customers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "feedback": { + "name": "feedback", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_url": { + "name": "page_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "viewport_width": { + "name": "viewport_width", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "viewport_height": { + "name": "viewport_height", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ip_hash": { + "name": "ip_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_issue_url": { + "name": "github_issue_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "projects": { + "name": "projects", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'OPEN'" + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "project_manager": { + "name": "project_manager", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "netsuite_job_id": { + "name": "netsuite_job_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "schedule_baselines": { + "name": "schedule_baselines", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "snapshot_data": { + "name": "snapshot_data", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_baselines_project_id_projects_id_fk": { + "name": "schedule_baselines_project_id_projects_id_fk", + "tableFrom": "schedule_baselines", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "schedule_tasks": { + "name": "schedule_tasks", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_date": { + "name": "start_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workdays": { + "name": "workdays", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_date_calculated": { + "name": "end_date_calculated", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'PENDING'" + }, + "is_critical_path": { + "name": "is_critical_path", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_milestone": { + "name": "is_milestone", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "percent_complete": { + "name": "percent_complete", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "assigned_to": { + "name": "assigned_to", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_tasks_project_id_projects_id_fk": { + "name": "schedule_tasks_project_id_projects_id_fk", + "tableFrom": "schedule_tasks", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "task_dependencies": { + "name": "task_dependencies", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "predecessor_id": { + "name": "predecessor_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "successor_id": { + "name": "successor_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'FS'" + }, + "lag_days": { + "name": "lag_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "task_dependencies_predecessor_id_schedule_tasks_id_fk": { + "name": "task_dependencies_predecessor_id_schedule_tasks_id_fk", + "tableFrom": "task_dependencies", + "tableTo": "schedule_tasks", + "columnsFrom": [ + "predecessor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "task_dependencies_successor_id_schedule_tasks_id_fk": { + "name": "task_dependencies_successor_id_schedule_tasks_id_fk", + "tableFrom": "task_dependencies", + "tableTo": "schedule_tasks", + "columnsFrom": [ + "successor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "vendors": { + "name": "vendors", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Subcontractor'" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "workday_exceptions": { + "name": "workday_exceptions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_date": { + "name": "start_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_date": { + "name": "end_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'non_working'" + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'company_holiday'" + }, + "recurrence": { + "name": "recurrence", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'one_time'" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "workday_exceptions_project_id_projects_id_fk": { + "name": "workday_exceptions_project_id_projects_id_fk", + "tableFrom": "workday_exceptions", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "credit_memos": { + "name": "credit_memos", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "memo_number": { + "name": "memo_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'draft'" + }, + "issue_date": { + "name": "issue_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "total": { + "name": "total", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_applied": { + "name": "amount_applied", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_remaining": { + "name": "amount_remaining", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "memo": { + "name": "memo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "line_items": { + "name": "line_items", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "credit_memos_customer_id_customers_id_fk": { + "name": "credit_memos_customer_id_customers_id_fk", + "tableFrom": "credit_memos", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "credit_memos_project_id_projects_id_fk": { + "name": "credit_memos_project_id_projects_id_fk", + "tableFrom": "credit_memos", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invoices": { + "name": "invoices", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoice_number": { + "name": "invoice_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'draft'" + }, + "issue_date": { + "name": "issue_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "due_date": { + "name": "due_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subtotal": { + "name": "subtotal", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "tax": { + "name": "tax", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "total": { + "name": "total", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_paid": { + "name": "amount_paid", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_due": { + "name": "amount_due", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "memo": { + "name": "memo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "line_items": { + "name": "line_items", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "invoices_customer_id_customers_id_fk": { + "name": "invoices_customer_id_customers_id_fk", + "tableFrom": "invoices", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invoices_project_id_projects_id_fk": { + "name": "invoices_project_id_projects_id_fk", + "tableFrom": "invoices", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "netsuite_auth": { + "name": "netsuite_auth", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token_encrypted": { + "name": "access_token_encrypted", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_encrypted": { + "name": "refresh_token_encrypted", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_in": { + "name": "expires_in", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "issued_at": { + "name": "issued_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "netsuite_sync_log": { + "name": "netsuite_sync_log", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "sync_type": { + "name": "sync_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "record_type": { + "name": "record_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "records_processed": { + "name": "records_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "records_failed": { + "name": "records_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "error_summary": { + "name": "error_summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "netsuite_sync_metadata": { + "name": "netsuite_sync_metadata", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "local_table": { + "name": "local_table", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "local_record_id": { + "name": "local_record_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "netsuite_record_type": { + "name": "netsuite_record_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "netsuite_internal_id": { + "name": "netsuite_internal_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_modified_local": { + "name": "last_modified_local", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_modified_remote": { + "name": "last_modified_remote", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sync_status": { + "name": "sync_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'synced'" + }, + "conflict_data": { + "name": "conflict_data", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "retry_count": { + "name": "retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "payments": { + "name": "payments", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "vendor_id": { + "name": "vendor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payment_type": { + "name": "payment_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payment_date": { + "name": "payment_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payment_method": { + "name": "payment_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reference_number": { + "name": "reference_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "memo": { + "name": "memo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "payments_customer_id_customers_id_fk": { + "name": "payments_customer_id_customers_id_fk", + "tableFrom": "payments", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "payments_vendor_id_vendors_id_fk": { + "name": "payments_vendor_id_vendors_id_fk", + "tableFrom": "payments", + "tableTo": "vendors", + "columnsFrom": [ + "vendor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "payments_project_id_projects_id_fk": { + "name": "payments_project_id_projects_id_fk", + "tableFrom": "payments", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "vendor_bills": { + "name": "vendor_bills", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "vendor_id": { + "name": "vendor_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bill_number": { + "name": "bill_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "bill_date": { + "name": "bill_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "due_date": { + "name": "due_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subtotal": { + "name": "subtotal", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "tax": { + "name": "tax", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "total": { + "name": "total", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_paid": { + "name": "amount_paid", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_due": { + "name": "amount_due", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "memo": { + "name": "memo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "line_items": { + "name": "line_items", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "vendor_bills_vendor_id_vendors_id_fk": { + "name": "vendor_bills_vendor_id_vendors_id_fk", + "tableFrom": "vendor_bills", + "tableTo": "vendors", + "columnsFrom": [ + "vendor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "vendor_bills_project_id_projects_id_fk": { + "name": "vendor_bills_project_id_projects_id_fk", + "tableFrom": "vendor_bills", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0006_snapshot.json b/drizzle/meta/0006_snapshot.json new file mode 100755 index 0000000..5bc00a3 --- /dev/null +++ b/drizzle/meta/0006_snapshot.json @@ -0,0 +1,2151 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "79017edb-88ed-4147-b649-3e12d16a60f5", + "prevId": "2c6803c6-ee81-4536-ab3f-8d2e4f27ec2e", + "tables": { + "customers": { + "name": "customers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "feedback": { + "name": "feedback", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_url": { + "name": "page_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "viewport_width": { + "name": "viewport_width", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "viewport_height": { + "name": "viewport_height", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ip_hash": { + "name": "ip_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_issue_url": { + "name": "github_issue_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "group_members": { + "name": "group_members", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "joined_at": { + "name": "joined_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "group_members_group_id_groups_id_fk": { + "name": "group_members_group_id_groups_id_fk", + "tableFrom": "group_members", + "tableTo": "groups", + "columnsFrom": [ + "group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "group_members_user_id_users_id_fk": { + "name": "group_members_user_id_users_id_fk", + "tableFrom": "group_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "groups": { + "name": "groups", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "groups_organization_id_organizations_id_fk": { + "name": "groups_organization_id_organizations_id_fk", + "tableFrom": "groups", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organization_members": { + "name": "organization_members", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "joined_at": { + "name": "joined_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "organization_members_organization_id_organizations_id_fk": { + "name": "organization_members_organization_id_organizations_id_fk", + "tableFrom": "organization_members", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_members_user_id_users_id_fk": { + "name": "organization_members_user_id_users_id_fk", + "tableFrom": "organization_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "logo_url": { + "name": "logo_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "organizations_slug_unique": { + "name": "organizations_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "project_members": { + "name": "project_members", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "assigned_at": { + "name": "assigned_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "project_members_project_id_projects_id_fk": { + "name": "project_members_project_id_projects_id_fk", + "tableFrom": "project_members", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "project_members_user_id_users_id_fk": { + "name": "project_members_user_id_users_id_fk", + "tableFrom": "project_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "projects": { + "name": "projects", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'OPEN'" + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "project_manager": { + "name": "project_manager", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "netsuite_job_id": { + "name": "netsuite_job_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "projects_organization_id_organizations_id_fk": { + "name": "projects_organization_id_organizations_id_fk", + "tableFrom": "projects", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "schedule_baselines": { + "name": "schedule_baselines", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "snapshot_data": { + "name": "snapshot_data", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_baselines_project_id_projects_id_fk": { + "name": "schedule_baselines_project_id_projects_id_fk", + "tableFrom": "schedule_baselines", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "schedule_tasks": { + "name": "schedule_tasks", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_date": { + "name": "start_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workdays": { + "name": "workdays", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_date_calculated": { + "name": "end_date_calculated", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'PENDING'" + }, + "is_critical_path": { + "name": "is_critical_path", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_milestone": { + "name": "is_milestone", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "percent_complete": { + "name": "percent_complete", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "assigned_to": { + "name": "assigned_to", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_tasks_project_id_projects_id_fk": { + "name": "schedule_tasks_project_id_projects_id_fk", + "tableFrom": "schedule_tasks", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "task_dependencies": { + "name": "task_dependencies", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "predecessor_id": { + "name": "predecessor_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "successor_id": { + "name": "successor_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'FS'" + }, + "lag_days": { + "name": "lag_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "task_dependencies_predecessor_id_schedule_tasks_id_fk": { + "name": "task_dependencies_predecessor_id_schedule_tasks_id_fk", + "tableFrom": "task_dependencies", + "tableTo": "schedule_tasks", + "columnsFrom": [ + "predecessor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "task_dependencies_successor_id_schedule_tasks_id_fk": { + "name": "task_dependencies_successor_id_schedule_tasks_id_fk", + "tableFrom": "task_dependencies", + "tableTo": "schedule_tasks", + "columnsFrom": [ + "successor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team_members": { + "name": "team_members", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "joined_at": { + "name": "joined_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "team_members_team_id_teams_id_fk": { + "name": "team_members_team_id_teams_id_fk", + "tableFrom": "team_members", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_members_user_id_users_id_fk": { + "name": "team_members_user_id_users_id_fk", + "tableFrom": "team_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "teams": { + "name": "teams", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "teams_organization_id_organizations_id_fk": { + "name": "teams_organization_id_organizations_id_fk", + "tableFrom": "teams", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'office'" + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "last_login_at": { + "name": "last_login_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "vendors": { + "name": "vendors", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Subcontractor'" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "workday_exceptions": { + "name": "workday_exceptions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_date": { + "name": "start_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_date": { + "name": "end_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'non_working'" + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'company_holiday'" + }, + "recurrence": { + "name": "recurrence", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'one_time'" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "workday_exceptions_project_id_projects_id_fk": { + "name": "workday_exceptions_project_id_projects_id_fk", + "tableFrom": "workday_exceptions", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "credit_memos": { + "name": "credit_memos", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "memo_number": { + "name": "memo_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'draft'" + }, + "issue_date": { + "name": "issue_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "total": { + "name": "total", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_applied": { + "name": "amount_applied", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_remaining": { + "name": "amount_remaining", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "memo": { + "name": "memo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "line_items": { + "name": "line_items", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "credit_memos_customer_id_customers_id_fk": { + "name": "credit_memos_customer_id_customers_id_fk", + "tableFrom": "credit_memos", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "credit_memos_project_id_projects_id_fk": { + "name": "credit_memos_project_id_projects_id_fk", + "tableFrom": "credit_memos", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invoices": { + "name": "invoices", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoice_number": { + "name": "invoice_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'draft'" + }, + "issue_date": { + "name": "issue_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "due_date": { + "name": "due_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subtotal": { + "name": "subtotal", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "tax": { + "name": "tax", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "total": { + "name": "total", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_paid": { + "name": "amount_paid", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_due": { + "name": "amount_due", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "memo": { + "name": "memo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "line_items": { + "name": "line_items", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "invoices_customer_id_customers_id_fk": { + "name": "invoices_customer_id_customers_id_fk", + "tableFrom": "invoices", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invoices_project_id_projects_id_fk": { + "name": "invoices_project_id_projects_id_fk", + "tableFrom": "invoices", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "netsuite_auth": { + "name": "netsuite_auth", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token_encrypted": { + "name": "access_token_encrypted", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_encrypted": { + "name": "refresh_token_encrypted", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_in": { + "name": "expires_in", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "issued_at": { + "name": "issued_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "netsuite_sync_log": { + "name": "netsuite_sync_log", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "sync_type": { + "name": "sync_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "record_type": { + "name": "record_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "records_processed": { + "name": "records_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "records_failed": { + "name": "records_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "error_summary": { + "name": "error_summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "netsuite_sync_metadata": { + "name": "netsuite_sync_metadata", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "local_table": { + "name": "local_table", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "local_record_id": { + "name": "local_record_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "netsuite_record_type": { + "name": "netsuite_record_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "netsuite_internal_id": { + "name": "netsuite_internal_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_modified_local": { + "name": "last_modified_local", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_modified_remote": { + "name": "last_modified_remote", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sync_status": { + "name": "sync_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'synced'" + }, + "conflict_data": { + "name": "conflict_data", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "retry_count": { + "name": "retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "payments": { + "name": "payments", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "vendor_id": { + "name": "vendor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payment_type": { + "name": "payment_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payment_date": { + "name": "payment_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payment_method": { + "name": "payment_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reference_number": { + "name": "reference_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "memo": { + "name": "memo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "payments_customer_id_customers_id_fk": { + "name": "payments_customer_id_customers_id_fk", + "tableFrom": "payments", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "payments_vendor_id_vendors_id_fk": { + "name": "payments_vendor_id_vendors_id_fk", + "tableFrom": "payments", + "tableTo": "vendors", + "columnsFrom": [ + "vendor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "payments_project_id_projects_id_fk": { + "name": "payments_project_id_projects_id_fk", + "tableFrom": "payments", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "vendor_bills": { + "name": "vendor_bills", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "netsuite_id": { + "name": "netsuite_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "vendor_id": { + "name": "vendor_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bill_number": { + "name": "bill_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "bill_date": { + "name": "bill_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "due_date": { + "name": "due_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subtotal": { + "name": "subtotal", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "tax": { + "name": "tax", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "total": { + "name": "total", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_paid": { + "name": "amount_paid", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount_due": { + "name": "amount_due", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "memo": { + "name": "memo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "line_items": { + "name": "line_items", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "vendor_bills_vendor_id_vendors_id_fk": { + "name": "vendor_bills_vendor_id_vendors_id_fk", + "tableFrom": "vendor_bills", + "tableTo": "vendors", + "columnsFrom": [ + "vendor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "vendor_bills_project_id_projects_id_fk": { + "name": "vendor_bills_project_id_projects_id_fk", + "tableFrom": "vendor_bills", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index b74648f..27bb259 100755 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -36,6 +36,27 @@ "when": 1769287376759, "tag": "0004_quick_firebrand", "breakpoints": true + }, + { + "idx": 5, + "version": "6", + "when": 1770235828892, + "tag": "0005_concerned_midnight", + "breakpoints": true + }, + { + "idx": 6, + "version": "6", + "when": 1770235964505, + "tag": "0006_brainy_vulcan", + "breakpoints": true + }, + { + "idx": 7, + "version": "6", + "when": 1770321600000, + "tag": "0007_add_customer_fields", + "breakpoints": true } ] } \ No newline at end of file diff --git a/drizzle/seed-users.sql b/drizzle/seed-users.sql new file mode 100755 index 0000000..812bf1b --- /dev/null +++ b/drizzle/seed-users.sql @@ -0,0 +1,57 @@ +-- seed users for development +INSERT INTO users (id, email, first_name, last_name, display_name, avatar_url, role, is_active, last_login_at, created_at, updated_at) +VALUES + ('user-1', 'admin@compass.io', 'Admin', 'User', 'Admin User', NULL, 'admin', 1, '2026-02-04T12:00:00Z', '2026-01-01T00:00:00Z', '2026-02-04T12:00:00Z'), + ('user-2', 'john@compass.io', 'John', 'Smith', 'John Smith', NULL, 'office', 1, '2026-02-03T10:30:00Z', '2026-01-15T00:00:00Z', '2026-02-03T10:30:00Z'), + ('user-3', 'sarah@compass.io', 'Sarah', 'Johnson', 'Sarah Johnson', NULL, 'office', 1, '2026-02-04T08:15:00Z', '2026-01-20T00:00:00Z', '2026-02-04T08:15:00Z'), + ('user-4', 'mike@compass.io', 'Mike', 'Wilson', 'Mike Wilson', NULL, 'field', 1, '2026-02-02T14:20:00Z', '2026-01-25T00:00:00Z', '2026-02-02T14:20:00Z'), + ('user-5', 'client@example.com', 'Jane', 'Client', 'Jane Client', NULL, 'client', 1, '2026-02-01T09:00:00Z', '2026-02-01T00:00:00Z', '2026-02-01T09:00:00Z'); + +-- seed organizations +INSERT INTO organizations (id, name, slug, type, logo_url, is_active, created_at, updated_at) +VALUES + ('org-1', 'Open Range Construction', 'open-range', 'internal', NULL, 1, '2026-01-01T00:00:00Z', '2026-01-01T00:00:00Z'), + ('org-2', 'Example Corp', 'example-corp', 'client', NULL, 1, '2026-02-01T00:00:00Z', '2026-02-01T00:00:00Z'); + +-- seed organization members +INSERT INTO organization_members (id, organization_id, user_id, role, joined_at) +VALUES + ('om-1', 'org-1', 'user-1', 'admin', '2026-01-01T00:00:00Z'), + ('om-2', 'org-1', 'user-2', 'office', '2026-01-15T00:00:00Z'), + ('om-3', 'org-1', 'user-3', 'office', '2026-01-20T00:00:00Z'), + ('om-4', 'org-1', 'user-4', 'field', '2026-01-25T00:00:00Z'), + ('om-5', 'org-2', 'user-5', 'client', '2026-02-01T00:00:00Z'); + +-- seed teams +INSERT INTO teams (id, organization_id, name, description, created_at) +VALUES + ('team-1', 'org-1', 'Engineering Team', 'Main engineering team', '2026-01-01T00:00:00Z'), + ('team-2', 'org-1', 'Field Crew Alpha', 'Field crew for site work', '2026-01-01T00:00:00Z'); + +-- seed team members +INSERT INTO team_members (id, team_id, user_id, joined_at) +VALUES + ('tm-1', 'team-1', 'user-2', '2026-01-15T00:00:00Z'), + ('tm-2', 'team-1', 'user-3', '2026-01-20T00:00:00Z'), + ('tm-3', 'team-2', 'user-4', '2026-01-25T00:00:00Z'); + +-- seed groups +INSERT INTO groups (id, organization_id, name, description, color, created_at) +VALUES + ('group-1', 'org-1', 'Project Managers', 'Project management group', '#3b82f6', '2026-01-01T00:00:00Z'), + ('group-2', 'org-1', 'Field Supervisors', 'Field supervision group', '#10b981', '2026-01-01T00:00:00Z'); + +-- seed group members +INSERT INTO group_members (id, group_id, user_id, joined_at) +VALUES + ('gm-1', 'group-1', 'user-2', '2026-01-15T00:00:00Z'), + ('gm-2', 'group-2', 'user-4', '2026-01-25T00:00:00Z'); + +-- seed project members (using existing project) +INSERT INTO project_members (id, project_id, user_id, role, assigned_at) +VALUES + ('pm-1', 'proj-o-001', 'user-1', 'admin', '2026-01-01T00:00:00Z'), + ('pm-2', 'proj-o-001', 'user-2', 'manager', '2026-01-15T00:00:00Z'), + ('pm-3', 'proj-o-001', 'user-4', 'crew', '2026-01-25T00:00:00Z'), + ('pm-4', 'proj-o-002', 'user-2', 'manager', '2026-01-16T00:00:00Z'), + ('pm-5', 'proj-o-003', 'user-3', 'manager', '2026-01-21T00:00:00Z'); diff --git a/seed.sql b/drizzle/seed.sql similarity index 100% rename from seed.sql rename to drizzle/seed.sql diff --git a/package.json b/package.json index 25b3886..8aee75e 100755 --- a/package.json +++ b/package.json @@ -51,6 +51,8 @@ "@radix-ui/react-tooltip": "^1.2.8", "@tabler/icons-react": "^3.36.1", "@tanstack/react-table": "^8.21.3", + "@workos-inc/authkit-nextjs": "^2.13.0", + "@workos-inc/node": "^8.1.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", diff --git a/src/app/globals.css b/src/app/globals.css index 4d3843a..32be34a 100755 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -202,4 +202,14 @@ } [data-mobile="true"] [data-sidebar="content"]::-webkit-scrollbar { display: none; +} + +/* hide scrollbars globally while maintaining scroll functionality */ +* { + scrollbar-width: none; /* firefox */ + -ms-overflow-style: none; /* IE and edge */ +} + +*::-webkit-scrollbar { + display: none; /* chrome, safari, opera */ } \ No newline at end of file diff --git a/src/components/ui/badge-indicator.tsx b/src/components/ui/badge-indicator.tsx new file mode 100755 index 0000000..beea074 --- /dev/null +++ b/src/components/ui/badge-indicator.tsx @@ -0,0 +1,32 @@ +import { cn } from "@/lib/utils" + +interface BadgeIndicatorProps { + children: React.ReactNode + count?: number + dot?: boolean + className?: string +} + +export function BadgeIndicator({ + children, + count, + dot, + className, +}: BadgeIndicatorProps) { + const showCount = count !== undefined && count > 0 + const showDot = dot && !showCount + + return ( + + {children} + {showCount && ( + + {count > 99 ? "99+" : count} + + )} + {showDot && ( + + )} + + ) +} diff --git a/src/components/ui/carousel-pages.tsx b/src/components/ui/carousel-pages.tsx new file mode 100755 index 0000000..922a1c4 --- /dev/null +++ b/src/components/ui/carousel-pages.tsx @@ -0,0 +1,81 @@ +"use client" + +import * as React from "react" +import useEmblaCarousel from "embla-carousel-react" +import { cn } from "@/lib/utils" + +interface CarouselPagesProps { + children: React.ReactNode[] + className?: string +} + +export function CarouselPages({ children, className }: CarouselPagesProps) { + const [emblaRef, emblaApi] = useEmblaCarousel({ + loop: false, + skipSnaps: false, + watchDrag: (emblaApi, evt) => { + // Don't capture drag events on interactive elements + const target = evt.target as HTMLElement + const interactiveElements = ['INPUT', 'SELECT', 'BUTTON', 'A', 'TEXTAREA'] + const isInteractive = interactiveElements.includes(target.tagName) + const isSlider = target.closest('[role="slider"]') + const isSwitch = target.closest('[role="switch"]') + const isSelect = target.closest('[role="combobox"]') || target.closest('[role="listbox"]') + + return !isInteractive && !isSlider && !isSwitch && !isSelect + } + }) + const [selectedIndex, setSelectedIndex] = React.useState(0) + + const onSelect = React.useCallback(() => { + if (!emblaApi) return + setSelectedIndex(emblaApi.selectedScrollSnap()) + }, [emblaApi]) + + React.useEffect(() => { + if (!emblaApi) return + onSelect() + emblaApi.on("select", onSelect) + emblaApi.on("reInit", onSelect) + return () => { + emblaApi.off("select", onSelect) + emblaApi.off("reInit", onSelect) + } + }, [emblaApi, onSelect]) + + return ( +
+
+
+ {children.map((child, index) => ( +
+ {child} +
+ ))} +
+
+ + {children.length > 1 && ( +
+ {children.map((_, index) => ( +
+ )} +
+ ) +} diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx index 8cb4ca7..a19a462 100755 --- a/src/components/ui/command.tsx +++ b/src/components/ui/command.tsx @@ -49,10 +49,13 @@ function CommandDialog({ {description} - + {children} @@ -67,7 +70,7 @@ function CommandInput({ return (
void + placeholder?: string + className?: string +} + +export function DatePicker({ + value, + onChange, + placeholder = "Pick a date", + className, +}: DatePickerProps) { + const [open, setOpen] = React.useState(false) + + const selected = value + ? parse(value, "yyyy-MM-dd", new Date()) + : undefined + + return ( + + + + + + { + onChange(date ? format(date, "yyyy-MM-dd") : "") + setOpen(false) + }} + /> + + + ) +} diff --git a/src/components/ui/responsive-dialog.tsx b/src/components/ui/responsive-dialog.tsx new file mode 100755 index 0000000..0d7364f --- /dev/null +++ b/src/components/ui/responsive-dialog.tsx @@ -0,0 +1,193 @@ +"use client" + +import * as React from "react" +import { useIsMobile } from "@/hooks/use-mobile" +import { CarouselPages } from "@/components/ui/carousel-pages" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, +} from "@/components/ui/sheet" +import { cn } from "@/lib/utils" + +interface ResponsiveDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + children: React.ReactNode + title?: string + description?: string + className?: string + /** + * Use Sheet on mobile instead of Dialog + * @default false - uses Dialog with mobile optimizations + */ + useSheetOnMobile?: boolean +} + +/** + * A responsive dialog component based on the account modal pattern. + * Provides consistent mobile and desktop experience with proper scrolling. + * + * Key features: + * - Max height 90vh with scrollable content + * - Optimized padding: p-4 on mobile, p-6 on desktop + * - Consistent text sizing: title is text-base, description is text-xs + * - Content spacing: space-y-3 with py-1 + * + * @example + * ```tsx + * + * + *
...
+ *
+ * + * + * + *
+ * ``` + */ +export function ResponsiveDialog({ + open, + onOpenChange, + children, + title, + description, + className, + useSheetOnMobile = false, +}: ResponsiveDialogProps) { + const isMobile = useIsMobile() + + if (isMobile && useSheetOnMobile) { + return ( + + + {(title || description) && ( + + {title && ( + + {title} + + )} + {description && ( + + {description} + + )} + + )} + {children} + + + ) + } + + return ( + + + {(title || description) && ( + + {title && ( + + {title} + + )} + {description && ( + + {description} + + )} + + )} + {children} + + + ) +} + +/** + * Body container for ResponsiveDialog content. + * Scrollable when content exceeds available space. + * On mobile, if pages prop is provided, renders as swipeable carousel. + */ +export function ResponsiveDialogBody({ + children, + className, + pages, +}: { + children?: React.ReactNode + className?: string + pages?: React.ReactNode[] +}) { + const isMobile = useIsMobile() + + // If pages are provided and we're on mobile, use carousel + if (pages && pages.length > 0 && isMobile) { + return ( +
+ + {pages.map((page, index) => ( +
+ {page} +
+ ))} +
+
+ ) + } + + // Default: single scrollable container + return ( +
+ {children} +
+ ) +} + +/** + * Footer with action buttons for ResponsiveDialog. + * Buttons stack vertically on mobile (reverse order), horizontally on desktop. + * Uses h-9 for buttons to match account modal. + */ +export function ResponsiveDialogFooter({ + children, + className, +}: { + children: React.ReactNode + className?: string +}) { + return ( +
+ {children} +
+ ) +} diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx index 84649ad..4d73dca 100755 --- a/src/components/ui/sheet.tsx +++ b/src/components/ui/sheet.tsx @@ -48,9 +48,11 @@ function SheetContent({ className, children, side = "right", + showClose = true, ...props }: React.ComponentProps & { side?: "top" | "right" | "bottom" | "left" + showClose?: boolean }) { return ( @@ -62,7 +64,7 @@ function SheetContent({ side === "right" && "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm", side === "left" && - "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm", + "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 sm:max-w-sm", side === "top" && "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b", side === "bottom" && @@ -72,10 +74,12 @@ function SheetContent({ {...props} > {children} - - - Close - + {showClose && ( + + + Close + + )} ) diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index 7fb31e8..691c2cf 100755 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -27,7 +27,7 @@ import { const SIDEBAR_COOKIE_NAME = "sidebar_state" const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7 -const SIDEBAR_WIDTH = "16rem" +const SIDEBAR_WIDTH = "11rem" const SIDEBAR_WIDTH_MOBILE = "18rem" const SIDEBAR_WIDTH_ICON = "3rem" const SIDEBAR_KEYBOARD_SHORTCUT = "b" @@ -187,13 +187,9 @@ function Sidebar({ data-sidebar="sidebar" data-slot="sidebar" data-mobile="true" - className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:text-sidebar-foreground [&>button]:opacity-80 [&>button]:hover:opacity-100" - style={ - { - "--sidebar-width": SIDEBAR_WIDTH_MOBILE, - } as React.CSSProperties - } + className="bg-sidebar text-sidebar-foreground w-[85vw] max-w-none p-0 border-0" side={side} + showClose={false} > Sidebar diff --git a/src/db/index.ts b/src/db/index.ts index bae04f4..ebc14ff 100755 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,6 +1,9 @@ import { drizzle } from "drizzle-orm/d1" import * as schema from "./schema" +import * as netsuiteSchema from "./schema-netsuite" + +const allSchemas = { ...schema, ...netsuiteSchema } export function getDb(d1: D1Database) { - return drizzle(d1, { schema }) + return drizzle(d1, { schema: allSchemas }) } diff --git a/src/db/schema-netsuite.ts b/src/db/schema-netsuite.ts new file mode 100755 index 0000000..dfc1b2c --- /dev/null +++ b/src/db/schema-netsuite.ts @@ -0,0 +1,153 @@ +import { + sqliteTable, + text, + integer, + real, +} from "drizzle-orm/sqlite-core" +import { projects, customers, vendors } from "./schema" + +// oauth token storage (encrypted at rest) +export const netsuiteAuth = sqliteTable("netsuite_auth", { + id: text("id").primaryKey(), + accountId: text("account_id").notNull(), + accessTokenEncrypted: text("access_token_encrypted").notNull(), + refreshTokenEncrypted: text("refresh_token_encrypted").notNull(), + expiresIn: integer("expires_in").notNull(), + tokenType: text("token_type").notNull(), + issuedAt: integer("issued_at").notNull(), + createdAt: text("created_at").notNull(), + updatedAt: text("updated_at").notNull(), +}) + +// per-record sync tracking +export const netsuiteSyncMetadata = sqliteTable("netsuite_sync_metadata", { + id: text("id").primaryKey(), + localTable: text("local_table").notNull(), + localRecordId: text("local_record_id").notNull(), + netsuiteRecordType: text("netsuite_record_type").notNull(), + netsuiteInternalId: text("netsuite_internal_id"), + lastSyncedAt: text("last_synced_at"), + lastModifiedLocal: text("last_modified_local"), + lastModifiedRemote: text("last_modified_remote"), + syncStatus: text("sync_status").notNull().default("synced"), + conflictData: text("conflict_data"), + errorMessage: text("error_message"), + retryCount: integer("retry_count").notNull().default(0), + createdAt: text("created_at").notNull(), + updatedAt: text("updated_at").notNull(), +}) + +// sync run history +export const netsuiteSyncLog = sqliteTable("netsuite_sync_log", { + id: text("id").primaryKey(), + syncType: text("sync_type").notNull(), + recordType: text("record_type").notNull(), + direction: text("direction").notNull(), + status: text("status").notNull(), + recordsProcessed: integer("records_processed").notNull().default(0), + recordsFailed: integer("records_failed").notNull().default(0), + errorSummary: text("error_summary"), + startedAt: text("started_at").notNull(), + completedAt: text("completed_at"), + createdAt: text("created_at").notNull(), +}) + +// financial tables + +export const invoices = sqliteTable("invoices", { + id: text("id").primaryKey(), + netsuiteId: text("netsuite_id"), + customerId: text("customer_id") + .notNull() + .references(() => customers.id), + projectId: text("project_id") + .references(() => projects.id), + invoiceNumber: text("invoice_number"), + status: text("status").notNull().default("draft"), + issueDate: text("issue_date").notNull(), + dueDate: text("due_date"), + subtotal: real("subtotal").notNull().default(0), + tax: real("tax").notNull().default(0), + total: real("total").notNull().default(0), + amountPaid: real("amount_paid").notNull().default(0), + amountDue: real("amount_due").notNull().default(0), + memo: text("memo"), + lineItems: text("line_items"), + createdAt: text("created_at").notNull(), + updatedAt: text("updated_at").notNull(), +}) + +export const vendorBills = sqliteTable("vendor_bills", { + id: text("id").primaryKey(), + netsuiteId: text("netsuite_id"), + vendorId: text("vendor_id") + .notNull() + .references(() => vendors.id), + projectId: text("project_id") + .references(() => projects.id), + billNumber: text("bill_number"), + status: text("status").notNull().default("pending"), + billDate: text("bill_date").notNull(), + dueDate: text("due_date"), + subtotal: real("subtotal").notNull().default(0), + tax: real("tax").notNull().default(0), + total: real("total").notNull().default(0), + amountPaid: real("amount_paid").notNull().default(0), + amountDue: real("amount_due").notNull().default(0), + memo: text("memo"), + lineItems: text("line_items"), + createdAt: text("created_at").notNull(), + updatedAt: text("updated_at").notNull(), +}) + +export const payments = sqliteTable("payments", { + id: text("id").primaryKey(), + netsuiteId: text("netsuite_id"), + customerId: text("customer_id") + .references(() => customers.id), + vendorId: text("vendor_id") + .references(() => vendors.id), + projectId: text("project_id") + .references(() => projects.id), + paymentType: text("payment_type").notNull(), + amount: real("amount").notNull(), + paymentDate: text("payment_date").notNull(), + paymentMethod: text("payment_method"), + referenceNumber: text("reference_number"), + memo: text("memo"), + createdAt: text("created_at").notNull(), + updatedAt: text("updated_at").notNull(), +}) + +export const creditMemos = sqliteTable("credit_memos", { + id: text("id").primaryKey(), + netsuiteId: text("netsuite_id"), + customerId: text("customer_id") + .notNull() + .references(() => customers.id), + projectId: text("project_id") + .references(() => projects.id), + memoNumber: text("memo_number"), + status: text("status").notNull().default("draft"), + issueDate: text("issue_date").notNull(), + total: real("total").notNull().default(0), + amountApplied: real("amount_applied").notNull().default(0), + amountRemaining: real("amount_remaining").notNull().default(0), + memo: text("memo"), + lineItems: text("line_items"), + createdAt: text("created_at").notNull(), + updatedAt: text("updated_at").notNull(), +}) + +// type exports +export type NetSuiteAuth = typeof netsuiteAuth.$inferSelect +export type NetSuiteSyncMetadata = typeof netsuiteSyncMetadata.$inferSelect +export type NetSuiteSyncLog = typeof netsuiteSyncLog.$inferSelect +export type Invoice = typeof invoices.$inferSelect +export type NewInvoice = typeof invoices.$inferInsert +export type VendorBill = typeof vendorBills.$inferSelect +export type NewVendorBill = typeof vendorBills.$inferInsert +export type Payment = typeof payments.$inferSelect +export type NewPayment = typeof payments.$inferInsert +export type CreditMemo = typeof creditMemos.$inferSelect +export type NewCreditMemo = typeof creditMemos.$inferInsert diff --git a/src/db/schema.ts b/src/db/schema.ts index d0b9cab..72dce66 100755 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -4,6 +4,87 @@ import { integer, } from "drizzle-orm/sqlite-core" +// Auth and user management tables +export const users = sqliteTable("users", { + id: text("id").primaryKey(), // workos user id + email: text("email").notNull().unique(), + firstName: text("first_name"), + lastName: text("last_name"), + displayName: text("display_name"), + avatarUrl: text("avatar_url"), + role: text("role").notNull().default("office"), // admin, office, field, client + isActive: integer("is_active", { mode: "boolean" }).notNull().default(true), + lastLoginAt: text("last_login_at"), + createdAt: text("created_at").notNull(), + updatedAt: text("updated_at").notNull(), +}) + +export const organizations = sqliteTable("organizations", { + id: text("id").primaryKey(), // workos org id + name: text("name").notNull(), + slug: text("slug").notNull().unique(), + type: text("type").notNull(), // "internal" or "client" + logoUrl: text("logo_url"), + isActive: integer("is_active", { mode: "boolean" }).notNull().default(true), + createdAt: text("created_at").notNull(), + updatedAt: text("updated_at").notNull(), +}) + +export const organizationMembers = sqliteTable("organization_members", { + id: text("id").primaryKey(), + organizationId: text("organization_id") + .notNull() + .references(() => organizations.id, { onDelete: "cascade" }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + role: text("role").notNull(), + joinedAt: text("joined_at").notNull(), +}) + +export const teams = sqliteTable("teams", { + id: text("id").primaryKey(), + organizationId: text("organization_id") + .notNull() + .references(() => organizations.id, { onDelete: "cascade" }), + name: text("name").notNull(), + description: text("description"), + createdAt: text("created_at").notNull(), +}) + +export const teamMembers = sqliteTable("team_members", { + id: text("id").primaryKey(), + teamId: text("team_id") + .notNull() + .references(() => teams.id, { onDelete: "cascade" }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + joinedAt: text("joined_at").notNull(), +}) + +export const groups = sqliteTable("groups", { + id: text("id").primaryKey(), + organizationId: text("organization_id") + .notNull() + .references(() => organizations.id, { onDelete: "cascade" }), + name: text("name").notNull(), + description: text("description"), + color: text("color"), // hex color for badges + createdAt: text("created_at").notNull(), +}) + +export const groupMembers = sqliteTable("group_members", { + id: text("id").primaryKey(), + groupId: text("group_id") + .notNull() + .references(() => groups.id, { onDelete: "cascade" }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + joinedAt: text("joined_at").notNull(), +}) + export const projects = sqliteTable("projects", { id: text("id").primaryKey(), name: text("name").notNull(), @@ -11,9 +92,23 @@ export const projects = sqliteTable("projects", { address: text("address"), clientName: text("client_name"), projectManager: text("project_manager"), + organizationId: text("organization_id").references(() => organizations.id), + netsuiteJobId: text("netsuite_job_id"), createdAt: text("created_at").notNull(), }) +export const projectMembers = sqliteTable("project_members", { + id: text("id").primaryKey(), + projectId: text("project_id") + .notNull() + .references(() => projects.id, { onDelete: "cascade" }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + role: text("role").notNull(), + assignedAt: text("assigned_at").notNull(), +}) + export const scheduleTasks = sqliteTable("schedule_tasks", { id: text("id").primaryKey(), projectId: text("project_id") @@ -79,9 +174,14 @@ export const scheduleBaselines = sqliteTable("schedule_baselines", { export const customers = sqliteTable("customers", { id: text("id").primaryKey(), name: text("name").notNull(), + company: text("company"), email: text("email"), phone: text("phone"), + address: text("address"), + notes: text("notes"), + netsuiteId: text("netsuite_id"), createdAt: text("created_at").notNull(), + updatedAt: text("updated_at"), }) export const vendors = sqliteTable("vendors", { @@ -91,7 +191,9 @@ export const vendors = sqliteTable("vendors", { email: text("email"), phone: text("phone"), address: text("address"), + netsuiteId: text("netsuite_id"), createdAt: text("created_at").notNull(), + updatedAt: text("updated_at"), }) export type Project = typeof projects.$inferSelect @@ -124,3 +226,21 @@ export type Vendor = typeof vendors.$inferSelect export type NewVendor = typeof vendors.$inferInsert export type Feedback = typeof feedback.$inferSelect export type NewFeedback = typeof feedback.$inferInsert + +// Auth and user management types +export type User = typeof users.$inferSelect +export type NewUser = typeof users.$inferInsert +export type Organization = typeof organizations.$inferSelect +export type NewOrganization = typeof organizations.$inferInsert +export type OrganizationMember = typeof organizationMembers.$inferSelect +export type NewOrganizationMember = typeof organizationMembers.$inferInsert +export type Team = typeof teams.$inferSelect +export type NewTeam = typeof teams.$inferInsert +export type TeamMember = typeof teamMembers.$inferSelect +export type NewTeamMember = typeof teamMembers.$inferInsert +export type Group = typeof groups.$inferSelect +export type NewGroup = typeof groups.$inferInsert +export type GroupMember = typeof groupMembers.$inferSelect +export type NewGroupMember = typeof groupMembers.$inferInsert +export type ProjectMember = typeof projectMembers.$inferSelect +export type NewProjectMember = typeof projectMembers.$inferInsert diff --git a/src/lib/utils.ts b/src/lib/utils.ts index d084cca..f2a5826 100755 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -4,3 +4,10 @@ import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } + +export function formatCurrency(amount: number): string { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(amount) +} diff --git a/wrangler.jsonc b/wrangler.jsonc index 5e5c1ea..867cc08 100755 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -41,5 +41,8 @@ "database_id": "cd6983ff-d286-4042-a823-6b2433c9fba7", "migrations_dir": "drizzle" } - ] + ], + "vars": { + "WORKOS_REDIRECT_URI": "https://compass.openrangeconstruction.ltd/api/auth/callback" + } } \ No newline at end of file