feat(04-03): VendorService, ExpenseService, API routes, migration, and 15 passing tests
- VendorService: CRUD with unique name validation per tenant - ExpenseService: create, approve, post, void with full JE integration - Auto-generated EXP-NNNN expense numbers per tenant - JE posting: DR category expense account, CR cash (1010) or bank (1020) - Optional approval workflow: requireApproval flag controls DRAFT-only vs immediate post - Void reverses JE via JournalEntryService.reverseEntry - Custom category creation with COA account validation - System categories protected from deletion - API routes: expenses CRUD, approve, categories CRUD, vendors CRUD - Migration: add_expense_vendor_models applied via db push + resolve - 15 integration tests covering all expense lifecycle scenarios Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ExpenseStatus" AS ENUM ('DRAFT', 'APPROVED', 'POSTED', 'VOIDED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ExpensePaymentMethod" AS ENUM ('CASH', 'BANK_TRANSFER', 'CHECK');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Vendor" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"contactPerson" TEXT,
|
||||
"phone" TEXT,
|
||||
"email" TEXT,
|
||||
"address" TEXT,
|
||||
"servicesProvided" TEXT,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Vendor_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ExpenseCategory" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"accountCode" TEXT NOT NULL,
|
||||
"isSystemCategory" BOOLEAN NOT NULL DEFAULT false,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "ExpenseCategory_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Expense" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"expenseNumber" TEXT NOT NULL,
|
||||
"categoryId" TEXT NOT NULL,
|
||||
"vendorId" TEXT,
|
||||
"amount" DECIMAL(10,2) NOT NULL,
|
||||
"expenseDate" TIMESTAMP(3) NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"paymentMethod" "ExpensePaymentMethod" NOT NULL,
|
||||
"status" "ExpenseStatus" NOT NULL DEFAULT 'DRAFT',
|
||||
"attachmentPath" TEXT,
|
||||
"journalEntryId" TEXT,
|
||||
"createdById" TEXT NOT NULL,
|
||||
"approvedById" TEXT,
|
||||
"approvedAt" TIMESTAMP(3),
|
||||
"postedAt" TIMESTAMP(3),
|
||||
"voidedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Expense_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Vendor_tenantId_name_key" ON "Vendor"("tenantId", "name");
|
||||
CREATE INDEX "Vendor_tenantId_idx" ON "Vendor"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ExpenseCategory_tenantId_name_key" ON "ExpenseCategory"("tenantId", "name");
|
||||
CREATE INDEX "ExpenseCategory_tenantId_idx" ON "ExpenseCategory"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Expense_tenantId_expenseNumber_key" ON "Expense"("tenantId", "expenseNumber");
|
||||
CREATE INDEX "Expense_tenantId_idx" ON "Expense"("tenantId");
|
||||
CREATE INDEX "Expense_tenantId_categoryId_idx" ON "Expense"("tenantId", "categoryId");
|
||||
CREATE INDEX "Expense_tenantId_vendorId_idx" ON "Expense"("tenantId", "vendorId");
|
||||
CREATE INDEX "Expense_tenantId_status_idx" ON "Expense"("tenantId", "status");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Expense" ADD CONSTRAINT "Expense_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "ExpenseCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
ALTER TABLE "Expense" ADD CONSTRAINT "Expense_vendorId_fkey" FOREIGN KEY ("vendorId") REFERENCES "Vendor"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
ALTER TABLE "Expense" ADD CONSTRAINT "Expense_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
ALTER TABLE "Expense" ADD CONSTRAINT "Expense_approvedById_fkey" FOREIGN KEY ("approvedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
Reference in New Issue
Block a user