invoice
This commit is contained in:
@@ -703,6 +703,16 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"length": 6,
|
"length": 6,
|
||||||
"mappedType": "datetime"
|
"mappedType": "datetime"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"length": 255,
|
||||||
|
"mappedType": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"name": "order_prints",
|
"name": "order_prints",
|
||||||
@@ -2063,24 +2073,6 @@
|
|||||||
"nullable": false,
|
"nullable": false,
|
||||||
"length": 26,
|
"length": 26,
|
||||||
"mappedType": "character"
|
"mappedType": "character"
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"name": "description",
|
|
||||||
"type": "text",
|
|
||||||
"unsigned": false,
|
|
||||||
"autoincrement": false,
|
|
||||||
"primary": false,
|
|
||||||
"nullable": true,
|
|
||||||
"mappedType": "text"
|
|
||||||
},
|
|
||||||
"attachments": {
|
|
||||||
"name": "attachments",
|
|
||||||
"type": "jsonb",
|
|
||||||
"unsigned": false,
|
|
||||||
"autoincrement": false,
|
|
||||||
"primary": false,
|
|
||||||
"nullable": true,
|
|
||||||
"mappedType": "json"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"name": "request_items",
|
"name": "request_items",
|
||||||
@@ -2509,8 +2501,8 @@
|
|||||||
"nullable": false,
|
"nullable": false,
|
||||||
"mappedType": "integer"
|
"mappedType": "integer"
|
||||||
},
|
},
|
||||||
"confirm_status": {
|
"status": {
|
||||||
"name": "confirm_status",
|
"name": "status",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"unsigned": false,
|
"unsigned": false,
|
||||||
"autoincrement": false,
|
"autoincrement": false,
|
||||||
@@ -2520,7 +2512,8 @@
|
|||||||
"enumItems": [
|
"enumItems": [
|
||||||
"pending",
|
"pending",
|
||||||
"partially_confirmed",
|
"partially_confirmed",
|
||||||
"confirmed"
|
"confirmed",
|
||||||
|
"archived"
|
||||||
],
|
],
|
||||||
"mappedType": "enum"
|
"mappedType": "enum"
|
||||||
},
|
},
|
||||||
@@ -3213,6 +3206,16 @@
|
|||||||
"name": "orders",
|
"name": "orders",
|
||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
|
{
|
||||||
|
"columnNames": [
|
||||||
|
"invoice_item_id"
|
||||||
|
],
|
||||||
|
"composite": false,
|
||||||
|
"keyName": "orders_invoice_item_id_unique",
|
||||||
|
"constraint": true,
|
||||||
|
"primary": false,
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"print_id"
|
"print_id"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class Migration20260626140000 extends Migration {
|
|||||||
v_total int;
|
v_total int;
|
||||||
v_paid int;
|
v_paid int;
|
||||||
v_enable_tax boolean;
|
v_enable_tax boolean;
|
||||||
v_confirm_status text;
|
v_status text;
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
v_invoice_id := COALESCE(NEW.invoice_id, OLD.invoice_id);
|
v_invoice_id := COALESCE(NEW.invoice_id, OLD.invoice_id);
|
||||||
@@ -68,7 +68,7 @@ export class Migration20260626140000 extends Migration {
|
|||||||
) THEN 'confirmed'
|
) THEN 'confirmed'
|
||||||
ELSE 'partially_confirmed'
|
ELSE 'partially_confirmed'
|
||||||
END
|
END
|
||||||
INTO v_confirm_status;
|
INTO v_status;
|
||||||
|
|
||||||
UPDATE invoices
|
UPDATE invoices
|
||||||
SET
|
SET
|
||||||
@@ -78,8 +78,9 @@ export class Migration20260626140000 extends Migration {
|
|||||||
total = v_total,
|
total = v_total,
|
||||||
paid_amount = v_paid,
|
paid_amount = v_paid,
|
||||||
balance = v_total - v_paid,
|
balance = v_total - v_paid,
|
||||||
confirm_status = v_confirm_status
|
status = v_status
|
||||||
WHERE id = v_invoice_id;
|
WHERE id = v_invoice_id
|
||||||
|
AND status <> 'archived';
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END;
|
END;
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../negareh-console"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../negareh-admin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
@@ -233,7 +233,7 @@ export class AdminService {
|
|||||||
customersCount,
|
customersCount,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.em.count(Request),
|
this.em.count(Request),
|
||||||
this.em.count(Invoice, { confirmStatus: InvoiceConfirmStatusEnum.PENDING }),
|
this.em.count(Invoice, { status: InvoiceConfirmStatusEnum.PENDING }),
|
||||||
this.em.count(Order, {
|
this.em.count(Order, {
|
||||||
status: { $nin: [OrderStatusEnum.FINISHED, OrderStatusEnum.CANCELED] },
|
status: { $nin: [OrderStatusEnum.FINISHED, OrderStatusEnum.CANCELED] },
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
IsDateString,
|
IsDateString,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsEnum,
|
IsEnum,
|
||||||
|
IsArray,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { InvoiceConfirmStatusEnum } from '../enum/invoice-confirm-status.enum';
|
import { InvoiceConfirmStatusEnum } from '../enum/invoice-confirm-status.enum';
|
||||||
import { Type, Transform } from 'class-transformer';
|
import { Type, Transform } from 'class-transformer';
|
||||||
@@ -68,12 +69,21 @@ export class FindInvoicesDto {
|
|||||||
enableTax?: boolean;
|
enableTax?: boolean;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
|
description: 'Filter by invoice statuses',
|
||||||
enum: InvoiceConfirmStatusEnum,
|
enum: InvoiceConfirmStatusEnum,
|
||||||
description: 'Filter by invoice confirmation status',
|
isArray: true,
|
||||||
})
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsEnum(InvoiceConfirmStatusEnum)
|
@Transform(({ value }: { value: unknown }) =>
|
||||||
confirmStatus?: InvoiceConfirmStatusEnum;
|
Array.isArray(value)
|
||||||
|
? value
|
||||||
|
: typeof value === 'string'
|
||||||
|
? value.split(',')
|
||||||
|
: value,
|
||||||
|
)
|
||||||
|
@IsArray()
|
||||||
|
@IsEnum(InvoiceConfirmStatusEnum, { each: true })
|
||||||
|
statuses?: InvoiceConfirmStatusEnum[];
|
||||||
|
|
||||||
@ApiPropertyOptional({ default: 'createdAt' })
|
@ApiPropertyOptional({ default: 'createdAt' })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { IAttachment } from 'src/modules/order/interface/order.interface';
|
|||||||
@Entity({ tableName: 'invoices' })
|
@Entity({ tableName: 'invoices' })
|
||||||
@Index({ properties: ['user'] })
|
@Index({ properties: ['user'] })
|
||||||
export class Invoice extends BaseEntity {
|
export class Invoice extends BaseEntity {
|
||||||
[OptionalProps]?: 'createdAt' | 'deletedAt' | 'invoiceNumber' | 'confirmStatus'
|
[OptionalProps]?: 'createdAt' | 'deletedAt' | 'invoiceNumber' | 'status'
|
||||||
|
|
||||||
@PrimaryKey({ type: 'string', columnType: 'char(26)' })
|
@PrimaryKey({ type: 'string', columnType: 'char(26)' })
|
||||||
id: string = ulid()
|
id: string = ulid()
|
||||||
@@ -80,7 +80,7 @@ export class Invoice extends BaseEntity {
|
|||||||
|
|
||||||
|
|
||||||
@Enum(() => InvoiceConfirmStatusEnum)
|
@Enum(() => InvoiceConfirmStatusEnum)
|
||||||
confirmStatus: InvoiceConfirmStatusEnum = InvoiceConfirmStatusEnum.PENDING;
|
status: InvoiceConfirmStatusEnum = InvoiceConfirmStatusEnum.PENDING;
|
||||||
|
|
||||||
@Property({ type: 'boolean', default: false })
|
@Property({ type: 'boolean', default: false })
|
||||||
enableTax?: boolean = false
|
enableTax?: boolean = false
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ export enum InvoiceConfirmStatusEnum {
|
|||||||
PENDING = 'pending',
|
PENDING = 'pending',
|
||||||
PARTIALLY_CONFIRMED = 'partially_confirmed',
|
PARTIALLY_CONFIRMED = 'partially_confirmed',
|
||||||
CONFIRMED = 'confirmed',
|
CONFIRMED = 'confirmed',
|
||||||
|
ARCHIVED = 'archived',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
export enum InvoiceStatusEnum {
|
|
||||||
PENDING = 'pending',
|
|
||||||
PAID = 'paid',
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -80,6 +80,14 @@ export class InvoiceController {
|
|||||||
return this.invoiceService.update(id, updateInvoiceDto);
|
return this.invoiceService.update(id, updateInvoiceDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Patch('admin/invoice/:id/archive')
|
||||||
|
@UseGuards(AdminAuthGuard)
|
||||||
|
@Permissions(PermissionEnum.UPDATE_INVOICE)
|
||||||
|
@ApiOperation({ summary: 'Archive an invoice (admin)' })
|
||||||
|
archive(@Param('id') id: string) {
|
||||||
|
return this.invoiceService.archive(id);
|
||||||
|
}
|
||||||
|
|
||||||
@Delete('admin/invoice/:id')
|
@Delete('admin/invoice/:id')
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
@Permissions(PermissionEnum.DELETE_INVOICE)
|
@Permissions(PermissionEnum.DELETE_INVOICE)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { ProductService } from '../product/providers/product.service';
|
|||||||
import { RequestService } from '../request/request.service';
|
import { RequestService } from '../request/request.service';
|
||||||
import { InvoiceRepository } from './repositories/invoice.repository';
|
import { InvoiceRepository } from './repositories/invoice.repository';
|
||||||
import { InvoiceCreatedEvent } from './events/invoice.events';
|
import { InvoiceCreatedEvent } from './events/invoice.events';
|
||||||
|
import { InvoiceConfirmStatusEnum } from './enum/invoice-confirm-status.enum';
|
||||||
const TAX_RATE = 0.1;
|
const TAX_RATE = 0.1;
|
||||||
|
|
||||||
type ResolvedItemDiscount = {
|
type ResolvedItemDiscount = {
|
||||||
@@ -336,6 +337,16 @@ export class InvoiceService {
|
|||||||
return invoice;
|
return invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async archive(id: string) {
|
||||||
|
const invoice = await this.findOneOrFail(id);
|
||||||
|
if (invoice.status === InvoiceConfirmStatusEnum.ARCHIVED) {
|
||||||
|
throw new BadRequestException('Invoice is already archived');
|
||||||
|
}
|
||||||
|
invoice.status = InvoiceConfirmStatusEnum.ARCHIVED;
|
||||||
|
await this.em.flush();
|
||||||
|
return invoice;
|
||||||
|
}
|
||||||
|
|
||||||
async remove(id: string) {
|
async remove(id: string) {
|
||||||
const invoice = await this.findOneOrFail(id);
|
const invoice = await this.findOneOrFail(id);
|
||||||
this.em.remove(invoice);
|
this.em.remove(invoice);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { FilterQuery } from '@mikro-orm/core';
|
|||||||
import { Invoice } from '../entities/invoice.entity';
|
import { Invoice } from '../entities/invoice.entity';
|
||||||
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||||
import { FindInvoicesDto } from '../dto/find-invoices.dto';
|
import { FindInvoicesDto } from '../dto/find-invoices.dto';
|
||||||
|
import { InvoiceConfirmStatusEnum } from '../enum/invoice-confirm-status.enum';
|
||||||
|
|
||||||
interface FindInvoicesOpts extends FindInvoicesDto {
|
interface FindInvoicesOpts extends FindInvoicesDto {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
@@ -27,7 +28,7 @@ export class InvoiceRepository extends EntityRepository<Invoice> {
|
|||||||
to,
|
to,
|
||||||
enableTax,
|
enableTax,
|
||||||
requestId,
|
requestId,
|
||||||
confirmStatus,
|
statuses,
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
const offset = (page - 1) * limit;
|
const offset = (page - 1) * limit;
|
||||||
@@ -46,8 +47,17 @@ export class InvoiceRepository extends EntityRepository<Invoice> {
|
|||||||
where.enableTax = enableTax;
|
where.enableTax = enableTax;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (confirmStatus) {
|
if (statuses) {
|
||||||
where.confirmStatus = confirmStatus;
|
const normalizedStatuses = Array.isArray(statuses) ? statuses : [statuses];
|
||||||
|
const validStatuses = normalizedStatuses.filter(
|
||||||
|
(s): s is InvoiceConfirmStatusEnum => !!s,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (validStatuses.length === 1) {
|
||||||
|
where.status = validStatuses[0];
|
||||||
|
} else if (validStatuses.length > 1) {
|
||||||
|
where.status = { $in: validStatuses };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (from || to) {
|
if (from || to) {
|
||||||
|
|||||||
Reference in New Issue
Block a user