Add lookup tables, payment methods, tax exemptions, and processor link APIs
Replace unit_status and item_condition pgEnums with company-scoped lookup tables that support custom values. Add account_payment_method table, tax_exemption table with approve/revoke workflow, and CRUD routes for processor links. Validate inventory unit status/condition against lookup tables at service layer.
This commit is contained in:
114
packages/backend/src/services/lookup.service.ts
Normal file
114
packages/backend/src/services/lookup.service.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { eq, and } from 'drizzle-orm'
|
||||
import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'
|
||||
import {
|
||||
inventoryUnitStatuses,
|
||||
itemConditions,
|
||||
SYSTEM_UNIT_STATUSES,
|
||||
SYSTEM_ITEM_CONDITIONS,
|
||||
} from '../db/schema/lookups.js'
|
||||
import type { LookupCreateInput, LookupUpdateInput } from '@forte/shared/schemas'
|
||||
|
||||
function createLookupService(
|
||||
table: typeof inventoryUnitStatuses | typeof itemConditions,
|
||||
systemSeeds: ReadonlyArray<{ slug: string; name: string; description: string; sortOrder: number }>,
|
||||
) {
|
||||
return {
|
||||
async seedForCompany(db: PostgresJsDatabase, companyId: string) {
|
||||
const existing = await db
|
||||
.select()
|
||||
.from(table)
|
||||
.where(and(eq(table.companyId, companyId), eq(table.isSystem, true)))
|
||||
.limit(1)
|
||||
|
||||
if (existing.length > 0) return // already seeded
|
||||
|
||||
await db.insert(table).values(
|
||||
systemSeeds.map((seed) => ({
|
||||
companyId,
|
||||
...seed,
|
||||
isSystem: true,
|
||||
})),
|
||||
)
|
||||
},
|
||||
|
||||
async list(db: PostgresJsDatabase, companyId: string) {
|
||||
return db
|
||||
.select()
|
||||
.from(table)
|
||||
.where(and(eq(table.companyId, companyId), eq(table.isActive, true)))
|
||||
.orderBy(table.sortOrder)
|
||||
},
|
||||
|
||||
async getBySlug(db: PostgresJsDatabase, companyId: string, slug: string) {
|
||||
const [row] = await db
|
||||
.select()
|
||||
.from(table)
|
||||
.where(and(eq(table.companyId, companyId), eq(table.slug, slug)))
|
||||
.limit(1)
|
||||
return row ?? null
|
||||
},
|
||||
|
||||
async create(db: PostgresJsDatabase, companyId: string, input: LookupCreateInput) {
|
||||
const [row] = await db
|
||||
.insert(table)
|
||||
.values({
|
||||
companyId,
|
||||
name: input.name,
|
||||
slug: input.slug,
|
||||
description: input.description,
|
||||
sortOrder: input.sortOrder,
|
||||
isSystem: false,
|
||||
})
|
||||
.returning()
|
||||
return row
|
||||
},
|
||||
|
||||
async update(db: PostgresJsDatabase, companyId: string, id: string, input: LookupUpdateInput) {
|
||||
// Prevent modifying system rows' slug or system flag
|
||||
const existing = await db
|
||||
.select()
|
||||
.from(table)
|
||||
.where(and(eq(table.id, id), eq(table.companyId, companyId)))
|
||||
.limit(1)
|
||||
|
||||
if (!existing[0]) return null
|
||||
if (existing[0].isSystem && input.isActive === false) {
|
||||
throw new Error('Cannot deactivate a system status')
|
||||
}
|
||||
|
||||
const [row] = await db
|
||||
.update(table)
|
||||
.set(input)
|
||||
.where(and(eq(table.id, id), eq(table.companyId, companyId)))
|
||||
.returning()
|
||||
return row ?? null
|
||||
},
|
||||
|
||||
async delete(db: PostgresJsDatabase, companyId: string, id: string) {
|
||||
const existing = await db
|
||||
.select()
|
||||
.from(table)
|
||||
.where(and(eq(table.id, id), eq(table.companyId, companyId)))
|
||||
.limit(1)
|
||||
|
||||
if (!existing[0]) return null
|
||||
if (existing[0].isSystem) {
|
||||
throw new Error('Cannot delete a system status')
|
||||
}
|
||||
|
||||
const [row] = await db
|
||||
.delete(table)
|
||||
.where(and(eq(table.id, id), eq(table.companyId, companyId)))
|
||||
.returning()
|
||||
return row ?? null
|
||||
},
|
||||
|
||||
async validateSlug(db: PostgresJsDatabase, companyId: string, slug: string): Promise<boolean> {
|
||||
const row = await this.getBySlug(db, companyId, slug)
|
||||
return row !== null && row.isActive
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const UnitStatusService = createLookupService(inventoryUnitStatuses, SYSTEM_UNIT_STATUSES)
|
||||
export const ItemConditionService = createLookupService(itemConditions, SYSTEM_ITEM_CONDITIONS)
|
||||
Reference in New Issue
Block a user