121 lines
3.7 KiB
TypeScript
121 lines
3.7 KiB
TypeScript
import {
|
|||
|
|
BadRequestException,
|
||
|
|
Injectable,
|
||
|
|
NotFoundException,
|
||
|
|
} from '@nestjs/common';
|
||
|
|
import type { ChequeDto, UpdateChequeRequest } from '@mento/shared';
|
||
|
|
import { Prisma } from '@prisma/client';
|
||
|
|
import { PrismaService } from '../prisma/prisma.service';
|
||
|
|
import { type Actor, ownerWhere } from '../common/record-scope';
|
||
|
|
|
||
|
|
const CHEQUE_STATUSES = ['pending', 'cleared', 'bounced'] as const;
|
||
|
|
|
||
|
|
@Injectable()
|
||
|
|
export class ChequesService {
|
||
|
|
constructor(private readonly prisma: PrismaService) {}
|
||
|
|
|
||
|
|
async list(
|
||
|
|
organizationId: string,
|
||
|
|
actor: Pick<Actor, 'userId' | 'role'>,
|
||
|
|
opts?: { status?: string; q?: string },
|
||
|
|
): Promise<ChequeDto[]> {
|
||
|
|
const rows = await this.prisma.cheque.findMany({
|
||
|
|
where: {
|
||
|
|
organizationId,
|
||
|
|
invoice: ownerWhere(actor),
|
||
|
|
...(opts?.status ? { status: opts.status } : {}),
|
||
|
|
...(opts?.q?.trim()
|
||
|
|
? {
|
||
|
|
OR: [
|
||
|
|
{ number: { contains: opts.q.trim(), mode: 'insensitive' } },
|
||
|
|
{ bank: { contains: opts.q.trim(), mode: 'insensitive' } },
|
||
|
|
{
|
||
|
|
invoice: {
|
||
|
|
number: { contains: opts.q.trim(), mode: 'insensitive' },
|
||
|
|
},
|
||
|
|
},
|
||
|
|
],
|
||
|
|
}
|
||
|
|
: {}),
|
||
|
|
},
|
||
|
|
include: this.include,
|
||
|
|
orderBy: [{ dueDate: 'asc' }, { createdAt: 'desc' }],
|
||
|
|
take: 300,
|
||
|
|
});
|
||
|
|
return rows.map((row) => this.toDto(row));
|
||
|
|
}
|
||
|
|
|
||
|
|
async update(
|
||
|
|
organizationId: string,
|
||
|
|
id: string,
|
||
|
|
actor: Pick<Actor, 'userId' | 'role'>,
|
||
|
|
dto: UpdateChequeRequest,
|
||
|
|
): Promise<ChequeDto> {
|
||
|
|
const existing = await this.prisma.cheque.findFirst({
|
||
|
|
where: { id, organizationId, invoice: ownerWhere(actor) },
|
||
|
|
select: { id: true },
|
||
|
|
});
|
||
|
|
if (!existing) throw new NotFoundException('چک پیدا نشد');
|
||
|
|
|
||
|
|
const data: Prisma.ChequeUncheckedUpdateInput = {};
|
||
|
|
if (dto.number !== undefined) data.number = dto.number.trim();
|
||
|
|
if (dto.bank !== undefined) data.bank = dto.bank.trim();
|
||
|
|
if (dto.dueDate !== undefined) data.dueDate = new Date(dto.dueDate);
|
||
|
|
if (dto.status !== undefined) {
|
||
|
|
if (!CHEQUE_STATUSES.includes(dto.status as (typeof CHEQUE_STATUSES)[number])) {
|
||
|
|
throw new BadRequestException('وضعیت چک معتبر نیست');
|
||
|
|
}
|
||
|
|
data.status = dto.status;
|
||
|
|
}
|
||
|
|
if ('notes' in dto) {
|
||
|
|
data.notes = dto.notes?.trim() ? dto.notes.trim() : null;
|
||
|
|
}
|
||
|
|
|
||
|
|
const row = await this.prisma.cheque.update({
|
||
|
|
where: { id },
|
||
|
|
data,
|
||
|
|
include: this.include,
|
||
|
|
});
|
||
|
|
return this.toDto(row);
|
||
|
|
}
|
||
|
|
|
||
|
|
private include = {
|
||
|
|
payment: { select: { id: true, amount: true, currency: true, paidAt: true } },
|
||
|
|
invoice: {
|
||
|
|
select: {
|
||
|
|
id: true,
|
||
|
|
number: true,
|
||
|
|
title: true,
|
||
|
|
person: { select: { firstName: true, lastName: true } },
|
||
|
|
company: { select: { name: true } },
|
||
|
|
},
|
||
|
|
},
|
||
|
|
} satisfies Prisma.ChequeInclude;
|
||
|
|
|
||
|
|
private toDto(
|
||
|
|
row: Prisma.ChequeGetPayload<{ include: ChequesService['include'] }>,
|
||
|
|
): ChequeDto {
|
||
|
|
const personName = row.invoice.person
|
||
|
|
? `${row.invoice.person.firstName} ${row.invoice.person.lastName}`.trim()
|
||
|
|
: row.invoice.company?.name ?? null;
|
||
|
|
return {
|
||
|
|
id: row.id,
|
||
|
|
paymentId: row.paymentId,
|
||
|
|
invoiceId: row.invoiceId,
|
||
|
|
invoiceNumber: row.invoice.number,
|
||
|
|
invoiceTitle: row.invoice.title,
|
||
|
|
personName,
|
||
|
|
amount: Number(row.payment.amount),
|
||
|
|
currency: row.payment.currency,
|
||
|
|
number: row.number,
|
||
|
|
bank: row.bank,
|
||
|
|
dueDate: row.dueDate.toISOString(),
|
||
|
|
status: row.status,
|
||
|
|
notes: row.notes,
|
||
|
|
paidAt: row.payment.paidAt.toISOString(),
|
||
|
|
createdAt: row.createdAt.toISOString(),
|
||
|
|
updatedAt: row.updatedAt.toISOString(),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|