installed typeorm and added initial merchant resource
This commit is contained in:
parent
746adcd2fd
commit
ccc6540ae8
15 changed files with 1972 additions and 321 deletions
1927
package-lock.json
generated
1927
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -24,8 +24,11 @@
|
||||||
"@nestjs/core": "^11.0.1",
|
"@nestjs/core": "^11.0.1",
|
||||||
"@nestjs/mapped-types": "*",
|
"@nestjs/mapped-types": "*",
|
||||||
"@nestjs/platform-express": "^11.0.1",
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
|
"@nestjs/typeorm": "^11.0.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1",
|
||||||
|
"sqlite3": "^5.1.7",
|
||||||
|
"typeorm": "^0.3.28"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.2.0",
|
"@eslint/eslintrc": "^3.2.0",
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { ExpensesService } from './services/expenses.service';
|
import { MerchantsModule } from './merchants/merchants.module';
|
||||||
import { ExpensesController } from './controllers/expenses/expenses.controller';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [MerchantsModule],
|
||||||
controllers: [AppController, ExpensesController],
|
controllers: [AppController],
|
||||||
providers: [ExpensesService]
|
providers: []
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import { PartialType } from '@nestjs/mapped-types';
|
|
||||||
|
|
||||||
export class CreateExpenseDto {
|
|
||||||
date: Date;
|
|
||||||
cents: number;
|
|
||||||
categoryId: string;
|
|
||||||
merchantId?: string;
|
|
||||||
subcategoryIds?: string[];
|
|
||||||
tagIds?: string[];
|
|
||||||
description?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UpdateExpenseDto extends PartialType(CreateExpenseDto) {}
|
|
||||||
|
|
||||||
export class GetExpenseDto {
|
|
||||||
id: string;
|
|
||||||
date: Date;
|
|
||||||
cents: number;
|
|
||||||
category: string;
|
|
||||||
merchant?: string;
|
|
||||||
subcategories?: string[];
|
|
||||||
tags?: string[];
|
|
||||||
description?: string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
export class Expense {
|
|
||||||
id: string;
|
|
||||||
date: Date;
|
|
||||||
cents: number;
|
|
||||||
categoryId: string;
|
|
||||||
merchantId?: string;
|
|
||||||
subcategoryIds: string[];
|
|
||||||
tagIds: string[];
|
|
||||||
description?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Category {
|
|
||||||
id: string;
|
|
||||||
category: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SubCategory {
|
|
||||||
id: string;
|
|
||||||
subcategory: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Merchant {
|
|
||||||
id: string;
|
|
||||||
merchant: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Tag {
|
|
||||||
id: string;
|
|
||||||
tag: string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
import { Controller, Get, Post, Body, Patch, Param, Delete, NotFoundException, HttpCode } from '@nestjs/common';
|
|
||||||
import { ExpensesService } from '../../services/expenses.service';
|
|
||||||
import { CreateExpenseDto, UpdateExpenseDto } from './expense.dto';
|
|
||||||
|
|
||||||
@Controller('expenses')
|
|
||||||
export class ExpensesController {
|
|
||||||
constructor(private readonly expensesService: ExpensesService) {}
|
|
||||||
|
|
||||||
@Post()
|
|
||||||
create(@Body() createExpenseDto: CreateExpenseDto) {
|
|
||||||
return this.expensesService.create(createExpenseDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get()
|
|
||||||
findAll() {
|
|
||||||
return this.expensesService.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get(':id')
|
|
||||||
findOne(@Param('id') id: string) {
|
|
||||||
const expense = this.expensesService.findOne(id);
|
|
||||||
if (!expense) {
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return expense;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Patch(':id')
|
|
||||||
update(@Param('id') id: string, @Body() updateExpenseDto: UpdateExpenseDto) {
|
|
||||||
const expense = this.expensesService.update(id, updateExpenseDto);
|
|
||||||
if (!expense) {
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return expense;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Delete(':id')
|
|
||||||
@HttpCode(204)
|
|
||||||
remove(@Param('id') id: string) {
|
|
||||||
return this.expensesService.remove(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
src/merchants/dto/create-merchant.dto.ts
Normal file
1
src/merchants/dto/create-merchant.dto.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export class CreateMerchantDto {}
|
||||||
4
src/merchants/dto/update-merchant.dto.ts
Normal file
4
src/merchants/dto/update-merchant.dto.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { PartialType } from '@nestjs/mapped-types';
|
||||||
|
import { CreateMerchantDto } from './create-merchant.dto';
|
||||||
|
|
||||||
|
export class UpdateMerchantDto extends PartialType(CreateMerchantDto) {}
|
||||||
1
src/merchants/entities/merchant.entity.ts
Normal file
1
src/merchants/entities/merchant.entity.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export class Merchant {}
|
||||||
20
src/merchants/merchants.controller.spec.ts
Normal file
20
src/merchants/merchants.controller.spec.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { MerchantsController } from './merchants.controller';
|
||||||
|
import { MerchantsService } from './merchants.service';
|
||||||
|
|
||||||
|
describe('MerchantsController', () => {
|
||||||
|
let controller: MerchantsController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [MerchantsController],
|
||||||
|
providers: [MerchantsService]
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<MerchantsController>(MerchantsController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
34
src/merchants/merchants.controller.ts
Normal file
34
src/merchants/merchants.controller.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||||
|
import { MerchantsService } from './merchants.service';
|
||||||
|
import { CreateMerchantDto } from './dto/create-merchant.dto';
|
||||||
|
import { UpdateMerchantDto } from './dto/update-merchant.dto';
|
||||||
|
|
||||||
|
@Controller('merchants')
|
||||||
|
export class MerchantsController {
|
||||||
|
constructor(private readonly merchantsService: MerchantsService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
create(@Body() createMerchantDto: CreateMerchantDto) {
|
||||||
|
return this.merchantsService.create(createMerchantDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
findAll() {
|
||||||
|
return this.merchantsService.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
findOne(@Param('id') id: string) {
|
||||||
|
return this.merchantsService.findOne(+id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
update(@Param('id') id: string, @Body() updateMerchantDto: UpdateMerchantDto) {
|
||||||
|
return this.merchantsService.update(+id, updateMerchantDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
remove(@Param('id') id: string) {
|
||||||
|
return this.merchantsService.remove(+id);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/merchants/merchants.module.ts
Normal file
9
src/merchants/merchants.module.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { MerchantsService } from './merchants.service';
|
||||||
|
import { MerchantsController } from './merchants.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [MerchantsController],
|
||||||
|
providers: [MerchantsService]
|
||||||
|
})
|
||||||
|
export class MerchantsModule {}
|
||||||
18
src/merchants/merchants.service.spec.ts
Normal file
18
src/merchants/merchants.service.spec.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { MerchantsService } from './merchants.service';
|
||||||
|
|
||||||
|
describe('MerchantsService', () => {
|
||||||
|
let service: MerchantsService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [MerchantsService]
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<MerchantsService>(MerchantsService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
26
src/merchants/merchants.service.ts
Normal file
26
src/merchants/merchants.service.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { CreateMerchantDto } from './dto/create-merchant.dto';
|
||||||
|
import { UpdateMerchantDto } from './dto/update-merchant.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MerchantsService {
|
||||||
|
create(createMerchantDto: CreateMerchantDto) {
|
||||||
|
return 'This action adds a new merchant';
|
||||||
|
}
|
||||||
|
|
||||||
|
findAll() {
|
||||||
|
return `This action returns all merchants`;
|
||||||
|
}
|
||||||
|
|
||||||
|
findOne(id: number) {
|
||||||
|
return `This action returns a #${id} merchant`;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: number, updateMerchantDto: UpdateMerchantDto) {
|
||||||
|
return `This action updates a #${id} merchant`;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(id: number) {
|
||||||
|
return `This action removes a #${id} merchant`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,141 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { Category, Expense, Merchant, SubCategory, Tag } from '../controllers/expenses/expense.entities';
|
|
||||||
import { CreateExpenseDto, GetExpenseDto, UpdateExpenseDto } from '../controllers/expenses/expense.dto';
|
|
||||||
import { randomUUID } from 'node:crypto';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ExpensesService {
|
|
||||||
private categories: Category[] = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
category: 'Category 1'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
private merchants: Merchant[] = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
merchant: 'Merchant 1'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
private subcategories: SubCategory[] = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
subcategory: 'Subcategory 1'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
subcategory: 'Subcategory 2'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
private tags: Tag[] = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
tag: 'Tag 1'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
tag: 'Tag 2'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
private expenses: Expense[] = [
|
|
||||||
{
|
|
||||||
id: '2aa94170-2c57-4c4f-a1e7-13544ba72917',
|
|
||||||
date: new Date('2025-12-01'),
|
|
||||||
cents: 15443,
|
|
||||||
categoryId: '1',
|
|
||||||
merchantId: '1',
|
|
||||||
subcategoryIds: ['1', '2'],
|
|
||||||
tagIds: ['1', '2'],
|
|
||||||
description: 'Full existing expense'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '0708e7f7-3a2a-4b93-81da-38954925ca78',
|
|
||||||
date: new Date('2025-12-02'),
|
|
||||||
cents: 8723,
|
|
||||||
categoryId: '1',
|
|
||||||
subcategoryIds: [],
|
|
||||||
tagIds: [],
|
|
||||||
description: 'Partial existing expense'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
public create(createExpenseDto: CreateExpenseDto) {
|
|
||||||
const expense = {
|
|
||||||
id: randomUUID(),
|
|
||||||
date: new Date(createExpenseDto.date),
|
|
||||||
cents: createExpenseDto.cents,
|
|
||||||
categoryId: createExpenseDto.categoryId,
|
|
||||||
merchantId: createExpenseDto.merchantId,
|
|
||||||
subcategoryIds: createExpenseDto.subcategoryIds ?? [],
|
|
||||||
tagIds: createExpenseDto.tagIds ?? [],
|
|
||||||
description: createExpenseDto.description
|
|
||||||
} as Expense;
|
|
||||||
this.expenses.push(expense);
|
|
||||||
|
|
||||||
return this.mapExpense(expense);
|
|
||||||
}
|
|
||||||
|
|
||||||
public findAll() {
|
|
||||||
return this.expenses.map((expense) => {
|
|
||||||
return this.mapExpense(expense);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public findOne(id: string) {
|
|
||||||
const expense = this.getExpense(id);
|
|
||||||
return expense ? this.mapExpense(expense) : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
public update(id: string, updateExpenseDto: UpdateExpenseDto) {
|
|
||||||
let index;
|
|
||||||
const expense = this.expenses.find((exp, idx) => {
|
|
||||||
if (exp.id === id) {
|
|
||||||
index = idx;
|
|
||||||
return exp;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (expense && index) {
|
|
||||||
this.expenses[index] = {
|
|
||||||
...expense,
|
|
||||||
...updateExpenseDto
|
|
||||||
};
|
|
||||||
|
|
||||||
return this.mapExpense(this.expenses[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
public remove(id: string) {
|
|
||||||
this.expenses = this.expenses.filter((expense) => expense.id !== id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getExpense(id: string) {
|
|
||||||
return this.expenses.find((expense) => expense.id === id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getSubcategories(ids: string[]) {
|
|
||||||
return this.subcategories.filter((sub) => ids.includes(sub.id)).map((s) => s.subcategory);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getTags(ids: string[]) {
|
|
||||||
return this.tags.filter((tag) => ids.includes(tag.id)).map((t) => t.tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
private mapExpense(expense: Expense) {
|
|
||||||
const category = this.categories.find((category) => category.id === expense.categoryId);
|
|
||||||
const merchant = this.merchants.find((merchant) => merchant.id === expense.merchantId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: expense.id,
|
|
||||||
date: expense.date,
|
|
||||||
cents: expense.cents,
|
|
||||||
category: category?.category,
|
|
||||||
merchant: merchant?.merchant,
|
|
||||||
subcategories: this.getSubcategories(expense.subcategoryIds),
|
|
||||||
tags: this.getTags(expense.tagIds),
|
|
||||||
description: expense.description
|
|
||||||
} as GetExpenseDto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue