Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d9d3df4
Add organization structure
GAsplund Oct 17, 2025
35c4bf1
Change routing for org ID
GAsplund Nov 4, 2025
ea2a096
Update Prisma
GAsplund Nov 5, 2025
f6fb893
Add basic routing for organizations
GAsplund Nov 7, 2025
1592b50
Add basic admin pages for orgs
GAsplund Nov 14, 2025
2aea88e
Improve table styles and behavior
GAsplund Nov 14, 2025
e47e2b9
Fetch expenses and invoices based on org
GAsplund Dec 5, 2025
8594753
Start adding org settings
GAsplund Dec 21, 2025
991bc6a
Update React and Next
GAsplund Dec 21, 2025
d098e0d
Add organization selector
GAsplund Dec 22, 2025
738e677
Revert to Next 15
GAsplund Dec 22, 2025
6c6c864
Add note about bank accounts being shared for now
GAsplund Dec 23, 2025
3982a81
Don't default orgId in CRUD
GAsplund Dec 23, 2025
4604a5c
Handle orgId parameter in navigation
GAsplund Dec 23, 2025
a122b03
Place admin route correctly, add manual notifs
GAsplund Dec 23, 2025
9d48304
Send emails to specific groups
GAsplund Dec 23, 2025
6bdad3c
Display correct dates on expenses
GAsplund Dec 24, 2025
5dcf78c
Resolve redirect and editing issues in expenses
GAsplund Dec 24, 2025
c780f38
Allow no group to be selected
GAsplund Dec 24, 2025
ae29c9f
Add org IDs to (hopefully) all breadcrumbs
GAsplund Dec 24, 2025
69cc5ea
Improve receipt creator performance
GAsplund Dec 25, 2025
7f0470b
Add file drop zone
GAsplund Dec 25, 2025
b65c93d
Include nicks in names
GAsplund Dec 25, 2025
68dcfd3
Add sandbox finance on development
GAsplund Dec 25, 2025
15e164c
Disable admin test mode in production
GAsplund Dec 25, 2025
529ac8d
Allow changing groups
GAsplund Dec 25, 2025
f3a8061
Improve table click behavior
GAsplund Dec 25, 2025
26afb2c
Correct redirects for bank accounts
GAsplund Dec 25, 2025
df0d62b
Add organization primary emails and owner groups
GAsplund Dec 25, 2025
6bd1183
Show organization name in receipts
GAsplund Dec 25, 2025
be122a0
Add dropdown to select super group owner
GAsplund Dec 25, 2025
9d80efb
Reduce routing complexity
GAsplund Dec 25, 2025
becf536
Add ability to only view items
GAsplund Dec 25, 2025
f511652
Merge remote-tracking branch 'origin/main' into feature/organizations
GAsplund Dec 25, 2025
279a3ed
Add dev dependency for typescript-eslint/parser
GAsplund Dec 25, 2025
760d30f
i18n
GAsplund Dec 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ NEXTAUTH_SECRET=secret
NEXTAUTH_URL=http://localhost:3000/api/auth
MEDIA_PATH="./media"
BOARD_GROUP="styrit"
DIVISION_TREASURER_EMAIL="kassor.styrit@chalmers.it"
TREASURER_POST_ID="cd6c2fc4-9fac-42e3-873c-6cfd71c3dd8d"
GOTIFY_ROOT_URL=http://localhost:8080
GOTIFY_TOKEN="123abc"
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ package-lock.json

.idea/
/db

.direnv
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cashit",
"version": "0.5.0",
"version": "0.6.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
Expand All @@ -9,13 +9,13 @@
"lint": "next lint"
},
"dependencies": {
"@chakra-ui/react": "3.26.0",
"@chakra-ui/react": "3.30.0",
"@emotion/react": "^11.14.0",
"@prisma/client": "6.6.0",
"@react-pdf/renderer": "^4.2.1",
"@tanstack/react-table": "^8.21.3",
"dayjs": "^1.11.13",
"next": "15.3.6",
"next": "15.5.9",
"next-auth": "^4.24.11",
"next-i18n-router": "^5.5.1",
"next-themes": "^0.4.4",
Expand All @@ -31,14 +31,15 @@
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"eslint": "^8",
"eslint-config-next": "15.3.6",
"eslint-config-next": "15.5.9",
"@typescript-eslint/parser": "8.50.1",
"prisma": "6.6.0",
"typescript": "^5"
},
"pnpm": {
"overrides": {
"@types/react": "19.1.0",
"@types/react-dom": "19.1.0"
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3"
}
}
}
3,001 changes: 1,645 additions & 1,356 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions prisma/migrations/20251017170007_organiations/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
Warnings:

- The values [CARD] on the enum `ExpenseType` will be removed. If these variants are still used in the database, this will fail.
- Added the required column `organizationId` to the `Expense` table without a default value. This is not possible if the table is not empty.
- Added the required column `organizationId` to the `Invoice` table without a default value. This is not possible if the table is not empty.
- Added the required column `organizationId` to the `NameList` table without a default value. This is not possible if the table is not empty.
- Added the required column `organizationId` to the `ZettleSale` table without a default value. This is not possible if the table is not empty.

*/
-- AlterEnum
BEGIN;
CREATE TYPE "ExpenseType_new" AS ENUM ('EXPENSE', 'INVOICE');
ALTER TABLE "Expense" ALTER COLUMN "type" TYPE "ExpenseType_new" USING ("type"::text::"ExpenseType_new");
ALTER TYPE "ExpenseType" RENAME TO "ExpenseType_old";
ALTER TYPE "ExpenseType_new" RENAME TO "ExpenseType";
DROP TYPE "ExpenseType_old";
COMMIT;

-- CreateTable (moved before AlterTable operations)
CREATE TABLE "Organization" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"ownerGammaSuperGroupId" TEXT NOT NULL,
"primaryEmail" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "Organization_pkey" PRIMARY KEY ("id")
);

-- Create default organization only if there are existing records
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM "Expense") OR
EXISTS (SELECT 1 FROM "Invoice") OR
EXISTS (SELECT 1 FROM "NameList") OR
EXISTS (SELECT 1 FROM "ZettleSale") THEN
INSERT INTO "Organization" (
"name",
"ownerGammaSuperGroupId",
"primaryEmail",
"createdAt",
"updatedAt"
)
VALUES (
'Default Organization',
'45432d44-0de4-4ed0-88ce-8cdf31b72f73',
'default@example.com',
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
END IF;
END $$;

-- AlterTable for Expense
ALTER TABLE "Expense" ADD COLUMN "organizationId" INTEGER;
UPDATE "Expense" SET "organizationId" = (SELECT id FROM "Organization" WHERE name = 'Default Organization') WHERE "organizationId" IS NULL;
ALTER TABLE "Expense" ALTER COLUMN "organizationId" SET NOT NULL;

-- AlterTable for Invoice
ALTER TABLE "Invoice" ADD COLUMN "organizationId" INTEGER;
UPDATE "Invoice" SET "organizationId" = (SELECT id FROM "Organization" WHERE name = 'Default Organization') WHERE "organizationId" IS NULL;
ALTER TABLE "Invoice" ALTER COLUMN "organizationId" SET NOT NULL;

-- AlterTable for NameList
ALTER TABLE "NameList" ADD COLUMN "organizationId" INTEGER;
UPDATE "NameList" SET "organizationId" = (SELECT id FROM "Organization" WHERE name = 'Default Organization') WHERE "organizationId" IS NULL;
ALTER TABLE "NameList" ALTER COLUMN "organizationId" SET NOT NULL;

-- AlterTable for ZettleSale
ALTER TABLE "ZettleSale" ADD COLUMN "organizationId" INTEGER;
UPDATE "ZettleSale" SET "organizationId" = (SELECT id FROM "Organization" WHERE name = 'Default Organization') WHERE "organizationId" IS NULL;
ALTER TABLE "ZettleSale" ALTER COLUMN "organizationId" SET NOT NULL;

-- AddForeignKey
ALTER TABLE "Expense" ADD CONSTRAINT "Expense_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

ALTER TABLE "Invoice" ADD CONSTRAINT "Invoice_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

ALTER TABLE "NameList" ADD CONSTRAINT "NameList_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

ALTER TABLE "ZettleSale" ADD CONSTRAINT "ZettleSale_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
37 changes: 29 additions & 8 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,24 @@ enum RequestStatus {
REJECTED
}

model Organization {
id Int @id @default(autoincrement())
name String
primaryEmail String
ownerGammaSuperGroupId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

expenses Expense[]
invoices Invoice[]
nameLists NameList[]
zettleSales ZettleSale[]
}

model Expense {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
organization Organization @relation(fields: [organizationId], references: [id])
organizationId Int
gammaSuperGroupId String?
gammaGroupId String?
gammaUserId String
Expand All @@ -36,11 +52,12 @@ model Expense {
enum ExpenseType {
EXPENSE
INVOICE
CARD
}

model Invoice {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
organization Organization @relation(fields: [organizationId], references: [id])
organizationId Int
gammaSuperGroupId String?
gammaGroupId String?
gammaUserId String
Expand Down Expand Up @@ -83,6 +100,8 @@ enum InvoiceItemVat {

model NameList {
id Int @id @default(autoincrement())
organization Organization @relation(fields: [organizationId], references: [id])
organizationId Int
gammaSuperGroupId String?
gammaGroupId String?
gammaUserId String
Expand Down Expand Up @@ -142,16 +161,18 @@ model NamedMedia {
}

model ZettleSale {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
organization Organization @relation(fields: [organizationId], references: [id])
organizationId Int
gammaSuperGroupId String
gammaGroupId String
gammaUserId String
amount Float @db.DoublePrecision
amount Float @db.DoublePrecision
description String
name String
saleDate DateTime @db.Date
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
saleDate DateTime @db.Date
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@unique([gammaGroupId, saleDate])
}
Expand Down
Loading