manicanldes-backend/src/addresses/address.store.ts

82 lines
2.7 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { DatabaseService } from '../database/database.service';
import type { Prisma } from '../generated/prisma/client';
import { AccessStore } from '../identity/access.store';
import { recordAudit } from '../identity/audit';
import type { Principal } from '../identity/identity.types';
import { AppError } from '../common/errors/app-error';
import type { AddressInput } from './address.schema';
async function ensureDefault(tx: Prisma.TransactionClient, userId: string) {
if (await tx.address.count({ where: { userId, isDefault: true } })) return;
const first = await tx.address.findFirst({
where: { userId },
orderBy: [{ createdAt: 'asc' }, { id: 'asc' }],
});
if (first)
await tx.address.update({
where: { id: first.id },
data: { isDefault: true },
});
}
@Injectable()
export class AddressStore {
constructor(
private readonly db: DatabaseService,
private readonly access: AccessStore,
) {}
list(actor: Principal) {
return this.db.address.findMany({
where: { userId: actor.userId, organizationId: actor.organizationId },
orderBy: [{ isDefault: 'desc' }, { createdAt: 'asc' }, { id: 'asc' }],
take: 20,
});
}
save(actor: Principal, input: AddressInput, id?: string) {
return this.access.mutate(actor, null, async (tx) => {
const where = {
userId: actor.userId,
organizationId: actor.organizationId,
};
if (id && !(await tx.address.findFirst({ where: { ...where, id } })))
throw new AppError('ADDRESS_NOT_FOUND');
if (!id && (await tx.address.count({ where })) >= 20)
throw new AppError('ADDRESS_LIMIT');
if (input.isDefault)
await tx.address.updateMany({ where, data: { isDefault: false } });
const address = id
? await tx.address.update({ where: { id }, data: input })
: await tx.address.create({ data: { ...where, ...input } });
await ensureDefault(tx, actor.userId);
await recordAudit(
tx,
actor.organizationId,
actor.userId,
id ? 'address.updated' : 'address.created',
address.id,
);
return tx.address.findUniqueOrThrow({ where: { id: address.id } });
});
}
remove(actor: Principal, id: string) {
return this.access.mutate(actor, null, async (tx) => {
const removed = await tx.address.deleteMany({
where: {
id,
userId: actor.userId,
organizationId: actor.organizationId,
},
});
if (!removed.count) throw new AppError('ADDRESS_NOT_FOUND');
await ensureDefault(tx, actor.userId);
await recordAudit(
tx,
actor.organizationId,
actor.userId,
'address.deleted',
id,
);
});
}
}