@hed-hog/finance 0.0.225 → 0.0.227
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dto/create-bank-account.dto.d.ts +9 -0
- package/dist/dto/create-bank-account.dto.d.ts.map +1 -0
- package/dist/dto/create-bank-account.dto.js +61 -0
- package/dist/dto/create-bank-account.dto.js.map +1 -0
- package/dist/dto/update-bank-account.dto.d.ts +9 -0
- package/dist/dto/update-bank-account.dto.d.ts.map +1 -0
- package/dist/dto/update-bank-account.dto.js +60 -0
- package/dist/dto/update-bank-account.dto.js.map +1 -0
- package/dist/finance-bank-accounts.controller.d.ts +47 -0
- package/dist/finance-bank-accounts.controller.d.ts.map +1 -0
- package/dist/finance-bank-accounts.controller.js +73 -0
- package/dist/finance-bank-accounts.controller.js.map +1 -0
- package/dist/finance-data.controller.d.ts +136 -0
- package/dist/finance-data.controller.d.ts.map +1 -0
- package/dist/finance-data.controller.js +36 -0
- package/dist/finance-data.controller.js.map +1 -0
- package/dist/finance-installments.controller.d.ts +149 -0
- package/dist/finance-installments.controller.d.ts.map +1 -0
- package/dist/finance-installments.controller.js +101 -0
- package/dist/finance-installments.controller.js.map +1 -0
- package/dist/finance.module.d.ts.map +1 -1
- package/dist/finance.module.js +8 -2
- package/dist/finance.module.js.map +1 -1
- package/dist/finance.service.d.ts +53 -5
- package/dist/finance.service.d.ts.map +1 -1
- package/dist/finance.service.js +167 -9
- package/dist/finance.service.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/hedhog/data/menu.yaml +5 -1
- package/hedhog/data/route.yaml +36 -0
- package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +4 -1
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +307 -112
- package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +25 -8
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +301 -100
- package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +378 -71
- package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +6 -3
- package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +4 -2
- package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +6 -7
- package/package.json +6 -6
- package/src/dto/create-bank-account.dto.ts +49 -0
- package/src/dto/update-bank-account.dto.ts +45 -0
- package/src/finance-bank-accounts.controller.ts +43 -0
- package/src/finance-data.controller.ts +14 -0
- package/src/{finance.controller.ts → finance-installments.controller.ts} +8 -13
- package/src/finance.module.ts +8 -2
- package/src/finance.service.ts +202 -9
- package/src/index.ts +3 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getLocaleText } from '@hed-hog/api-locale';
|
|
2
|
+
import { IsOptional, IsString } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
export class UpdateBankAccountDto {
|
|
5
|
+
@IsOptional()
|
|
6
|
+
@IsString({
|
|
7
|
+
message: (args) => getLocaleText('validation.bankMustBeString', args.value),
|
|
8
|
+
})
|
|
9
|
+
bank?: string;
|
|
10
|
+
|
|
11
|
+
@IsOptional()
|
|
12
|
+
@IsString({
|
|
13
|
+
message: (args) =>
|
|
14
|
+
getLocaleText('validation.branchMustBeString', args.value),
|
|
15
|
+
})
|
|
16
|
+
branch?: string;
|
|
17
|
+
|
|
18
|
+
@IsOptional()
|
|
19
|
+
@IsString({
|
|
20
|
+
message: (args) =>
|
|
21
|
+
getLocaleText('validation.accountMustBeString', args.value),
|
|
22
|
+
})
|
|
23
|
+
account?: string;
|
|
24
|
+
|
|
25
|
+
@IsOptional()
|
|
26
|
+
@IsString({
|
|
27
|
+
message: (args) =>
|
|
28
|
+
getLocaleText('validation.accountTypeMustBeString', args.value),
|
|
29
|
+
})
|
|
30
|
+
type?: string;
|
|
31
|
+
|
|
32
|
+
@IsOptional()
|
|
33
|
+
@IsString({
|
|
34
|
+
message: (args) =>
|
|
35
|
+
getLocaleText('validation.descriptionMustBeString', args.value),
|
|
36
|
+
})
|
|
37
|
+
description?: string;
|
|
38
|
+
|
|
39
|
+
@IsOptional()
|
|
40
|
+
@IsString({
|
|
41
|
+
message: (args) =>
|
|
42
|
+
getLocaleText('validation.statusMustBeString', args.value),
|
|
43
|
+
})
|
|
44
|
+
status?: 'active' | 'inactive';
|
|
45
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Role, User } from '@hed-hog/api';
|
|
2
|
+
import {
|
|
3
|
+
Body,
|
|
4
|
+
Controller,
|
|
5
|
+
Delete,
|
|
6
|
+
Get,
|
|
7
|
+
Param,
|
|
8
|
+
ParseIntPipe,
|
|
9
|
+
Patch,
|
|
10
|
+
Post,
|
|
11
|
+
} from '@nestjs/common';
|
|
12
|
+
import { CreateBankAccountDto } from './dto/create-bank-account.dto';
|
|
13
|
+
import { UpdateBankAccountDto } from './dto/update-bank-account.dto';
|
|
14
|
+
import { FinanceService } from './finance.service';
|
|
15
|
+
|
|
16
|
+
@Role()
|
|
17
|
+
@Controller('finance')
|
|
18
|
+
export class FinanceBankAccountsController {
|
|
19
|
+
constructor(private readonly financeService: FinanceService) {}
|
|
20
|
+
|
|
21
|
+
@Get('bank-accounts')
|
|
22
|
+
async listBankAccounts() {
|
|
23
|
+
return this.financeService.listBankAccounts();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Post('bank-accounts')
|
|
27
|
+
async createBankAccount(@Body() data: CreateBankAccountDto, @User() user) {
|
|
28
|
+
return this.financeService.createBankAccount(data, user?.id);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Patch('bank-accounts/:id')
|
|
32
|
+
async updateBankAccount(
|
|
33
|
+
@Param('id', ParseIntPipe) id: number,
|
|
34
|
+
@Body() data: UpdateBankAccountDto,
|
|
35
|
+
) {
|
|
36
|
+
return this.financeService.updateBankAccount(id, data);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Delete('bank-accounts/:id')
|
|
40
|
+
async deleteBankAccount(@Param('id', ParseIntPipe) id: number) {
|
|
41
|
+
return this.financeService.deleteBankAccount(id);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Role } from '@hed-hog/api';
|
|
2
|
+
import { Controller, Get } from '@nestjs/common';
|
|
3
|
+
import { FinanceService } from './finance.service';
|
|
4
|
+
|
|
5
|
+
@Role()
|
|
6
|
+
@Controller('finance')
|
|
7
|
+
export class FinanceDataController {
|
|
8
|
+
constructor(private readonly financeService: FinanceService) {}
|
|
9
|
+
|
|
10
|
+
@Get('data')
|
|
11
|
+
async getData() {
|
|
12
|
+
return this.financeService.getData();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -2,27 +2,22 @@ import { Role, User } from '@hed-hog/api';
|
|
|
2
2
|
import { Locale } from '@hed-hog/api-locale';
|
|
3
3
|
import { Pagination } from '@hed-hog/api-pagination';
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
Body,
|
|
6
|
+
Controller,
|
|
7
|
+
Get,
|
|
8
|
+
Param,
|
|
9
|
+
ParseIntPipe,
|
|
10
|
+
Post,
|
|
11
|
+
Query,
|
|
12
12
|
} from '@nestjs/common';
|
|
13
13
|
import { CreateFinancialTitleDto } from './dto/create-financial-title.dto';
|
|
14
14
|
import { FinanceService } from './finance.service';
|
|
15
15
|
|
|
16
16
|
@Role()
|
|
17
17
|
@Controller('finance')
|
|
18
|
-
export class
|
|
18
|
+
export class FinanceInstallmentsController {
|
|
19
19
|
constructor(private readonly financeService: FinanceService) {}
|
|
20
20
|
|
|
21
|
-
@Get('data')
|
|
22
|
-
async getData() {
|
|
23
|
-
return this.financeService.getData();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
21
|
@Get('accounts-payable/installments')
|
|
27
22
|
async listAccountsPayableInstallments(
|
|
28
23
|
@Pagination() paginationParams,
|
package/src/finance.module.ts
CHANGED
|
@@ -3,7 +3,9 @@ import { PaginationModule } from '@hed-hog/api-pagination';
|
|
|
3
3
|
import { PrismaModule } from '@hed-hog/api-prisma';
|
|
4
4
|
import { forwardRef, Module } from '@nestjs/common';
|
|
5
5
|
import { ConfigModule } from '@nestjs/config';
|
|
6
|
-
import {
|
|
6
|
+
import { FinanceBankAccountsController } from './finance-bank-accounts.controller';
|
|
7
|
+
import { FinanceDataController } from './finance-data.controller';
|
|
8
|
+
import { FinanceInstallmentsController } from './finance-installments.controller';
|
|
7
9
|
import { FinanceService } from './finance.service';
|
|
8
10
|
|
|
9
11
|
@Module({
|
|
@@ -13,7 +15,11 @@ import { FinanceService } from './finance.service';
|
|
|
13
15
|
forwardRef(() => PrismaModule),
|
|
14
16
|
forwardRef(() => LocaleModule)
|
|
15
17
|
],
|
|
16
|
-
controllers: [
|
|
18
|
+
controllers: [
|
|
19
|
+
FinanceDataController,
|
|
20
|
+
FinanceInstallmentsController,
|
|
21
|
+
FinanceBankAccountsController,
|
|
22
|
+
],
|
|
17
23
|
providers: [FinanceService],
|
|
18
24
|
exports: [FinanceService],
|
|
19
25
|
})
|
package/src/finance.service.ts
CHANGED
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
Injectable,
|
|
7
7
|
NotFoundException,
|
|
8
8
|
} from '@nestjs/common';
|
|
9
|
+
import { CreateBankAccountDto } from './dto/create-bank-account.dto';
|
|
9
10
|
import { CreateFinancialTitleDto } from './dto/create-financial-title.dto';
|
|
11
|
+
import { UpdateBankAccountDto } from './dto/update-bank-account.dto';
|
|
10
12
|
|
|
11
13
|
type TitleType = 'payable' | 'receivable';
|
|
12
14
|
|
|
@@ -109,6 +111,131 @@ export class FinanceService {
|
|
|
109
111
|
return this.createTitle(data, 'receivable', locale, userId);
|
|
110
112
|
}
|
|
111
113
|
|
|
114
|
+
async listBankAccounts() {
|
|
115
|
+
const bankAccounts = await this.prisma.bank_account.findMany({
|
|
116
|
+
include: {
|
|
117
|
+
bank_statement_line: {
|
|
118
|
+
select: {
|
|
119
|
+
amount_cents: true,
|
|
120
|
+
status: true,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
orderBy: [{ code: 'asc' }, { name: 'asc' }],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return bankAccounts.map((bankAccount) => this.mapBankAccountToFront(bankAccount));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async createBankAccount(data: CreateBankAccountDto, userId?: number) {
|
|
131
|
+
const accountType = this.mapAccountTypeFromPt(data.type);
|
|
132
|
+
|
|
133
|
+
const code = this.generateBankAccountCode(data.bank, data.account);
|
|
134
|
+
const name = data.description?.trim() || data.bank;
|
|
135
|
+
|
|
136
|
+
const createdAccount = await this.prisma.bank_account.create({
|
|
137
|
+
data: {
|
|
138
|
+
code,
|
|
139
|
+
name,
|
|
140
|
+
bank_name: data.bank,
|
|
141
|
+
agency: data.branch || null,
|
|
142
|
+
account_number: data.account || null,
|
|
143
|
+
account_type: accountType,
|
|
144
|
+
status: 'active',
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (data.initial_balance && data.initial_balance > 0) {
|
|
149
|
+
const statement = await this.prisma.bank_statement.create({
|
|
150
|
+
data: {
|
|
151
|
+
bank_account_id: createdAccount.id,
|
|
152
|
+
source_type: 'csv',
|
|
153
|
+
imported_at: new Date(),
|
|
154
|
+
imported_by_user_id: userId,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
await this.prisma.bank_statement_line.create({
|
|
159
|
+
data: {
|
|
160
|
+
bank_statement_id: statement.id,
|
|
161
|
+
bank_account_id: createdAccount.id,
|
|
162
|
+
posted_date: new Date(),
|
|
163
|
+
amount_cents: this.toCents(data.initial_balance),
|
|
164
|
+
description: 'Saldo inicial',
|
|
165
|
+
status: 'reconciled',
|
|
166
|
+
dedupe_key: `initial-balance-${createdAccount.id}-${Date.now()}`,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const account = await this.prisma.bank_account.findUnique({
|
|
172
|
+
where: { id: createdAccount.id },
|
|
173
|
+
include: {
|
|
174
|
+
bank_statement_line: {
|
|
175
|
+
select: {
|
|
176
|
+
amount_cents: true,
|
|
177
|
+
status: true,
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return this.mapBankAccountToFront(account);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async updateBankAccount(id: number, data: UpdateBankAccountDto) {
|
|
187
|
+
const current = await this.prisma.bank_account.findUnique({
|
|
188
|
+
where: { id },
|
|
189
|
+
select: { id: true },
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (!current) {
|
|
193
|
+
throw new NotFoundException('Bank account not found');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const updated = await this.prisma.bank_account.update({
|
|
197
|
+
where: { id },
|
|
198
|
+
data: {
|
|
199
|
+
bank_name: data.bank,
|
|
200
|
+
agency: data.branch,
|
|
201
|
+
account_number: data.account,
|
|
202
|
+
name: data.description,
|
|
203
|
+
account_type: data.type ? this.mapAccountTypeFromPt(data.type) : undefined,
|
|
204
|
+
status: data.status,
|
|
205
|
+
},
|
|
206
|
+
include: {
|
|
207
|
+
bank_statement_line: {
|
|
208
|
+
select: {
|
|
209
|
+
amount_cents: true,
|
|
210
|
+
status: true,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return this.mapBankAccountToFront(updated);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async deleteBankAccount(id: number) {
|
|
220
|
+
const current = await this.prisma.bank_account.findUnique({
|
|
221
|
+
where: { id },
|
|
222
|
+
select: { id: true },
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (!current) {
|
|
226
|
+
throw new NotFoundException('Bank account not found');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
await this.prisma.bank_account.update({
|
|
230
|
+
where: { id },
|
|
231
|
+
data: {
|
|
232
|
+
status: 'inactive',
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
return { success: true };
|
|
237
|
+
}
|
|
238
|
+
|
|
112
239
|
private async listTitles(
|
|
113
240
|
titleType: TitleType,
|
|
114
241
|
paginationParams: PaginationDTO,
|
|
@@ -326,18 +453,18 @@ export class FinanceService {
|
|
|
326
453
|
|
|
327
454
|
private async loadBankAccounts() {
|
|
328
455
|
const bankAccounts = await this.prisma.bank_account.findMany({
|
|
456
|
+
include: {
|
|
457
|
+
bank_statement_line: {
|
|
458
|
+
select: {
|
|
459
|
+
amount_cents: true,
|
|
460
|
+
status: true,
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
},
|
|
329
464
|
orderBy: [{ code: 'asc' }, { name: 'asc' }],
|
|
330
465
|
});
|
|
331
466
|
|
|
332
|
-
return bankAccounts.map((bankAccount) => (
|
|
333
|
-
id: String(bankAccount.id),
|
|
334
|
-
codigo: bankAccount.code,
|
|
335
|
-
descricao: bankAccount.name,
|
|
336
|
-
banco: bankAccount.bank_name,
|
|
337
|
-
agencia: bankAccount.agency,
|
|
338
|
-
conta: bankAccount.account_number,
|
|
339
|
-
ativo: bankAccount.status === 'active',
|
|
340
|
-
}));
|
|
467
|
+
return bankAccounts.map((bankAccount) => this.mapBankAccountToFront(bankAccount));
|
|
341
468
|
}
|
|
342
469
|
|
|
343
470
|
private async loadTags() {
|
|
@@ -519,6 +646,72 @@ export class FinanceService {
|
|
|
519
646
|
return paymentMethodMap[paymentMethodType] || undefined;
|
|
520
647
|
}
|
|
521
648
|
|
|
649
|
+
private mapAccountTypeToPt(accountType?: string | null) {
|
|
650
|
+
const accountTypeMap = {
|
|
651
|
+
checking: 'corrente',
|
|
652
|
+
savings: 'poupanca',
|
|
653
|
+
investment: 'investimento',
|
|
654
|
+
cash: 'caixa',
|
|
655
|
+
other: 'corrente',
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
return accountTypeMap[accountType] || 'corrente';
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
private mapAccountTypeFromPt(accountType?: string | null) {
|
|
662
|
+
const accountTypeMap = {
|
|
663
|
+
corrente: 'checking',
|
|
664
|
+
poupanca: 'savings',
|
|
665
|
+
investimento: 'investment',
|
|
666
|
+
caixa: 'cash',
|
|
667
|
+
other: 'other',
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
return accountTypeMap[accountType] || 'checking';
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
private mapBankAccountToFront(bankAccount: any) {
|
|
674
|
+
const currentCents = (bankAccount.bank_statement_line || []).reduce(
|
|
675
|
+
(acc, line) => acc + line.amount_cents,
|
|
676
|
+
0,
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
const reconciledCents = (bankAccount.bank_statement_line || []).reduce(
|
|
680
|
+
(acc, line) =>
|
|
681
|
+
line.status === 'reconciled' || line.status === 'adjusted'
|
|
682
|
+
? acc + line.amount_cents
|
|
683
|
+
: acc,
|
|
684
|
+
0,
|
|
685
|
+
);
|
|
686
|
+
|
|
687
|
+
return {
|
|
688
|
+
id: String(bankAccount.id),
|
|
689
|
+
codigo: bankAccount.code,
|
|
690
|
+
descricao: bankAccount.name,
|
|
691
|
+
banco: bankAccount.bank_name || bankAccount.name,
|
|
692
|
+
agencia: bankAccount.agency || '-',
|
|
693
|
+
conta: bankAccount.account_number || '-',
|
|
694
|
+
tipo: this.mapAccountTypeToPt(bankAccount.account_type),
|
|
695
|
+
saldoAtual: this.fromCents(currentCents),
|
|
696
|
+
saldoConciliado: this.fromCents(reconciledCents),
|
|
697
|
+
ativo: bankAccount.status === 'active',
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
private generateBankAccountCode(bankName?: string, accountNumber?: string) {
|
|
702
|
+
const bankPrefix = (bankName || 'ACC')
|
|
703
|
+
.replace(/[^A-Za-z0-9]/g, '')
|
|
704
|
+
.slice(0, 4)
|
|
705
|
+
.toUpperCase();
|
|
706
|
+
|
|
707
|
+
const accountSuffix = (accountNumber || '')
|
|
708
|
+
.replace(/\D/g, '')
|
|
709
|
+
.slice(-4)
|
|
710
|
+
.padStart(4, '0');
|
|
711
|
+
|
|
712
|
+
return `${bankPrefix || 'ACC'}-${accountSuffix}`;
|
|
713
|
+
}
|
|
714
|
+
|
|
522
715
|
private toCents(value: number) {
|
|
523
716
|
return Math.round(value * 100);
|
|
524
717
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from './dto/create-financial-title.dto';
|
|
2
|
-
export * from './finance.controller';
|
|
2
|
+
export * from './finance-bank-accounts.controller';
|
|
3
|
+
export * from './finance-data.controller';
|
|
4
|
+
export * from './finance-installments.controller';
|
|
3
5
|
export * from './finance.module';
|
|
4
6
|
export * from './finance.service';
|
|
5
7
|
|