invoice update

This commit is contained in:
2026-02-19 11:00:06 +03:30
parent 928af266ce
commit d32367f37d
3 changed files with 99 additions and 3 deletions
@@ -0,0 +1,11 @@
import { PartialType } from '@nestjs/swagger';
import { IsOptional, IsString } from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger';
import { CreateInvoiceItemDto } from './create-invoice.dto';
export class UpdateInvoiceItemDto extends PartialType(CreateInvoiceItemDto) {
@IsOptional()
@IsString()
@ApiPropertyOptional({ description: 'Invoice item ID - when provided, updates existing item; when omitted, creates new item' })
id?: string;
}
+21 -2
View File
@@ -1,4 +1,23 @@
import { PartialType } from '@nestjs/swagger'; import { IntersectionType, OmitType, PartialType } from '@nestjs/swagger';
import { IsArray, IsOptional } from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { CreateInvoiceDto } from './create-invoice.dto'; import { CreateInvoiceDto } from './create-invoice.dto';
import { UpdateInvoiceItemDto } from './update-invoice-item.dto';
export class UpdateInvoiceDto extends PartialType(CreateInvoiceDto) {} class UpdateInvoiceBaseDto extends PartialType(
OmitType(CreateInvoiceDto, ['items']),
) {}
class UpdateInvoiceItemsDto {
@IsOptional()
@IsArray()
@Type(() => UpdateInvoiceItemDto)
@ApiPropertyOptional({ type: [UpdateInvoiceItemDto] })
items?: UpdateInvoiceItemDto[];
}
export class UpdateInvoiceDto extends IntersectionType(
UpdateInvoiceBaseDto,
UpdateInvoiceItemsDto,
) {}
+67 -1
View File
@@ -9,7 +9,7 @@ import { Request } from '../request/entities/request.entity';
import { ProductService } from '../product/providers/product.service'; import { ProductService } from '../product/providers/product.service';
import { InvoiceRepository } from './repositories/invoice.repository'; import { InvoiceRepository } from './repositories/invoice.repository';
const TAX_RATE = 0.09; const TAX_RATE = 0.1;
@Injectable() @Injectable()
export class InvoiceService { export class InvoiceService {
@@ -127,6 +127,72 @@ export class InvoiceService {
if (updateInvoiceDto.approvalDeadline !== undefined) { if (updateInvoiceDto.approvalDeadline !== undefined) {
invoice.approvalDeadline = new Date(updateInvoiceDto.approvalDeadline); invoice.approvalDeadline = new Date(updateInvoiceDto.approvalDeadline);
} }
if (updateInvoiceDto.items !== undefined && updateInvoiceDto.items.length > 0) {
for (const item of updateInvoiceDto.items) {
if (item.id) {
const existingItem = await this.em.findOne(InvoiceItem, {
id: item.id,
invoice: { id: invoice.id },
});
if (!existingItem) {
throw new NotFoundException(`Invoice item ${item.id} not found`);
}
if (item.productId !== undefined) {
existingItem.product = await this.productService.findOneOrFail(
item.productId,
);
}
if (item.quantity !== undefined) existingItem.quantity = item.quantity;
if (item.unitPrice !== undefined)
existingItem.unitPrice = item.unitPrice;
if (item.discount !== undefined) existingItem.discount = item.discount;
if (item.description !== undefined)
existingItem.description = item.description;
} else {
if (
item.productId === undefined ||
item.quantity === undefined ||
item.unitPrice === undefined
) {
throw new BadRequestException(
'New invoice items must have productId, quantity and unitPrice',
);
}
const product = await this.productService.findOneOrFail(item.productId);
const itemDiscount = item.discount ?? 0;
const invoiceItem = this.em.create(InvoiceItem, {
invoice,
product,
quantity: item.quantity,
unitPrice: item.unitPrice,
discount: itemDiscount || undefined,
description: item.description,
});
invoice.items.add(invoiceItem);
}
}
const allItems = invoice.items.getItems();
let subTotal = 0;
let discount = 0;
for (const item of allItems) {
const itemTotal = Math.max(
0,
item.unitPrice * item.quantity - (item.discount ?? 0),
);
subTotal += itemTotal;
discount += item.discount ?? 0;
}
invoice.discount = discount;
const taxAmount = invoice.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 this.em.flush(); await this.em.flush();
return invoice; return invoice;
} }