- number-поля теперь рендерятся как text + inputMode=numeric, чтобы браузер не округлял значения через input type=number - пробелы при вставке в number-поля удаляются - бэкенд нормализует значения number-полей в строку перед сохранением - добавлен хелпер normalizeFieldValueForStorage Closes: искажение расчётного счёта и других длинных числовых полей
110 lines
3.3 KiB
TypeScript
110 lines
3.3 KiB
TypeScript
import { userStatuses, users, type UserStatus, type InsertUserStatus } from "@shared/schema";
|
|
import { db } from "../db";
|
|
import { eq, and, asc, sql } from "drizzle-orm";
|
|
|
|
export class UserStatusesStorage {
|
|
async getById(id: number): Promise<UserStatus | undefined> {
|
|
const [status] = await db.select().from(userStatuses).where(eq(userStatuses.id, id));
|
|
return status || undefined;
|
|
}
|
|
|
|
async listByOrganization(organizationId: number): Promise<UserStatus[]> {
|
|
return await db
|
|
.select()
|
|
.from(userStatuses)
|
|
.where(eq(userStatuses.organizationId, organizationId))
|
|
.orderBy(asc(userStatuses.position), asc(userStatuses.name));
|
|
}
|
|
|
|
async getDefaultByOrganization(organizationId: number): Promise<UserStatus | undefined> {
|
|
const [status] = await db
|
|
.select()
|
|
.from(userStatuses)
|
|
.where(and(eq(userStatuses.organizationId, organizationId), eq(userStatuses.isDefault, true)))
|
|
.limit(1);
|
|
return status || undefined;
|
|
}
|
|
|
|
async create(data: InsertUserStatus): Promise<UserStatus> {
|
|
const { organizationId, isDefault } = data;
|
|
|
|
if (isDefault) {
|
|
await db
|
|
.update(userStatuses)
|
|
.set({ isDefault: false })
|
|
.where(eq(userStatuses.organizationId, organizationId));
|
|
}
|
|
|
|
const [status] = await db.insert(userStatuses).values(data).returning();
|
|
return status;
|
|
}
|
|
|
|
async update(id: number, organizationId: number, data: Partial<InsertUserStatus>): Promise<UserStatus> {
|
|
if (data.isDefault) {
|
|
await db
|
|
.update(userStatuses)
|
|
.set({ isDefault: false })
|
|
.where(eq(userStatuses.organizationId, organizationId));
|
|
}
|
|
|
|
const [status] = await db
|
|
.update(userStatuses)
|
|
.set(data)
|
|
.where(and(eq(userStatuses.id, id), eq(userStatuses.organizationId, organizationId)))
|
|
.returning();
|
|
return status;
|
|
}
|
|
|
|
async delete(id: number, organizationId: number): Promise<void> {
|
|
const status = await this.getById(id);
|
|
if (!status || status.organizationId !== organizationId) return;
|
|
|
|
const defaultStatus = await this.getDefaultByOrganization(organizationId);
|
|
|
|
await db.transaction(async (tx) => {
|
|
if (defaultStatus && defaultStatus.id !== id) {
|
|
await tx
|
|
.update(users)
|
|
.set({ statusId: defaultStatus.id })
|
|
.where(eq(users.statusId, id));
|
|
} else {
|
|
await tx
|
|
.update(users)
|
|
.set({ statusId: null })
|
|
.where(eq(users.statusId, id));
|
|
}
|
|
|
|
await tx
|
|
.delete(userStatuses)
|
|
.where(and(eq(userStatuses.id, id), eq(userStatuses.organizationId, organizationId)));
|
|
});
|
|
}
|
|
|
|
async ensureDefaultStatus(organizationId: number): Promise<UserStatus> {
|
|
const existing = await this.getDefaultByOrganization(organizationId);
|
|
if (existing) return existing;
|
|
|
|
const [status] = await db
|
|
.insert(userStatuses)
|
|
.values({
|
|
organizationId,
|
|
name: "Зарегистрирован",
|
|
color: "#e5e7eb",
|
|
isDefault: true,
|
|
position: 0,
|
|
})
|
|
.returning();
|
|
return status;
|
|
}
|
|
|
|
async getUsageCount(statusId: number): Promise<number> {
|
|
const [result] = await db
|
|
.select({ total: sql<number>`count(*)::int` })
|
|
.from(users)
|
|
.where(eq(users.statusId, statusId));
|
|
return result?.total || 0;
|
|
}
|
|
}
|
|
|
|
export const userStatusesStorage = new UserStatusesStorage();
|