82 lines
2.7 KiB
TypeScript
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,
|
|
);
|
|
});
|
|
}
|
|
}
|