Remove multi-tenant company_id scoping from entire codebase

Drop company_id column from all 22 domain tables via migration.
Remove companyId from JWT payload, auth plugins, all service method
signatures (~215 occurrences), all route handlers (~105 occurrences),
test runner, test suites, and frontend auth store/types.

The company table stays as store settings (name, timezone). Tenant
isolation in a SaaS deployment would be at the database level (one
DB per customer) not the application level.

All 107 API tests pass. Zero TSC errors across all packages.
This commit is contained in:
Ryan Moon
2026-03-29 14:58:33 -05:00
parent 55f8591cf1
commit d36c6f7135
35 changed files with 353 additions and 511 deletions

View File

@@ -18,11 +18,10 @@ import {
import { UnitStatusService, ItemConditionService } from './lookup.service.js'
export const ProductService = {
async create(db: PostgresJsDatabase<any>, companyId: string, input: ProductCreateInput) {
async create(db: PostgresJsDatabase<any>, input: ProductCreateInput) {
const [product] = await db
.insert(products)
.values({
companyId,
...input,
price: input.price?.toString(),
minPrice: input.minPrice?.toString(),
@@ -32,17 +31,17 @@ export const ProductService = {
return product
},
async getById(db: PostgresJsDatabase<any>, companyId: string, id: string) {
async getById(db: PostgresJsDatabase<any>, id: string) {
const [product] = await db
.select()
.from(products)
.where(and(eq(products.id, id), eq(products.companyId, companyId)))
.where(eq(products.id, id))
.limit(1)
return product ?? null
},
async list(db: PostgresJsDatabase<any>, companyId: string, params: PaginationInput) {
const baseWhere = and(eq(products.companyId, companyId), eq(products.isActive, true))
async list(db: PostgresJsDatabase<any>, params: PaginationInput) {
const baseWhere = eq(products.isActive, true)
const searchCondition = params.q
? buildSearchCondition(params.q, [products.name, products.sku, products.upc, products.brand])
@@ -72,17 +71,15 @@ export const ProductService = {
async update(
db: PostgresJsDatabase<any>,
companyId: string,
id: string,
input: ProductUpdateInput,
changedBy?: string,
) {
if (input.price !== undefined || input.minPrice !== undefined) {
const existing = await this.getById(db, companyId, id)
const existing = await this.getById(db, id)
if (existing) {
await db.insert(priceHistory).values({
productId: id,
companyId,
previousPrice: existing.price,
newPrice: input.price?.toString() ?? existing.price ?? '0',
previousMinPrice: existing.minPrice,
@@ -101,36 +98,35 @@ export const ProductService = {
const [product] = await db
.update(products)
.set(updates)
.where(and(eq(products.id, id), eq(products.companyId, companyId)))
.where(eq(products.id, id))
.returning()
return product ?? null
},
async softDelete(db: PostgresJsDatabase<any>, companyId: string, id: string) {
async softDelete(db: PostgresJsDatabase<any>, id: string) {
const [product] = await db
.update(products)
.set({ isActive: false, updatedAt: new Date() })
.where(and(eq(products.id, id), eq(products.companyId, companyId)))
.where(eq(products.id, id))
.returning()
return product ?? null
},
}
export const InventoryUnitService = {
async create(db: PostgresJsDatabase<any>, companyId: string, input: InventoryUnitCreateInput) {
async create(db: PostgresJsDatabase<any>, input: InventoryUnitCreateInput) {
if (input.condition) {
const valid = await ItemConditionService.validateSlug(db, companyId, input.condition)
const valid = await ItemConditionService.validateSlug(db, input.condition)
if (!valid) throw new ValidationError(`Invalid condition: "${input.condition}"`)
}
if (input.status) {
const valid = await UnitStatusService.validateSlug(db, companyId, input.status)
const valid = await UnitStatusService.validateSlug(db, input.status)
if (!valid) throw new ValidationError(`Invalid status: "${input.status}"`)
}
const [unit] = await db
.insert(inventoryUnits)
.values({
companyId,
productId: input.productId,
locationId: input.locationId,
serialNumber: input.serialNumber,
@@ -144,25 +140,21 @@ export const InventoryUnitService = {
return unit
},
async getById(db: PostgresJsDatabase<any>, companyId: string, id: string) {
async getById(db: PostgresJsDatabase<any>, id: string) {
const [unit] = await db
.select()
.from(inventoryUnits)
.where(and(eq(inventoryUnits.id, id), eq(inventoryUnits.companyId, companyId)))
.where(eq(inventoryUnits.id, id))
.limit(1)
return unit ?? null
},
async listByProduct(
db: PostgresJsDatabase<any>,
companyId: string,
productId: string,
params: PaginationInput,
) {
const where = and(
eq(inventoryUnits.companyId, companyId),
eq(inventoryUnits.productId, productId),
)
const where = eq(inventoryUnits.productId, productId)
const sortableColumns: Record<string, Column> = {
serial_number: inventoryUnits.serialNumber,
@@ -185,16 +177,15 @@ export const InventoryUnitService = {
async update(
db: PostgresJsDatabase<any>,
companyId: string,
id: string,
input: InventoryUnitUpdateInput,
) {
if (input.condition) {
const valid = await ItemConditionService.validateSlug(db, companyId, input.condition)
const valid = await ItemConditionService.validateSlug(db, input.condition)
if (!valid) throw new ValidationError(`Invalid condition: "${input.condition}"`)
}
if (input.status) {
const valid = await UnitStatusService.validateSlug(db, companyId, input.status)
const valid = await UnitStatusService.validateSlug(db, input.status)
if (!valid) throw new ValidationError(`Invalid status: "${input.status}"`)
}
@@ -204,7 +195,7 @@ export const InventoryUnitService = {
const [unit] = await db
.update(inventoryUnits)
.set(updates)
.where(and(eq(inventoryUnits.id, id), eq(inventoryUnits.companyId, companyId)))
.where(eq(inventoryUnits.id, id))
.returning()
return unit ?? null
},