customer registration api integration

This commit is contained in:
yaschalew
2026-05-21 02:21:32 +03:00
parent fea308a6af
commit c7347b88d7
39 changed files with 2273 additions and 910 deletions

View File

@@ -1,15 +1,20 @@
import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Param,
ParseUUIDPipe,
Patch,
Post,
} from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger";
import { CustomersService } from "./customers.service";
import { CreateCustomerDto } from "./dto/create-customer.dto";
import { UpdateCustomerDto } from "./dto/update-customer.dto";
@ApiTags("customers")
@Controller("customers")
@@ -33,4 +38,20 @@ export class CustomersController {
findOne(@Param("id", ParseUUIDPipe) id: string) {
return this.customersService.findById(id);
}
@Patch(":id")
@ApiOperation({ summary: "Update a customer" })
update(
@Param("id", ParseUUIDPipe) id: string,
@Body() dto: UpdateCustomerDto,
) {
return this.customersService.update(id, dto);
}
@Delete(":id")
@ApiOperation({ summary: "Soft-delete a customer" })
@HttpCode(HttpStatus.NO_CONTENT)
remove(@Param("id", ParseUUIDPipe) id: string) {
return this.customersService.remove(id);
}
}

View File

@@ -1,24 +1,32 @@
import { Injectable, NotFoundException } from "@nestjs/common";
import {
ConflictException,
Injectable,
NotFoundException,
} from "@nestjs/common";
import { CustomersRepository } from "./customers.repository";
import { CreateCustomerDto } from "./dto/create-customer.dto";
import { UpdateCustomerDto } from "./dto/update-customer.dto";
import { Customer } from "./entities/customer.entity";
@Injectable()
export class CustomersService {
constructor(private readonly customersRepository: CustomersRepository) {}
/** Create a new freight customer. */
create(dto: CreateCustomerDto): Promise<Customer> {
async create(dto: CreateCustomerDto): Promise<Customer> {
const existing = await this.customersRepository.findByEmail(dto.email);
if (existing) {
throw new ConflictException(
`Customer with email "${dto.email}" already exists`,
);
}
return this.customersRepository.create(dto);
}
/** List every customer (alphabetical). */
findAll(): Promise<Customer[]> {
return this.customersRepository.findAll({ order: { name: "ASC" } });
}
/** Get a single customer by ID. */
async findById(id: string): Promise<Customer> {
const customer = await this.customersRepository.findById(id);
if (!customer) {
@@ -26,4 +34,28 @@ export class CustomersService {
}
return customer;
}
async update(id: string, dto: UpdateCustomerDto): Promise<Customer> {
await this.findById(id);
if (dto.email) {
const conflict = await this.customersRepository.findByEmail(dto.email);
if (conflict && conflict.id !== id) {
throw new ConflictException(
`Customer with email "${dto.email}" already exists`,
);
}
}
const updated = await this.customersRepository.update(id, dto);
if (!updated) {
throw new NotFoundException(`Customer ${id} not found`);
}
return updated;
}
async remove(id: string): Promise<void> {
await this.findById(id);
await this.customersRepository.softDelete(id);
}
}

View File

@@ -1,20 +1,73 @@
import { IsEmail, IsOptional, IsString } from "class-validator";
import {
IsEmail,
IsEnum,
IsOptional,
IsString,
MaxLength,
} from "class-validator";
export enum CustomerStatusDto {
Active = "Active",
Pending = "Pending",
Inactive = "Inactive",
}
export enum CustomerTypeDto {
Importer = "Importer",
Exporter = "Exporter",
Supplier = "Supplier",
}
export class CreateCustomerDto {
@IsString()
@MaxLength(256)
name!: string;
@IsEmail()
email!: string;
@IsString()
@MaxLength(32)
phone!: string;
@IsOptional()
@IsString()
@MaxLength(256)
company?: string;
@IsOptional()
@IsEnum(CustomerTypeDto)
customerType?: CustomerTypeDto;
@IsOptional()
@IsEnum(CustomerStatusDto)
status?: CustomerStatusDto;
@IsOptional()
@IsString()
@MaxLength(64)
tinNumber?: string;
@IsOptional()
@IsString()
@MaxLength(128)
city?: string;
@IsOptional()
@IsString()
@MaxLength(128)
country?: string;
@IsOptional()
@IsString()
address?: string;
@IsOptional()
@IsString()
@MaxLength(64)
taxId?: string;
@IsOptional()
@IsString()
notes?: string;
}

View File

@@ -0,0 +1,5 @@
import { PartialType } from "@nestjs/swagger";
import { CreateCustomerDto } from "./create-customer.dto";
export class UpdateCustomerDto extends PartialType(CreateCustomerDto) {}

View File

@@ -1,6 +1,9 @@
import { BaseEntity } from "@edr/api-common";
import { Column, Entity } from "typeorm";
export type CustomerStatus = "Active" | "Pending" | "Inactive";
export type CustomerType = "Importer" | "Exporter" | "Supplier";
@Entity({ name: "customers" })
export class Customer extends BaseEntity {
@Column({ name: "name", type: "varchar", length: 256 })
@@ -12,9 +15,40 @@ export class Customer extends BaseEntity {
@Column({ name: "phone", type: "varchar", length: 32 })
phone!: string;
@Column({ name: "company", type: "varchar", length: 256, nullable: true })
company?: string | null;
@Column({
name: "customer_type",
type: "varchar",
length: 32,
default: "Importer",
})
customerType!: CustomerType;
@Column({
name: "status",
type: "varchar",
length: 32,
default: "Active",
})
status!: CustomerStatus;
@Column({ name: "tin_number", type: "varchar", length: 64, nullable: true })
tinNumber?: string | null;
@Column({ name: "city", type: "varchar", length: 128, nullable: true })
city?: string | null;
@Column({ name: "country", type: "varchar", length: 128, nullable: true })
country?: string | null;
@Column({ name: "address", type: "text", nullable: true })
address?: string | null;
@Column({ name: "tax_id", type: "varchar", length: 64, nullable: true })
taxId?: string | null;
@Column({ name: "notes", type: "text", nullable: true })
notes?: string | null;
}