invoice
This commit is contained in:
@@ -1,26 +1,152 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||
import { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { CreateInvoiceDto } from './dto/create-invoice.dto';
|
||||
import { UpdateInvoiceDto } from './dto/update-invoice.dto';
|
||||
import { FindInvoicesDto } from './dto/find-invoices.dto';
|
||||
import { Invoice } from './entities/invoice.entity';
|
||||
import { InvoiceItem } from './entities/invoice-item.entity';
|
||||
import { Request } from '../request/entities/request.entity';
|
||||
import { ProductService } from '../product/providers/product.service';
|
||||
import { InvoiceRepository } from './repositories/invoice.repository';
|
||||
|
||||
const TAX_RATE = 0.09;
|
||||
|
||||
@Injectable()
|
||||
export class InvoiceService {
|
||||
create(createInvoiceDto: CreateInvoiceDto) {
|
||||
return 'This action adds a new invoice';
|
||||
constructor(
|
||||
private readonly em: EntityManager,
|
||||
private readonly productService: ProductService,
|
||||
private readonly invoiceRepository: InvoiceRepository,
|
||||
) { }
|
||||
|
||||
|
||||
async create(dto: CreateInvoiceDto) {
|
||||
const request = await this.em.findOne(
|
||||
Request,
|
||||
{ id: dto.requestId },
|
||||
{ populate: ['user'] },
|
||||
);
|
||||
if (!request) {
|
||||
throw new NotFoundException('Request not found');
|
||||
}
|
||||
|
||||
const enableTax = dto.enableTax ?? false;
|
||||
const approvalDeadline = dto.approvalDeadline
|
||||
? new Date(dto.approvalDeadline)
|
||||
: undefined;
|
||||
|
||||
const discount = dto.items.reduce((acc, item) => acc + (item.discount ?? 0), 0);
|
||||
|
||||
return this.em.transactional(async (em) => {
|
||||
const invoice = em.create(Invoice, {
|
||||
user: request.user,
|
||||
request,
|
||||
enableTax,
|
||||
approvalDeadline,
|
||||
discount,
|
||||
paidAmount: 0,
|
||||
balance: 0,
|
||||
});
|
||||
em.persist(invoice);
|
||||
|
||||
let subTotal = 0;
|
||||
|
||||
for (const item of dto.items) {
|
||||
const product = await this.productService.findOneOrFail(item.productId);
|
||||
const unitPrice = item.unitPrice;
|
||||
const itemDiscount = item.discount ?? 0;
|
||||
const itemTotal = Math.max(0, unitPrice * item.quantity - itemDiscount);
|
||||
subTotal += itemTotal;
|
||||
|
||||
const invoiceItem = em.create(InvoiceItem, {
|
||||
invoice,
|
||||
product,
|
||||
quantity: item.quantity,
|
||||
unitPrice: unitPrice,
|
||||
discount: itemDiscount || undefined,
|
||||
description: item.description,
|
||||
});
|
||||
invoice.items.add(invoiceItem);
|
||||
}
|
||||
|
||||
const taxAmount = enableTax
|
||||
? Math.round((subTotal - discount) * TAX_RATE)
|
||||
: 0;
|
||||
const total = Math.max(0, subTotal - discount + taxAmount);
|
||||
|
||||
invoice.subTotal = subTotal;
|
||||
invoice.taxAmount = taxAmount;
|
||||
invoice.total = total;
|
||||
invoice.balance = total - invoice.paidAmount;
|
||||
|
||||
await em.flush();
|
||||
return invoice;
|
||||
});
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all invoice`;
|
||||
findAll(userId: string, dto: FindInvoicesDto) {
|
||||
return this.invoiceRepository.findAllPaginated({ userId, ...dto });
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
return `This action returns a #${id} invoice`;
|
||||
findAllAsAdmin(dto: FindInvoicesDto) {
|
||||
return this.invoiceRepository.findAllPaginated(dto);
|
||||
}
|
||||
|
||||
update(id: number, updateInvoiceDto: UpdateInvoiceDto) {
|
||||
return `This action updates a #${id} invoice`;
|
||||
async findOne(id: string, userId: string) {
|
||||
const invoice = await this.em.findOne(
|
||||
Invoice,
|
||||
{ id, user: { id: userId } },
|
||||
{ populate: ['user', 'request', 'items', 'items.product'] },
|
||||
);
|
||||
if (!invoice) {
|
||||
throw new NotFoundException('Invoice not found');
|
||||
}
|
||||
return invoice;
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} invoice`;
|
||||
async confirmInvoiceItem(id: string, userId: string) {
|
||||
const item = await this.em.findOne(
|
||||
InvoiceItem,
|
||||
{ id, invoice: { user: { id: userId } } },
|
||||
{ populate: ['invoice', 'product'] },
|
||||
);
|
||||
if (!item) {
|
||||
throw new NotFoundException('Invoice item not found');
|
||||
}
|
||||
if(item.confirmedAt){
|
||||
throw new BadRequestException('Invoice item already confirmed');
|
||||
}
|
||||
item.confirmedAt = new Date();
|
||||
await this.em.persistAndFlush(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
async update(id: string, updateInvoiceDto: UpdateInvoiceDto) {
|
||||
const invoice = await this.findOneOrFail(id);
|
||||
if (updateInvoiceDto.enableTax !== undefined) invoice.enableTax = updateInvoiceDto.enableTax;
|
||||
if (updateInvoiceDto.approvalDeadline !== undefined) {
|
||||
invoice.approvalDeadline = new Date(updateInvoiceDto.approvalDeadline);
|
||||
}
|
||||
await this.em.flush();
|
||||
return invoice;
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
const invoice = await this.findOneOrFail(id);
|
||||
this.em.remove(invoice);
|
||||
await this.em.flush();
|
||||
return invoice;
|
||||
}
|
||||
|
||||
async findOneOrFail(id: string) {
|
||||
const invoice = await this.em.findOne(
|
||||
Invoice,
|
||||
{ id },
|
||||
{ populate: ['user', 'request', 'items', 'items.product'] },
|
||||
);
|
||||
if (!invoice) {
|
||||
throw new NotFoundException('Invoice not found');
|
||||
}
|
||||
return invoice;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user