@hed-hog/finance 0.0.322 → 0.0.326

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 (28) hide show
  1. package/dist/dto/create-bank-account.dto.d.ts +1 -0
  2. package/dist/dto/create-bank-account.dto.d.ts.map +1 -1
  3. package/dist/dto/create-bank-account.dto.js +7 -0
  4. package/dist/dto/create-bank-account.dto.js.map +1 -1
  5. package/dist/dto/update-bank-account.dto.d.ts +1 -0
  6. package/dist/dto/update-bank-account.dto.d.ts.map +1 -1
  7. package/dist/dto/update-bank-account.dto.js +7 -0
  8. package/dist/dto/update-bank-account.dto.js.map +1 -1
  9. package/dist/finance-bank-accounts.controller.d.ts +21 -0
  10. package/dist/finance-bank-accounts.controller.d.ts.map +1 -1
  11. package/dist/finance-data.controller.d.ts +13 -0
  12. package/dist/finance-data.controller.d.ts.map +1 -1
  13. package/dist/finance.service.d.ts +37 -2
  14. package/dist/finance.service.d.ts.map +1 -1
  15. package/dist/finance.service.js +125 -19
  16. package/dist/finance.service.js.map +1 -1
  17. package/hedhog/data/integration_event_catalog.yaml +274 -0
  18. package/hedhog/frontend/app/administration/categories/page.tsx.ejs +1 -1
  19. package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +1 -1
  20. package/hedhog/frontend/app/administration/currencies/page.tsx.ejs +1 -1
  21. package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +1 -1
  22. package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +83 -37
  23. package/hedhog/frontend/widgets/bank-reconciliation-status.tsx.ejs +33 -9
  24. package/hedhog/frontend/widgets/cash-balance-kpi.tsx.ejs +30 -9
  25. package/package.json +7 -7
  26. package/src/dto/create-bank-account.dto.ts +12 -6
  27. package/src/dto/update-bank-account.dto.ts +6 -0
  28. package/src/finance.service.ts +156 -26
@@ -1,11 +1,11 @@
1
1
  import { getLocaleText } from '@hed-hog/api-locale';
2
2
  import {
3
- IsDateString,
4
- IsInt,
5
- IsNumber,
6
- IsOptional,
7
- IsString,
8
- Min,
3
+ IsDateString,
4
+ IsInt,
5
+ IsNumber,
6
+ IsOptional,
7
+ IsString,
8
+ Min,
9
9
  } from 'class-validator';
10
10
 
11
11
  export class CreateBankAccountDto {
@@ -47,6 +47,12 @@ export class CreateBankAccountDto {
47
47
  })
48
48
  logo_file_id?: number | null;
49
49
 
50
+ @IsOptional()
51
+ @IsInt({
52
+ message: (args) => getLocaleText('validation.idMustBeInteger', args.value),
53
+ })
54
+ currency_id?: number | null;
55
+
50
56
  @IsOptional()
51
57
  @IsNumber(
52
58
  {},
@@ -42,6 +42,12 @@ export class UpdateBankAccountDto {
42
42
  })
43
43
  logo_file_id?: number | null;
44
44
 
45
+ @IsOptional()
46
+ @IsInt({
47
+ message: (args) => getLocaleText('validation.idMustBeInteger', args.value),
48
+ })
49
+ currency_id?: number | null;
50
+
45
51
  @IsOptional()
46
52
  @IsString({
47
53
  message: (args) =>
@@ -1,19 +1,19 @@
1
1
  import { getLocaleText } from '@hed-hog/api-locale';
2
2
  import {
3
- PageOrderDirection,
4
- PaginationDTO,
5
- PaginationService,
3
+ PageOrderDirection,
4
+ PaginationDTO,
5
+ PaginationService,
6
6
  } from '@hed-hog/api-pagination';
7
7
  import { PrismaService } from '@hed-hog/api-prisma';
8
- import { AiService, FileService, SettingService } from '@hed-hog/core';
8
+ import { AiService, FileService, IntegrationDeveloperApiService, SettingService } from '@hed-hog/core';
9
9
  import {
10
- BadRequestException,
11
- ConflictException,
12
- forwardRef,
13
- Inject,
14
- Injectable,
15
- Logger,
16
- NotFoundException,
10
+ BadRequestException,
11
+ ConflictException,
12
+ forwardRef,
13
+ Inject,
14
+ Injectable,
15
+ Logger,
16
+ NotFoundException,
17
17
  } from '@nestjs/common';
18
18
  import { createHash } from 'node:crypto';
19
19
  import { readFile } from 'node:fs/promises';
@@ -87,6 +87,8 @@ export class FinanceService {
87
87
  private readonly settingService: SettingService,
88
88
  @Inject(forwardRef(() => FileService))
89
89
  private readonly fileService: FileService,
90
+ @Inject(forwardRef(() => IntegrationDeveloperApiService))
91
+ private readonly integrationApi: IntegrationDeveloperApiService,
90
92
  ) {}
91
93
 
92
94
  async getAgentExtractInfoFromFile(
@@ -2286,9 +2288,36 @@ export class FinanceService {
2286
2288
  );
2287
2289
  const receivableInstallments = this.extractOpenInstallments(receivables);
2288
2290
 
2289
- const saldoCaixa = (bankAccounts || [])
2290
- .filter((account) => account?.ativo !== false)
2291
- .reduce((acc, account) => acc + Number(account?.saldoAtual || 0), 0);
2291
+ const activeAccounts = (bankAccounts || []).filter(
2292
+ (account) => account?.ativo !== false,
2293
+ );
2294
+
2295
+ const saldoCaixa = activeAccounts.reduce(
2296
+ (acc, account) => acc + Number(account?.saldoAtual || 0),
2297
+ 0,
2298
+ );
2299
+
2300
+ const saldoCaixaPorMoeda: Record<
2301
+ string,
2302
+ { total: number; reconciled: number; symbol: string; name: string }
2303
+ > = {};
2304
+ for (const account of activeAccounts) {
2305
+ const code = account?.currency?.code || 'BRL';
2306
+ const symbol = account?.currency?.symbol || 'R$';
2307
+ const name = account?.currency?.name || 'Real Brasileiro';
2308
+ if (!saldoCaixaPorMoeda[code]) {
2309
+ saldoCaixaPorMoeda[code] = { total: 0, reconciled: 0, symbol, name };
2310
+ }
2311
+ saldoCaixaPorMoeda[code].total = Number(
2312
+ (saldoCaixaPorMoeda[code].total + Number(account?.saldoAtual || 0)).toFixed(2),
2313
+ );
2314
+ saldoCaixaPorMoeda[code].reconciled = Number(
2315
+ (
2316
+ saldoCaixaPorMoeda[code].reconciled +
2317
+ Number(account?.saldoConciliado || 0)
2318
+ ).toFixed(2),
2319
+ );
2320
+ }
2292
2321
 
2293
2322
  const aPagar7dias = this.sumInstallmentsDueBetween(
2294
2323
  payableInstallments,
@@ -2318,6 +2347,7 @@ export class FinanceService {
2318
2347
 
2319
2348
  return {
2320
2349
  saldoCaixa: Number(saldoCaixa.toFixed(2)),
2350
+ saldoCaixaPorMoeda,
2321
2351
  aPagar30dias: Number(aPagar30dias.toFixed(2)),
2322
2352
  aPagar7dias: Number(aPagar7dias.toFixed(2)),
2323
2353
  aReceber30dias: Number(aReceber30dias.toFixed(2)),
@@ -2794,6 +2824,14 @@ export class FinanceService {
2794
2824
  description: true,
2795
2825
  },
2796
2826
  },
2827
+ currency: {
2828
+ select: {
2829
+ id: true,
2830
+ code: true,
2831
+ symbol: true,
2832
+ name: true,
2833
+ },
2834
+ },
2797
2835
  },
2798
2836
  orderBy: [{ code: 'asc' }, { name: 'asc' }],
2799
2837
  },
@@ -3057,7 +3095,7 @@ export class FinanceService {
3057
3095
  });
3058
3096
  });
3059
3097
 
3060
- return {
3098
+ const transferResult = {
3061
3099
  id: transferReference.replace('transfer:', ''),
3062
3100
  contaOrigemId: String(sourceAccountId),
3063
3101
  contaDestinoId: String(destinationAccountId),
@@ -3065,6 +3103,16 @@ export class FinanceService {
3065
3103
  valor: amount,
3066
3104
  descricao: description,
3067
3105
  };
3106
+
3107
+ await this.integrationApi.publishEvent({
3108
+ eventName: 'finance.transfer.created',
3109
+ sourceModule: 'finance',
3110
+ aggregateType: 'transfer',
3111
+ aggregateId: transferResult.id,
3112
+ payload: { id: transferResult.id, sourceAccountId, destinationAccountId, amount, date: postedDate.toISOString() },
3113
+ }).catch(() => null);
3114
+
3115
+ return transferResult;
3068
3116
  }
3069
3117
 
3070
3118
  async listCostCenters() {
@@ -4430,6 +4478,7 @@ export class FinanceService {
4430
4478
  account_number: data.account || null,
4431
4479
  account_type: accountType,
4432
4480
  logo_file_id: data.logo_file_id ?? null,
4481
+ currency_id: data.currency_id ?? null,
4433
4482
  status: 'active',
4434
4483
  },
4435
4484
  });
@@ -4478,10 +4527,28 @@ export class FinanceService {
4478
4527
  description: true,
4479
4528
  },
4480
4529
  },
4530
+ currency: {
4531
+ select: {
4532
+ id: true,
4533
+ code: true,
4534
+ symbol: true,
4535
+ name: true,
4536
+ },
4537
+ },
4481
4538
  },
4482
4539
  });
4483
4540
 
4484
- return this.mapBankAccountToFront(account);
4541
+ const mapped = this.mapBankAccountToFront(account);
4542
+
4543
+ await this.integrationApi.publishEvent({
4544
+ eventName: 'finance.bank_account.created',
4545
+ sourceModule: 'finance',
4546
+ aggregateType: 'bank_account',
4547
+ aggregateId: String(createdAccount.id),
4548
+ payload: { id: createdAccount.id, name, bank: data.bank, accountType: this.mapAccountTypeFromPt(data.type) },
4549
+ }).catch(() => null);
4550
+
4551
+ return mapped;
4485
4552
  }
4486
4553
 
4487
4554
  async createCostCenter(data: CreateCostCenterDto) {
@@ -4495,6 +4562,14 @@ export class FinanceService {
4495
4562
  },
4496
4563
  });
4497
4564
 
4565
+ await this.integrationApi.publishEvent({
4566
+ eventName: 'finance.cost_center.created',
4567
+ sourceModule: 'finance',
4568
+ aggregateType: 'cost_center',
4569
+ aggregateId: String(created.id),
4570
+ payload: { id: created.id, code, name: data.name.trim() },
4571
+ }).catch(() => null);
4572
+
4498
4573
  return this.mapCostCenterToFront(created);
4499
4574
  }
4500
4575
 
@@ -4577,7 +4652,7 @@ export class FinanceService {
4577
4652
  throw new NotFoundException('Bank account not found');
4578
4653
  }
4579
4654
 
4580
- const updated = await this.prisma.bank_account.update({
4655
+ const updatedBankAccount = await this.prisma.bank_account.update({
4581
4656
  where: { id },
4582
4657
  data: {
4583
4658
  bank_name: data.bank,
@@ -4587,6 +4662,8 @@ export class FinanceService {
4587
4662
  account_type: data.type ? this.mapAccountTypeFromPt(data.type) : undefined,
4588
4663
  logo_file_id:
4589
4664
  data.logo_file_id === undefined ? undefined : data.logo_file_id,
4665
+ currency_id:
4666
+ data.currency_id === undefined ? undefined : data.currency_id,
4590
4667
  status: data.status,
4591
4668
  },
4592
4669
  include: {
@@ -4598,10 +4675,26 @@ export class FinanceService {
4598
4675
  description: true,
4599
4676
  },
4600
4677
  },
4678
+ currency: {
4679
+ select: {
4680
+ id: true,
4681
+ code: true,
4682
+ symbol: true,
4683
+ name: true,
4684
+ },
4685
+ },
4601
4686
  },
4602
4687
  });
4603
4688
 
4604
- return this.mapBankAccountToFront(updated);
4689
+ await this.integrationApi.publishEvent({
4690
+ eventName: 'finance.bank_account.updated',
4691
+ sourceModule: 'finance',
4692
+ aggregateType: 'bank_account',
4693
+ aggregateId: String(id),
4694
+ payload: { id, name: data.description, status: data.status },
4695
+ }).catch(() => null);
4696
+
4697
+ return this.mapBankAccountToFront(updatedBankAccount);
4605
4698
  }
4606
4699
 
4607
4700
  async updateCostCenter(id: number, data: UpdateCostCenterDto) {
@@ -4614,7 +4707,7 @@ export class FinanceService {
4614
4707
  throw new NotFoundException('Cost center not found');
4615
4708
  }
4616
4709
 
4617
- const updated = await this.prisma.cost_center.update({
4710
+ const updatedCostCenter = await this.prisma.cost_center.update({
4618
4711
  where: { id },
4619
4712
  data: {
4620
4713
  name: data.name?.trim(),
@@ -4622,7 +4715,15 @@ export class FinanceService {
4622
4715
  },
4623
4716
  });
4624
4717
 
4625
- return this.mapCostCenterToFront(updated);
4718
+ await this.integrationApi.publishEvent({
4719
+ eventName: 'finance.cost_center.updated',
4720
+ sourceModule: 'finance',
4721
+ aggregateType: 'cost_center',
4722
+ aggregateId: String(id),
4723
+ payload: { id, name: data.name, status: data.status },
4724
+ }).catch(() => null);
4725
+
4726
+ return this.mapCostCenterToFront(updatedCostCenter);
4626
4727
  }
4627
4728
 
4628
4729
  async updateFinanceCategory(id: number, data: UpdateFinanceCategoryDto) {
@@ -4714,11 +4815,17 @@ export class FinanceService {
4714
4815
 
4715
4816
  await this.prisma.bank_account.update({
4716
4817
  where: { id },
4717
- data: {
4718
- status: 'inactive',
4719
- },
4818
+ data: { status: 'inactive' },
4720
4819
  });
4721
4820
 
4821
+ await this.integrationApi.publishEvent({
4822
+ eventName: 'finance.bank_account.deleted',
4823
+ sourceModule: 'finance',
4824
+ aggregateType: 'bank_account',
4825
+ aggregateId: String(id),
4826
+ payload: { id },
4827
+ }).catch(() => null);
4828
+
4722
4829
  return { success: true };
4723
4830
  }
4724
4831
 
@@ -4734,11 +4841,17 @@ export class FinanceService {
4734
4841
 
4735
4842
  await this.prisma.cost_center.update({
4736
4843
  where: { id },
4737
- data: {
4738
- status: 'inactive',
4739
- },
4844
+ data: { status: 'inactive' },
4740
4845
  });
4741
4846
 
4847
+ await this.integrationApi.publishEvent({
4848
+ eventName: 'finance.cost_center.deleted',
4849
+ sourceModule: 'finance',
4850
+ aggregateType: 'cost_center',
4851
+ aggregateId: String(id),
4852
+ payload: { id },
4853
+ }).catch(() => null);
4854
+
4742
4855
  return { success: true };
4743
4856
  }
4744
4857
 
@@ -6465,6 +6578,14 @@ export class FinanceService {
6465
6578
  description: true,
6466
6579
  },
6467
6580
  },
6581
+ currency: {
6582
+ select: {
6583
+ id: true,
6584
+ code: true,
6585
+ symbol: true,
6586
+ name: true,
6587
+ },
6588
+ },
6468
6589
  },
6469
6590
  orderBy: [{ code: 'asc' }, { name: 'asc' }],
6470
6591
  });
@@ -7016,6 +7137,15 @@ export class FinanceService {
7016
7137
  conta: bankAccount.account_number || '-',
7017
7138
  tipo: this.mapAccountTypeToPt(bankAccount.account_type),
7018
7139
  logoFileId: bankAccount.logo_file_id ?? null,
7140
+ currencyId: bankAccount.currency_id ?? null,
7141
+ currency: bankAccount.currency
7142
+ ? {
7143
+ id: String(bankAccount.currency.id),
7144
+ code: bankAccount.currency.code,
7145
+ symbol: bankAccount.currency.symbol,
7146
+ name: bankAccount.currency.name,
7147
+ }
7148
+ : null,
7019
7149
  saldoAtual: this.fromCents(currentCents),
7020
7150
  saldoConciliado: this.fromCents(reconciledCents),
7021
7151
  ativo: bankAccount.status === 'active',