@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.
Files changed (50) hide show
  1. package/dist/dto/create-bank-account.dto.d.ts +9 -0
  2. package/dist/dto/create-bank-account.dto.d.ts.map +1 -0
  3. package/dist/dto/create-bank-account.dto.js +61 -0
  4. package/dist/dto/create-bank-account.dto.js.map +1 -0
  5. package/dist/dto/update-bank-account.dto.d.ts +9 -0
  6. package/dist/dto/update-bank-account.dto.d.ts.map +1 -0
  7. package/dist/dto/update-bank-account.dto.js +60 -0
  8. package/dist/dto/update-bank-account.dto.js.map +1 -0
  9. package/dist/finance-bank-accounts.controller.d.ts +47 -0
  10. package/dist/finance-bank-accounts.controller.d.ts.map +1 -0
  11. package/dist/finance-bank-accounts.controller.js +73 -0
  12. package/dist/finance-bank-accounts.controller.js.map +1 -0
  13. package/dist/finance-data.controller.d.ts +136 -0
  14. package/dist/finance-data.controller.d.ts.map +1 -0
  15. package/dist/finance-data.controller.js +36 -0
  16. package/dist/finance-data.controller.js.map +1 -0
  17. package/dist/finance-installments.controller.d.ts +149 -0
  18. package/dist/finance-installments.controller.d.ts.map +1 -0
  19. package/dist/finance-installments.controller.js +101 -0
  20. package/dist/finance-installments.controller.js.map +1 -0
  21. package/dist/finance.module.d.ts.map +1 -1
  22. package/dist/finance.module.js +8 -2
  23. package/dist/finance.module.js.map +1 -1
  24. package/dist/finance.service.d.ts +53 -5
  25. package/dist/finance.service.d.ts.map +1 -1
  26. package/dist/finance.service.js +167 -9
  27. package/dist/finance.service.js.map +1 -1
  28. package/dist/index.d.ts +3 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +3 -1
  31. package/dist/index.js.map +1 -1
  32. package/hedhog/data/menu.yaml +5 -1
  33. package/hedhog/data/route.yaml +36 -0
  34. package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +4 -1
  35. package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +307 -112
  36. package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +25 -8
  37. package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +301 -100
  38. package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +378 -71
  39. package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +6 -3
  40. package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +4 -2
  41. package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +6 -7
  42. package/package.json +6 -6
  43. package/src/dto/create-bank-account.dto.ts +49 -0
  44. package/src/dto/update-bank-account.dto.ts +45 -0
  45. package/src/finance-bank-accounts.controller.ts +43 -0
  46. package/src/finance-data.controller.ts +14 -0
  47. package/src/{finance.controller.ts → finance-installments.controller.ts} +8 -13
  48. package/src/finance.module.ts +8 -2
  49. package/src/finance.service.ts +202 -9
  50. 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
- Body,
6
- Controller,
7
- Get,
8
- Param,
9
- ParseIntPipe,
10
- Post,
11
- Query,
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 FinanceController {
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,
@@ -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 { FinanceController } from './finance.controller';
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: [FinanceController],
18
+ controllers: [
19
+ FinanceDataController,
20
+ FinanceInstallmentsController,
21
+ FinanceBankAccountsController,
22
+ ],
17
23
  providers: [FinanceService],
18
24
  exports: [FinanceService],
19
25
  })
@@ -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