Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { AssetCategoriesModule } from './asset-categories/asset-categories.modul
import { AssetCategory } from './asset-categories/asset-category.entity';
import { DepartmentsModule } from './departments/departments.module';
import { Department } from './departments/department.entity';
import { CompaniesModule } from './companies/companies.module';
import { Company } from './companies/entities/company.entity';
import { BranchesModule } from './branches/branches.module';
import { Branch } from './branches/entities/branch.entity';

@Module({
imports: [
Expand Down Expand Up @@ -38,13 +42,15 @@ import { Department } from './departments/department.entity';
username: configService.get('DB_USERNAME', 'postgres'),
password: configService.get('DB_PASSWORD', 'password'),
database: configService.get('DB_DATABASE', 'manage_assets'),
entities: [AssetCategory, Department],
entities: [AssetCategory, Department, Company, Branch],
synchronize: configService.get('NODE_ENV') !== 'production', // Only for development
}),
inject: [ConfigService],
}),
AssetCategoriesModule,
DepartmentsModule,
CompaniesModule,
BranchesModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
39 changes: 39 additions & 0 deletions backend/src/branches/branches.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Controller, Get, Post, Body, Patch, Param, Delete, ParseIntPipe } from '@nestjs/common';
import { BranchesService } from './branches.service';
import { CreateBranchDto } from './dto/create-branch.dto';
import { UpdateBranchDto } from './dto/update-branch.dto';

@Controller('branches')
export class BranchesController {
constructor(private readonly branchesService: BranchesService) {}

@Post()
create(@Body() createBranchDto: CreateBranchDto) {
return this.branchesService.create(createBranchDto);
}

@Get()
findAll() {
return this.branchesService.findAll();
}

@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.branchesService.findOne(id);
}

@Patch(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateBranchDto: UpdateBranchDto,
) {
return this.branchesService.update(id, updateBranchDto);
}

@Delete(':id')
remove(@Param('id', ParseIntPipe) id: number) {
return this.branchesService.remove(id);
}
}


16 changes: 16 additions & 0 deletions backend/src/branches/branches.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BranchesService } from './branches.service';
import { BranchesController } from './branches.controller';
import { Branch } from './entities/branch.entity';
import { Company } from '../companies/entities/company.entity';

@Module({
imports: [TypeOrmModule.forFeature([Branch, Company])],
controllers: [BranchesController],
providers: [BranchesService],
exports: [BranchesService],
})
export class BranchesModule {}


146 changes: 146 additions & 0 deletions backend/src/branches/branches.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BranchesService } from './branches.service';
import { Branch } from './entities/branch.entity';
import { CreateBranchDto } from './dto/create-branch.dto';
import { UpdateBranchDto } from './dto/update-branch.dto';

describe('BranchesService', () => {
let service: BranchesService;
let repository: Repository<Branch>;

const mockRepository = {
create: jest.fn(),
save: jest.fn(),
find: jest.fn(),
findOne: jest.fn(),
remove: jest.fn(),
};

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
BranchesService,
{
provide: getRepositoryToken(Branch),
useValue: mockRepository,
},
],
}).compile();

service = module.get<BranchesService>(BranchesService);
repository = module.get<Repository<Branch>>(getRepositoryToken(Branch));
jest.clearAllMocks();
});

it('should be defined', () => {
expect(service).toBeDefined();
expect(repository).toBeDefined();
});

describe('create', () => {
it('should create a new branch', async () => {
const dto: CreateBranchDto = {
name: 'Main Branch',
address: '123 Street',
companyId: 1,
};

const expected: Branch = {
id: 1,
...dto,
createdAt: new Date(),
updatedAt: new Date(),
} as Branch;

mockRepository.create.mockReturnValue(expected);
mockRepository.save.mockResolvedValue(expected);

const result = await service.create(dto);
expect(mockRepository.create).toHaveBeenCalledWith(dto);
expect(mockRepository.save).toHaveBeenCalledWith(expected);
expect(result).toEqual(expected);
});
});

describe('findAll', () => {
it('should return an array of branches', async () => {
const expected: Branch[] = [
{
id: 1,
name: 'Main Branch',
address: '123 Street',
companyId: 1,
createdAt: new Date(),
updatedAt: new Date(),
} as Branch,
];
mockRepository.find.mockResolvedValue(expected);
const result = await service.findAll();
expect(mockRepository.find).toHaveBeenCalled();
expect(result).toEqual(expected);
});
});

describe('findOne', () => {
it('should return a branch when found', async () => {
const expected: Branch = {
id: 1,
name: 'Main Branch',
address: '123 Street',
companyId: 1,
createdAt: new Date(),
updatedAt: new Date(),
} as Branch;
mockRepository.findOne.mockResolvedValue(expected);
const result = await service.findOne(1);
expect(mockRepository.findOne).toHaveBeenCalledWith({ where: { id: 1 } });
expect(result).toEqual(expected);
});

it('should throw when branch not found', async () => {
mockRepository.findOne.mockResolvedValue(undefined);
await expect(service.findOne(999)).rejects.toThrow('Branch 999 not found');
});
});

describe('update', () => {
it('should merge and save updates', async () => {
const existing: Branch = {
id: 1,
name: 'Main Branch',
address: '123 Street',
companyId: 1,
createdAt: new Date(),
updatedAt: new Date(),
} as Branch;
const updates: UpdateBranchDto = { address: '456 Ave' };
const saved = { ...existing, ...updates } as Branch;
mockRepository.findOne.mockResolvedValue(existing);
mockRepository.save.mockResolvedValue(saved);
const result = await service.update(1, updates);
expect(mockRepository.save).toHaveBeenCalledWith(saved);
expect(result).toEqual(saved);
});
});

describe('remove', () => {
it('should remove the branch', async () => {
const existing: Branch = {
id: 1,
name: 'Main Branch',
address: '123 Street',
companyId: 1,
createdAt: new Date(),
updatedAt: new Date(),
} as Branch;
mockRepository.findOne.mockResolvedValue(existing);
mockRepository.remove.mockResolvedValue(existing);
await service.remove(1);
expect(mockRepository.remove).toHaveBeenCalledWith(existing);
});
});
});


44 changes: 44 additions & 0 deletions backend/src/branches/branches.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Branch } from './entities/branch.entity';
import { CreateBranchDto } from './dto/create-branch.dto';
import { UpdateBranchDto } from './dto/update-branch.dto';

@Injectable()
export class BranchesService {
constructor(
@InjectRepository(Branch)
private readonly branchRepository: Repository<Branch>,
) {}

async create(createDto: CreateBranchDto): Promise<Branch> {
const branch = this.branchRepository.create(createDto);
return await this.branchRepository.save(branch);
}

async findAll(): Promise<Branch[]> {
return await this.branchRepository.find();
}

async findOne(id: number): Promise<Branch> {
const branch = await this.branchRepository.findOne({ where: { id } });
if (!branch) {
throw new NotFoundException(`Branch ${id} not found`);
}
return branch;
}

async update(id: number, updateDto: UpdateBranchDto): Promise<Branch> {
const branch = await this.findOne(id);
Object.assign(branch, updateDto);
return await this.branchRepository.save(branch);
}

async remove(id: number): Promise<void> {
const branch = await this.findOne(id);
await this.branchRepository.remove(branch);
}
}


19 changes: 19 additions & 0 deletions backend/src/branches/dto/create-branch.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IsInt, IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator';

export class CreateBranchDto {
@IsString()
@IsNotEmpty()
@MaxLength(255)
name: string;

@IsString()
@IsOptional()
@MaxLength(255)
address?: string;

@IsInt()
@IsNotEmpty()
companyId: number;
}


19 changes: 19 additions & 0 deletions backend/src/branches/dto/update-branch.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IsInt, IsOptional, IsString, MaxLength } from 'class-validator';

export class UpdateBranchDto {
@IsString()
@IsOptional()
@MaxLength(255)
name?: string;

@IsString()
@IsOptional()
@MaxLength(255)
address?: string;

@IsInt()
@IsOptional()
companyId?: number;
}


28 changes: 28 additions & 0 deletions backend/src/branches/entities/branch.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne } from 'typeorm';
import { Company } from '../../companies/entities/company.entity';

@Entity('branches')
export class Branch {
@PrimaryGeneratedColumn()
id: number;

@Column({ type: 'varchar', length: 255 })
name: string;

@Column({ type: 'varchar', length: 255, nullable: true })
address: string;

@Column({ type: 'int' })
companyId: number;

@ManyToOne(() => Company)
company: Company;

@CreateDateColumn()
createdAt: Date;

@UpdateDateColumn()
updatedAt: Date;
}


39 changes: 39 additions & 0 deletions backend/src/companies/companies.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Controller, Get, Post, Body, Patch, Param, Delete, ParseIntPipe } from '@nestjs/common';
import { CompaniesService } from './companies.service';
import { CreateCompanyDto } from './dto/create-company.dto';
import { UpdateCompanyDto } from './dto/update-company.dto';

@Controller('companies')
export class CompaniesController {
constructor(private readonly companiesService: CompaniesService) {}

@Post()
create(@Body() createCompanyDto: CreateCompanyDto) {
return this.companiesService.create(createCompanyDto);
}

@Get()
findAll() {
return this.companiesService.findAll();
}

@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.companiesService.findOne(id);
}

@Patch(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateCompanyDto: UpdateCompanyDto,
) {
return this.companiesService.update(id, updateCompanyDto);
}

@Delete(':id')
remove(@Param('id', ParseIntPipe) id: number) {
return this.companiesService.remove(id);
}
}


Loading