@hed-hog/finance 0.0.350 → 0.0.353
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-financial-title.dto.d.ts +6 -0
- package/dist/dto/create-financial-title.dto.d.ts.map +1 -1
- package/dist/dto/create-financial-title.dto.js +27 -1
- package/dist/dto/create-financial-title.dto.js.map +1 -1
- package/dist/finance-data.controller.d.ts +12 -0
- package/dist/finance-data.controller.d.ts.map +1 -1
- package/dist/finance-installments.controller.d.ts +120 -0
- package/dist/finance-installments.controller.d.ts.map +1 -1
- package/dist/finance-realtime.controller.d.ts +7 -0
- package/dist/finance-realtime.controller.d.ts.map +1 -0
- package/dist/finance-realtime.controller.js +34 -0
- package/dist/finance-realtime.controller.js.map +1 -0
- package/dist/finance-realtime.service.d.ts +36 -0
- package/dist/finance-realtime.service.d.ts.map +1 -0
- package/dist/finance-realtime.service.js +59 -0
- package/dist/finance-realtime.service.js.map +1 -0
- package/dist/finance-statements.controller.d.ts +6 -0
- package/dist/finance-statements.controller.d.ts.map +1 -1
- package/dist/finance.module.d.ts.map +1 -1
- package/dist/finance.module.js +28 -0
- package/dist/finance.module.js.map +1 -1
- package/dist/finance.service.d.ts +142 -1
- package/dist/finance.service.d.ts.map +1 -1
- package/dist/finance.service.js +134 -19
- package/dist/finance.service.js.map +1 -1
- package/dist/mcp-tools/finance-audit-logs.mcp-tools.d.ts +8 -0
- package/dist/mcp-tools/finance-audit-logs.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-audit-logs.mcp-tools.js +60 -0
- package/dist/mcp-tools/finance-audit-logs.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-bank-accounts.mcp-tools.d.ts +16 -0
- package/dist/mcp-tools/finance-bank-accounts.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-bank-accounts.mcp-tools.js +121 -0
- package/dist/mcp-tools/finance-bank-accounts.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-categories.mcp-tools.d.ts +20 -0
- package/dist/mcp-tools/finance-categories.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-categories.mcp-tools.js +135 -0
- package/dist/mcp-tools/finance-categories.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-collections.mcp-tools.d.ts +16 -0
- package/dist/mcp-tools/finance-collections.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-collections.mcp-tools.js +91 -0
- package/dist/mcp-tools/finance-collections.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-cost-centers.mcp-tools.d.ts +16 -0
- package/dist/mcp-tools/finance-cost-centers.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-cost-centers.mcp-tools.js +114 -0
- package/dist/mcp-tools/finance-cost-centers.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-currencies.mcp-tools.d.ts +16 -0
- package/dist/mcp-tools/finance-currencies.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-currencies.mcp-tools.js +120 -0
- package/dist/mcp-tools/finance-currencies.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-data.mcp-tools.d.ts +15 -0
- package/dist/mcp-tools/finance-data.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-data.mcp-tools.js +80 -0
- package/dist/mcp-tools/finance-data.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-installments.mcp-tools.d.ts +93 -0
- package/dist/mcp-tools/finance-installments.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-installments.mcp-tools.js +646 -0
- package/dist/mcp-tools/finance-installments.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-period-close.mcp-tools.d.ts +9 -0
- package/dist/mcp-tools/finance-period-close.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-period-close.mcp-tools.js +79 -0
- package/dist/mcp-tools/finance-period-close.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-reports.mcp-tools.d.ts +10 -0
- package/dist/mcp-tools/finance-reports.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-reports.mcp-tools.js +89 -0
- package/dist/mcp-tools/finance-reports.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-statements.mcp-tools.d.ts +34 -0
- package/dist/mcp-tools/finance-statements.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-statements.mcp-tools.js +253 -0
- package/dist/mcp-tools/finance-statements.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-transfers.mcp-tools.d.ts +9 -0
- package/dist/mcp-tools/finance-transfers.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-transfers.mcp-tools.js +79 -0
- package/dist/mcp-tools/finance-transfers.mcp-tools.js.map +1 -0
- package/hedhog/data/route.yaml +659 -1
- package/hedhog/frontend/app/_components/finance-title-actions-menu.tsx.ejs +9 -3
- package/hedhog/frontend/app/_lib/http-error.ts.ejs +105 -0
- package/hedhog/frontend/app/_lib/use-finance-realtime-refresh.ts.ejs +62 -0
- package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +3 -0
- package/hedhog/frontend/app/accounts-payable/installments/[id]/page.tsx.ejs +5 -5
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +776 -344
- package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +7 -13
- package/hedhog/frontend/app/accounts-receivable/installments/[id]/page.tsx.ejs +5 -5
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +802 -355
- package/hedhog/frontend/app/administration/categories/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/administration/currencies/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +7 -3
- package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +3 -3
- package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +28 -7
- package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +12 -3
- package/hedhog/frontend/messages/en.json +63 -3
- package/hedhog/frontend/messages/pt.json +63 -3
- package/hedhog/table/financial_title.yaml +10 -0
- package/package.json +5 -5
- package/src/dto/create-financial-title.dto.ts +23 -0
- package/src/finance-realtime.controller.ts +12 -0
- package/src/finance-realtime.service.ts +106 -0
- package/src/finance.module.ts +28 -0
- package/src/finance.service.ts +184 -45
- package/src/mcp-tools/finance-audit-logs.mcp-tools.ts +38 -0
- package/src/mcp-tools/finance-bank-accounts.mcp-tools.ts +76 -0
- package/src/mcp-tools/finance-categories.mcp-tools.ts +86 -0
- package/src/mcp-tools/finance-collections.mcp-tools.ts +50 -0
- package/src/mcp-tools/finance-cost-centers.mcp-tools.ts +69 -0
- package/src/mcp-tools/finance-currencies.mcp-tools.ts +75 -0
- package/src/mcp-tools/finance-data.mcp-tools.ts +43 -0
- package/src/mcp-tools/finance-installments.mcp-tools.ts +560 -0
- package/src/mcp-tools/finance-period-close.mcp-tools.ts +53 -0
- package/src/mcp-tools/finance-reports.mcp-tools.ts +59 -0
- package/src/mcp-tools/finance-statements.mcp-tools.ts +202 -0
- package/src/mcp-tools/finance-transfers.mcp-tools.ts +53 -0
package/src/finance.service.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from '@hed-hog/api-pagination';
|
|
7
7
|
import { PrismaService } from '@hed-hog/api-prisma';
|
|
8
8
|
import { AiService, FileService, IntegrationDeveloperApiService, SettingService } from '@hed-hog/core';
|
|
9
|
+
import { FinanceRealtimeService } from './finance-realtime.service';
|
|
9
10
|
import {
|
|
10
11
|
BadRequestException,
|
|
11
12
|
ConflictException,
|
|
@@ -89,6 +90,7 @@ export class FinanceService {
|
|
|
89
90
|
private readonly fileService: FileService,
|
|
90
91
|
@Inject(forwardRef(() => IntegrationDeveloperApiService))
|
|
91
92
|
private readonly integrationApi: IntegrationDeveloperApiService,
|
|
93
|
+
private readonly financeRealtime: FinanceRealtimeService,
|
|
92
94
|
) {}
|
|
93
95
|
|
|
94
96
|
async getAgentExtractInfoFromFile(
|
|
@@ -1501,6 +1503,7 @@ export class FinanceService {
|
|
|
1501
1503
|
} as any);
|
|
1502
1504
|
|
|
1503
1505
|
const settings = await this.loadFinancialScenarioSettings();
|
|
1506
|
+
this.financeRealtime.publish({ domain: 'installment', type: 'updated' });
|
|
1504
1507
|
return {
|
|
1505
1508
|
success: true,
|
|
1506
1509
|
cenarios: settings.cenarios,
|
|
@@ -2140,6 +2143,7 @@ export class FinanceService {
|
|
|
2140
2143
|
},
|
|
2141
2144
|
});
|
|
2142
2145
|
|
|
2146
|
+
this.financeRealtime.publish({ domain: 'collection', type: 'created', entityId: personId });
|
|
2143
2147
|
return {
|
|
2144
2148
|
id: String(log.id),
|
|
2145
2149
|
success: true,
|
|
@@ -2260,6 +2264,7 @@ export class FinanceService {
|
|
|
2260
2264
|
};
|
|
2261
2265
|
});
|
|
2262
2266
|
|
|
2267
|
+
this.financeRealtime.publish({ domain: 'collection', type: 'updated', entityId: personId });
|
|
2263
2268
|
return {
|
|
2264
2269
|
success: true,
|
|
2265
2270
|
titleId: String(created.titleId),
|
|
@@ -2749,6 +2754,7 @@ export class FinanceService {
|
|
|
2749
2754
|
});
|
|
2750
2755
|
});
|
|
2751
2756
|
|
|
2757
|
+
this.financeRealtime.publish({ domain: 'bank_reconciliation', type: 'deleted', entityId: id });
|
|
2752
2758
|
return { success: true };
|
|
2753
2759
|
}
|
|
2754
2760
|
|
|
@@ -2791,6 +2797,7 @@ export class FinanceService {
|
|
|
2791
2797
|
},
|
|
2792
2798
|
});
|
|
2793
2799
|
|
|
2800
|
+
this.financeRealtime.publish({ domain: 'tag', type: 'created', entityId: createdTag.id });
|
|
2794
2801
|
return {
|
|
2795
2802
|
id: String(createdTag.id),
|
|
2796
2803
|
nome: createdTag.slug,
|
|
@@ -3120,6 +3127,7 @@ export class FinanceService {
|
|
|
3120
3127
|
payload: { id: transferResult.id, sourceAccountId, destinationAccountId, amount, date: postedDate.toISOString() },
|
|
3121
3128
|
}).catch(() => null);
|
|
3122
3129
|
|
|
3130
|
+
this.financeRealtime.publish({ domain: 'transfer', type: 'created' });
|
|
3123
3131
|
return transferResult;
|
|
3124
3132
|
}
|
|
3125
3133
|
|
|
@@ -3729,6 +3737,7 @@ export class FinanceService {
|
|
|
3729
3737
|
return line;
|
|
3730
3738
|
});
|
|
3731
3739
|
|
|
3740
|
+
this.financeRealtime.publish({ domain: 'bank_statement', type: 'created' });
|
|
3732
3741
|
return {
|
|
3733
3742
|
id: String(created.id),
|
|
3734
3743
|
contaBancariaId: String(created.bank_account_id),
|
|
@@ -3812,6 +3821,7 @@ export class FinanceService {
|
|
|
3812
3821
|
});
|
|
3813
3822
|
});
|
|
3814
3823
|
|
|
3824
|
+
this.financeRealtime.publish({ domain: 'bank_statement', type: 'created', entityId: created.bank_account_id });
|
|
3815
3825
|
return {
|
|
3816
3826
|
id: String(created.id),
|
|
3817
3827
|
contaBancariaId: String(created.bank_account_id),
|
|
@@ -3915,6 +3925,7 @@ export class FinanceService {
|
|
|
3915
3925
|
return result;
|
|
3916
3926
|
});
|
|
3917
3927
|
|
|
3928
|
+
this.financeRealtime.publish({ domain: 'bank_statement', type: 'updated', entityId: id });
|
|
3918
3929
|
return {
|
|
3919
3930
|
id: String(updated.id),
|
|
3920
3931
|
contaBancariaId: String(updated.bank_account_id),
|
|
@@ -3932,7 +3943,7 @@ export class FinanceService {
|
|
|
3932
3943
|
}
|
|
3933
3944
|
|
|
3934
3945
|
async deleteBankStatementEntry(id: number, userId?: number) {
|
|
3935
|
-
|
|
3946
|
+
const result = await this.prisma.$transaction(async (tx) => {
|
|
3936
3947
|
const statementLine = await tx.bank_statement_line.findUnique({
|
|
3937
3948
|
where: { id },
|
|
3938
3949
|
include: {
|
|
@@ -3996,6 +4007,8 @@ export class FinanceService {
|
|
|
3996
4007
|
|
|
3997
4008
|
return { success: true };
|
|
3998
4009
|
});
|
|
4010
|
+
this.financeRealtime.publish({ domain: 'bank_statement', type: 'deleted', entityId: id });
|
|
4011
|
+
return result;
|
|
3999
4012
|
}
|
|
4000
4013
|
|
|
4001
4014
|
async importBankStatements(
|
|
@@ -4144,6 +4157,7 @@ export class FinanceService {
|
|
|
4144
4157
|
return createdStatement;
|
|
4145
4158
|
});
|
|
4146
4159
|
|
|
4160
|
+
this.financeRealtime.publish({ domain: 'bank_statement', type: 'imported', entityId: bankAccountId });
|
|
4147
4161
|
return {
|
|
4148
4162
|
statementId: String(statement.id),
|
|
4149
4163
|
fileId: String(uploadedFile.id),
|
|
@@ -4556,6 +4570,7 @@ export class FinanceService {
|
|
|
4556
4570
|
payload: { id: createdAccount.id, name, bank: data.bank, accountType: this.mapAccountTypeFromPt(data.type) },
|
|
4557
4571
|
}).catch(() => null);
|
|
4558
4572
|
|
|
4573
|
+
this.financeRealtime.publish({ domain: 'bank_account', type: 'created', entityId: createdAccount.id });
|
|
4559
4574
|
return mapped;
|
|
4560
4575
|
}
|
|
4561
4576
|
|
|
@@ -4578,6 +4593,7 @@ export class FinanceService {
|
|
|
4578
4593
|
payload: { id: created.id, code, name: data.name.trim() },
|
|
4579
4594
|
}).catch(() => null);
|
|
4580
4595
|
|
|
4596
|
+
this.financeRealtime.publish({ domain: 'cost_center', type: 'created', entityId: created.id });
|
|
4581
4597
|
return this.mapCostCenterToFront(created);
|
|
4582
4598
|
}
|
|
4583
4599
|
|
|
@@ -4596,6 +4612,7 @@ export class FinanceService {
|
|
|
4596
4612
|
},
|
|
4597
4613
|
});
|
|
4598
4614
|
|
|
4615
|
+
this.financeRealtime.publish({ domain: 'category', type: 'created', entityId: created.id });
|
|
4599
4616
|
return this.mapFinanceCategoryToFront(created);
|
|
4600
4617
|
}
|
|
4601
4618
|
|
|
@@ -4647,6 +4664,7 @@ export class FinanceService {
|
|
|
4647
4664
|
},
|
|
4648
4665
|
});
|
|
4649
4666
|
|
|
4667
|
+
this.financeRealtime.publish({ domain: 'period_close', type: 'created', entityId: created.id });
|
|
4650
4668
|
return this.mapPeriodCloseToFront(created);
|
|
4651
4669
|
}
|
|
4652
4670
|
|
|
@@ -4702,6 +4720,7 @@ export class FinanceService {
|
|
|
4702
4720
|
payload: { id, name: data.description, status: data.status },
|
|
4703
4721
|
}).catch(() => null);
|
|
4704
4722
|
|
|
4723
|
+
this.financeRealtime.publish({ domain: 'bank_account', type: 'updated', entityId: id });
|
|
4705
4724
|
return this.mapBankAccountToFront(updatedBankAccount);
|
|
4706
4725
|
}
|
|
4707
4726
|
|
|
@@ -4731,6 +4750,7 @@ export class FinanceService {
|
|
|
4731
4750
|
payload: { id, name: data.name, status: data.status },
|
|
4732
4751
|
}).catch(() => null);
|
|
4733
4752
|
|
|
4753
|
+
this.financeRealtime.publish({ domain: 'cost_center', type: 'updated', entityId: id });
|
|
4734
4754
|
return this.mapCostCenterToFront(updatedCostCenter);
|
|
4735
4755
|
}
|
|
4736
4756
|
|
|
@@ -4759,6 +4779,7 @@ export class FinanceService {
|
|
|
4759
4779
|
},
|
|
4760
4780
|
});
|
|
4761
4781
|
|
|
4782
|
+
this.financeRealtime.publish({ domain: 'category', type: 'updated', entityId: id });
|
|
4762
4783
|
return this.mapFinanceCategoryToFront(updated);
|
|
4763
4784
|
}
|
|
4764
4785
|
|
|
@@ -4808,6 +4829,7 @@ export class FinanceService {
|
|
|
4808
4829
|
),
|
|
4809
4830
|
);
|
|
4810
4831
|
|
|
4832
|
+
this.financeRealtime.publish({ domain: 'category', type: 'moved', entityId: id });
|
|
4811
4833
|
return { success: true };
|
|
4812
4834
|
}
|
|
4813
4835
|
|
|
@@ -4834,6 +4856,7 @@ export class FinanceService {
|
|
|
4834
4856
|
payload: { id },
|
|
4835
4857
|
}).catch(() => null);
|
|
4836
4858
|
|
|
4859
|
+
this.financeRealtime.publish({ domain: 'bank_account', type: 'deleted', entityId: id });
|
|
4837
4860
|
return { success: true };
|
|
4838
4861
|
}
|
|
4839
4862
|
|
|
@@ -4860,6 +4883,7 @@ export class FinanceService {
|
|
|
4860
4883
|
payload: { id },
|
|
4861
4884
|
}).catch(() => null);
|
|
4862
4885
|
|
|
4886
|
+
this.financeRealtime.publish({ domain: 'cost_center', type: 'deleted', entityId: id });
|
|
4863
4887
|
return { success: true };
|
|
4864
4888
|
}
|
|
4865
4889
|
|
|
@@ -4899,6 +4923,7 @@ export class FinanceService {
|
|
|
4899
4923
|
},
|
|
4900
4924
|
});
|
|
4901
4925
|
|
|
4926
|
+
this.financeRealtime.publish({ domain: 'currency', type: 'created', entityId: created.id });
|
|
4902
4927
|
return {
|
|
4903
4928
|
id: String(created.id),
|
|
4904
4929
|
code: created.code,
|
|
@@ -4929,6 +4954,7 @@ export class FinanceService {
|
|
|
4929
4954
|
},
|
|
4930
4955
|
});
|
|
4931
4956
|
|
|
4957
|
+
this.financeRealtime.publish({ domain: 'currency', type: 'updated', entityId: id });
|
|
4932
4958
|
return {
|
|
4933
4959
|
id: String(updated.id),
|
|
4934
4960
|
code: updated.code,
|
|
@@ -4954,6 +4980,7 @@ export class FinanceService {
|
|
|
4954
4980
|
data: { status: 'inactive' },
|
|
4955
4981
|
});
|
|
4956
4982
|
|
|
4983
|
+
this.financeRealtime.publish({ domain: 'currency', type: 'deleted', entityId: id });
|
|
4957
4984
|
return { success: true };
|
|
4958
4985
|
}
|
|
4959
4986
|
|
|
@@ -4974,6 +5001,7 @@ export class FinanceService {
|
|
|
4974
5001
|
},
|
|
4975
5002
|
});
|
|
4976
5003
|
|
|
5004
|
+
this.financeRealtime.publish({ domain: 'category', type: 'deleted', entityId: id });
|
|
4977
5005
|
return { success: true };
|
|
4978
5006
|
}
|
|
4979
5007
|
|
|
@@ -5078,7 +5106,23 @@ export class FinanceService {
|
|
|
5078
5106
|
locale: string,
|
|
5079
5107
|
userId?: number,
|
|
5080
5108
|
) {
|
|
5081
|
-
const
|
|
5109
|
+
const rule = data.recurrence_rule;
|
|
5110
|
+
if (rule && !rule.end_date && !rule.max_occurrences) {
|
|
5111
|
+
throw new BadRequestException(
|
|
5112
|
+
getLocaleText('recurrenceEndOrCountRequired', locale, 'Provide end_date or max_occurrences for recurring titles'),
|
|
5113
|
+
);
|
|
5114
|
+
}
|
|
5115
|
+
|
|
5116
|
+
const isRecurring = Boolean(rule);
|
|
5117
|
+
const installments = isRecurring
|
|
5118
|
+
? this.buildRecurrenceInstallments(
|
|
5119
|
+
data.due_date,
|
|
5120
|
+
rule!.frequency,
|
|
5121
|
+
this.toCents(data.total_amount),
|
|
5122
|
+
rule!.end_date,
|
|
5123
|
+
rule!.max_occurrences,
|
|
5124
|
+
)
|
|
5125
|
+
: this.normalizeAndValidateInstallments(data, locale);
|
|
5082
5126
|
|
|
5083
5127
|
const createdTitleId = await this.prisma.$transaction(async (tx) => {
|
|
5084
5128
|
const person = await tx.person.findUnique({
|
|
@@ -5152,6 +5196,10 @@ export class FinanceService {
|
|
|
5152
5196
|
'create title',
|
|
5153
5197
|
);
|
|
5154
5198
|
|
|
5199
|
+
const totalAmountCents = isRecurring
|
|
5200
|
+
? this.toCents(data.total_amount) * installments.length
|
|
5201
|
+
: this.toCents(data.total_amount);
|
|
5202
|
+
|
|
5155
5203
|
const title = await tx.financial_title.create({
|
|
5156
5204
|
data: {
|
|
5157
5205
|
person_id: data.person_id,
|
|
@@ -5163,7 +5211,10 @@ export class FinanceService {
|
|
|
5163
5211
|
? this.parseLocalDate(data.competence_date)
|
|
5164
5212
|
: null,
|
|
5165
5213
|
issue_date: data.issue_date ? this.parseLocalDate(data.issue_date) : null,
|
|
5166
|
-
total_amount_cents:
|
|
5214
|
+
total_amount_cents: totalAmountCents,
|
|
5215
|
+
is_recurring: isRecurring,
|
|
5216
|
+
recurrence_frequency: rule?.frequency ?? null,
|
|
5217
|
+
recurrence_end_date: rule?.end_date ? this.parseLocalDate(rule.end_date) : null,
|
|
5167
5218
|
finance_category_id: data.finance_category_id,
|
|
5168
5219
|
created_by_user_id: userId,
|
|
5169
5220
|
},
|
|
@@ -5229,6 +5280,7 @@ export class FinanceService {
|
|
|
5229
5280
|
});
|
|
5230
5281
|
|
|
5231
5282
|
const createdTitle = await this.getTitleById(createdTitleId, titleType, locale);
|
|
5283
|
+
this.financeRealtime.publish({ domain: 'installment', type: 'created', entityId: createdTitleId });
|
|
5232
5284
|
return this.mapTitleToFront(createdTitle, data.payment_channel);
|
|
5233
5285
|
}
|
|
5234
5286
|
|
|
@@ -5239,7 +5291,23 @@ export class FinanceService {
|
|
|
5239
5291
|
locale: string,
|
|
5240
5292
|
userId?: number,
|
|
5241
5293
|
) {
|
|
5242
|
-
const
|
|
5294
|
+
const rule = data.recurrence_rule;
|
|
5295
|
+
if (rule && !rule.end_date && !rule.max_occurrences) {
|
|
5296
|
+
throw new BadRequestException(
|
|
5297
|
+
getLocaleText('recurrenceEndOrCountRequired', locale, 'Provide end_date or max_occurrences for recurring titles'),
|
|
5298
|
+
);
|
|
5299
|
+
}
|
|
5300
|
+
|
|
5301
|
+
const isRecurring = Boolean(rule);
|
|
5302
|
+
const installments = isRecurring
|
|
5303
|
+
? this.buildRecurrenceInstallments(
|
|
5304
|
+
data.due_date,
|
|
5305
|
+
rule!.frequency,
|
|
5306
|
+
this.toCents(data.total_amount),
|
|
5307
|
+
rule!.end_date,
|
|
5308
|
+
rule!.max_occurrences,
|
|
5309
|
+
)
|
|
5310
|
+
: this.normalizeAndValidateInstallments(data, locale);
|
|
5243
5311
|
|
|
5244
5312
|
const updatedTitle = await this.prisma.$transaction(async (tx) => {
|
|
5245
5313
|
const title = await tx.financial_title.findFirst({
|
|
@@ -5362,6 +5430,10 @@ export class FinanceService {
|
|
|
5362
5430
|
}
|
|
5363
5431
|
}
|
|
5364
5432
|
|
|
5433
|
+
const totalAmountCents = isRecurring
|
|
5434
|
+
? this.toCents(data.total_amount) * installments.length
|
|
5435
|
+
: this.toCents(data.total_amount);
|
|
5436
|
+
|
|
5365
5437
|
await tx.financial_title.update({
|
|
5366
5438
|
where: { id: title.id },
|
|
5367
5439
|
data: {
|
|
@@ -5372,7 +5444,10 @@ export class FinanceService {
|
|
|
5372
5444
|
? this.parseLocalDate(data.competence_date)
|
|
5373
5445
|
: null,
|
|
5374
5446
|
issue_date: data.issue_date ? this.parseLocalDate(data.issue_date) : null,
|
|
5375
|
-
total_amount_cents:
|
|
5447
|
+
total_amount_cents: totalAmountCents,
|
|
5448
|
+
is_recurring: isRecurring,
|
|
5449
|
+
recurrence_frequency: rule?.frequency ?? null,
|
|
5450
|
+
recurrence_end_date: rule?.end_date ? this.parseLocalDate(rule.end_date) : null,
|
|
5376
5451
|
finance_category_id: data.finance_category_id,
|
|
5377
5452
|
},
|
|
5378
5453
|
});
|
|
@@ -5478,9 +5553,49 @@ export class FinanceService {
|
|
|
5478
5553
|
throw new NotFoundException('Financial title not found');
|
|
5479
5554
|
}
|
|
5480
5555
|
|
|
5556
|
+
this.financeRealtime.publish({ domain: 'installment', type: 'updated', entityId: titleId });
|
|
5481
5557
|
return this.mapTitleToFront(updatedTitle, data.payment_channel);
|
|
5482
5558
|
}
|
|
5483
5559
|
|
|
5560
|
+
private buildRecurrenceInstallments(
|
|
5561
|
+
startDate: string,
|
|
5562
|
+
frequency: string,
|
|
5563
|
+
amountCents: number,
|
|
5564
|
+
endDate?: string,
|
|
5565
|
+
maxOccurrences?: number,
|
|
5566
|
+
) {
|
|
5567
|
+
const advance = (date: Date, freq: string): Date => {
|
|
5568
|
+
const next = new Date(date);
|
|
5569
|
+
switch (freq) {
|
|
5570
|
+
case 'weekly': next.setDate(next.getDate() + 7); break;
|
|
5571
|
+
case 'biweekly': next.setDate(next.getDate() + 14); break;
|
|
5572
|
+
case 'bimonthly': next.setMonth(next.getMonth() + 2); break;
|
|
5573
|
+
case 'quarterly': next.setMonth(next.getMonth() + 3); break;
|
|
5574
|
+
case 'semiannual': next.setMonth(next.getMonth() + 6); break;
|
|
5575
|
+
case 'annual': next.setFullYear(next.getFullYear() + 1); break;
|
|
5576
|
+
default: next.setMonth(next.getMonth() + 1); // monthly
|
|
5577
|
+
}
|
|
5578
|
+
return next;
|
|
5579
|
+
};
|
|
5580
|
+
|
|
5581
|
+
const limit = maxOccurrences ?? 600;
|
|
5582
|
+
const endDateObj = endDate ? this.parseLocalDate(endDate) : null;
|
|
5583
|
+
const installments: { installment_number: number; due_date: string; amount_cents: number }[] = [];
|
|
5584
|
+
let current = this.parseLocalDate(startDate);
|
|
5585
|
+
|
|
5586
|
+
while (installments.length < limit) {
|
|
5587
|
+
if (endDateObj && current > endDateObj) break;
|
|
5588
|
+
installments.push({
|
|
5589
|
+
installment_number: installments.length + 1,
|
|
5590
|
+
due_date: current.toISOString().slice(0, 10),
|
|
5591
|
+
amount_cents: amountCents,
|
|
5592
|
+
});
|
|
5593
|
+
current = advance(current, frequency);
|
|
5594
|
+
}
|
|
5595
|
+
|
|
5596
|
+
return installments;
|
|
5597
|
+
}
|
|
5598
|
+
|
|
5484
5599
|
private normalizeAndValidateInstallments(
|
|
5485
5600
|
data: CreateFinancialTitleDto,
|
|
5486
5601
|
locale: string,
|
|
@@ -5619,6 +5734,7 @@ export class FinanceService {
|
|
|
5619
5734
|
throw new NotFoundException('Financial title not found');
|
|
5620
5735
|
}
|
|
5621
5736
|
|
|
5737
|
+
this.financeRealtime.publish({ domain: 'installment', type: 'approved', entityId: titleId });
|
|
5622
5738
|
return this.mapTitleToFront(updatedTitle);
|
|
5623
5739
|
}
|
|
5624
5740
|
|
|
@@ -5695,6 +5811,7 @@ export class FinanceService {
|
|
|
5695
5811
|
throw new NotFoundException('Financial title not found');
|
|
5696
5812
|
}
|
|
5697
5813
|
|
|
5814
|
+
this.financeRealtime.publish({ domain: 'installment', type: 'rejected', entityId: titleId });
|
|
5698
5815
|
return this.mapTitleToFront(updatedTitle);
|
|
5699
5816
|
}
|
|
5700
5817
|
|
|
@@ -5804,6 +5921,7 @@ export class FinanceService {
|
|
|
5804
5921
|
throw new NotFoundException('Financial title not found');
|
|
5805
5922
|
}
|
|
5806
5923
|
|
|
5924
|
+
this.financeRealtime.publish({ domain: 'installment', type: 'canceled', entityId: titleId });
|
|
5807
5925
|
return this.mapTitleToFront(updatedTitle);
|
|
5808
5926
|
}
|
|
5809
5927
|
|
|
@@ -6107,6 +6225,7 @@ export class FinanceService {
|
|
|
6107
6225
|
};
|
|
6108
6226
|
});
|
|
6109
6227
|
|
|
6228
|
+
this.financeRealtime.publish({ domain: 'settlement', type: 'settled', entityId: result.settlementId });
|
|
6110
6229
|
return {
|
|
6111
6230
|
...this.mapTitleToFront(result.title),
|
|
6112
6231
|
settlementId: String(result.settlementId),
|
|
@@ -6428,6 +6547,7 @@ export class FinanceService {
|
|
|
6428
6547
|
};
|
|
6429
6548
|
});
|
|
6430
6549
|
|
|
6550
|
+
this.financeRealtime.publish({ domain: 'settlement', type: 'reversed', entityId: settlementId });
|
|
6431
6551
|
return title;
|
|
6432
6552
|
}
|
|
6433
6553
|
|
|
@@ -6517,6 +6637,7 @@ export class FinanceService {
|
|
|
6517
6637
|
});
|
|
6518
6638
|
|
|
6519
6639
|
const updatedTitle = await this.getTitleById(titleId, titleType, locale);
|
|
6640
|
+
this.financeRealtime.publish({ domain: 'tag', type: 'updated', entityId: titleId });
|
|
6520
6641
|
return this.mapTitleToFront(updatedTitle);
|
|
6521
6642
|
}
|
|
6522
6643
|
|
|
@@ -6868,54 +6989,67 @@ export class FinanceService {
|
|
|
6868
6989
|
const tags = [
|
|
6869
6990
|
...new Set(
|
|
6870
6991
|
title.financial_installment
|
|
6871
|
-
.
|
|
6872
|
-
.
|
|
6992
|
+
.map((installment) => installment.financial_installment_tag?.tag_id)
|
|
6993
|
+
.filter((tagId) => typeof tagId === 'number')
|
|
6994
|
+
.map((tagId) => String(tagId)),
|
|
6873
6995
|
),
|
|
6874
6996
|
];
|
|
6875
6997
|
|
|
6876
|
-
const
|
|
6877
|
-
|
|
6998
|
+
const settlementAllocations = title.financial_installment.flatMap(
|
|
6999
|
+
(installment) =>
|
|
7000
|
+
installment.settlement_allocation
|
|
7001
|
+
? [installment.settlement_allocation]
|
|
7002
|
+
: [],
|
|
7003
|
+
);
|
|
7004
|
+
|
|
7005
|
+
const channelFromSettlement = settlementAllocations
|
|
6878
7006
|
.map((allocation) => allocation.settlement?.payment_method?.type)
|
|
6879
7007
|
.find(Boolean);
|
|
6880
7008
|
|
|
6881
|
-
const mappedInstallments = title.financial_installment.map((installment) =>
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
7009
|
+
const mappedInstallments = title.financial_installment.map((installment) => {
|
|
7010
|
+
const installmentSettlementAllocations = installment.settlement_allocation
|
|
7011
|
+
? [installment.settlement_allocation]
|
|
7012
|
+
: [];
|
|
7013
|
+
|
|
7014
|
+
return {
|
|
7015
|
+
id: String(installment.id),
|
|
7016
|
+
numero: installment.installment_number,
|
|
7017
|
+
vencimento: installment.due_date.toISOString(),
|
|
7018
|
+
valor: this.fromCents(installment.amount_cents),
|
|
7019
|
+
valorAberto: this.fromCents(installment.open_amount_cents),
|
|
7020
|
+
status: this.mapStatusToPt(
|
|
7021
|
+
this.resolveInstallmentStatus(
|
|
7022
|
+
installment.amount_cents,
|
|
7023
|
+
installment.open_amount_cents,
|
|
7024
|
+
installment.due_date,
|
|
7025
|
+
installment.status,
|
|
7026
|
+
),
|
|
6893
7027
|
),
|
|
6894
|
-
|
|
6895
|
-
metodoPagamento:
|
|
6896
|
-
this.mapPaymentMethodToPt(
|
|
6897
|
-
installment.settlement_allocation[0]?.settlement?.payment_method?.type,
|
|
6898
|
-
) || paymentChannelOverride || 'transferencia',
|
|
6899
|
-
liquidacoes: installment.settlement_allocation.map((allocation) => ({
|
|
6900
|
-
id: String(allocation.id),
|
|
6901
|
-
settlementId: allocation.settlement?.id
|
|
6902
|
-
? String(allocation.settlement.id)
|
|
6903
|
-
: null,
|
|
6904
|
-
data: allocation.settlement?.settled_at?.toISOString(),
|
|
6905
|
-
valor: this.fromCents(allocation.allocated_amount_cents),
|
|
6906
|
-
juros: this.fromCents(allocation.interest_cents || 0),
|
|
6907
|
-
desconto: this.fromCents(allocation.discount_cents || 0),
|
|
6908
|
-
multa: this.fromCents(allocation.penalty_cents || 0),
|
|
6909
|
-
contaBancariaId: allocation.settlement?.bank_account_id
|
|
6910
|
-
? String(allocation.settlement.bank_account_id)
|
|
6911
|
-
: null,
|
|
6912
|
-
status: allocation.settlement?.status || null,
|
|
6913
|
-
metodo:
|
|
7028
|
+
metodoPagamento:
|
|
6914
7029
|
this.mapPaymentMethodToPt(
|
|
6915
|
-
|
|
6916
|
-
) || 'transferencia',
|
|
6917
|
-
|
|
6918
|
-
|
|
7030
|
+
installmentSettlementAllocations[0]?.settlement?.payment_method?.type,
|
|
7031
|
+
) || paymentChannelOverride || 'transferencia',
|
|
7032
|
+
liquidacoes: installmentSettlementAllocations.map((allocation) => ({
|
|
7033
|
+
id: String(allocation.id),
|
|
7034
|
+
settlementId: allocation.settlement?.id
|
|
7035
|
+
? String(allocation.settlement.id)
|
|
7036
|
+
: null,
|
|
7037
|
+
data: allocation.settlement?.settled_at?.toISOString(),
|
|
7038
|
+
valor: this.fromCents(allocation.allocated_amount_cents),
|
|
7039
|
+
juros: this.fromCents(allocation.interest_cents || 0),
|
|
7040
|
+
desconto: this.fromCents(allocation.discount_cents || 0),
|
|
7041
|
+
multa: this.fromCents(allocation.penalty_cents || 0),
|
|
7042
|
+
contaBancariaId: allocation.settlement?.bank_account_id
|
|
7043
|
+
? String(allocation.settlement.bank_account_id)
|
|
7044
|
+
: null,
|
|
7045
|
+
status: allocation.settlement?.status || null,
|
|
7046
|
+
metodo:
|
|
7047
|
+
this.mapPaymentMethodToPt(
|
|
7048
|
+
allocation.settlement?.payment_method?.type,
|
|
7049
|
+
) || 'transferencia',
|
|
7050
|
+
})),
|
|
7051
|
+
};
|
|
7052
|
+
});
|
|
6919
7053
|
|
|
6920
7054
|
const attachmentDetails = title.financial_title_attachment.map((attachment) => ({
|
|
6921
7055
|
id: String(attachment.file_id),
|
|
@@ -6942,6 +7076,11 @@ export class FinanceService {
|
|
|
6942
7076
|
this.mapPaymentMethodToPt(channelFromSettlement) ||
|
|
6943
7077
|
paymentChannelOverride ||
|
|
6944
7078
|
'transferencia',
|
|
7079
|
+
isRecurring: title.is_recurring ?? false,
|
|
7080
|
+
recurrenceFrequency: title.recurrence_frequency ?? null,
|
|
7081
|
+
recurrenceEndDate: title.recurrence_end_date
|
|
7082
|
+
? title.recurrence_end_date.toISOString().slice(0, 10)
|
|
7083
|
+
: null,
|
|
6945
7084
|
...(title.title_type === 'payable'
|
|
6946
7085
|
? {
|
|
6947
7086
|
fornecedorId: String(title.person_id),
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { McpContext, McpTool } from '@hed-hog/core';
|
|
2
|
+
import { Injectable } from '@nestjs/common';
|
|
3
|
+
import { FinanceService } from '../finance.service';
|
|
4
|
+
|
|
5
|
+
@Injectable()
|
|
6
|
+
export class FinanceAuditLogsMcpTools {
|
|
7
|
+
constructor(private readonly financeService: FinanceService) {}
|
|
8
|
+
|
|
9
|
+
@McpTool({
|
|
10
|
+
name: 'finance.audit-logs.list',
|
|
11
|
+
description: 'Lists finance audit logs with filters and pagination.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
page: { type: 'number', description: 'Page number (default: 1)' },
|
|
16
|
+
pageSize: { type: 'number', description: 'Items per page (default: 20)' },
|
|
17
|
+
search: { type: 'string', description: 'Search term' },
|
|
18
|
+
action: { type: 'string', description: 'Action filter' },
|
|
19
|
+
entity_table: { type: 'string', description: 'Entity table filter' },
|
|
20
|
+
actor_user_id: { type: 'string', description: 'Actor user id filter' },
|
|
21
|
+
from: { type: 'string', description: 'Start date (YYYY-MM-DD)' },
|
|
22
|
+
to: { type: 'string', description: 'End date (YYYY-MM-DD)' },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
readOnly: true,
|
|
26
|
+
})
|
|
27
|
+
async list(args: Record<string, any>, _context: McpContext): Promise<any> {
|
|
28
|
+
const { page, pageSize, search, action, entity_table, actor_user_id, from, to } = args;
|
|
29
|
+
return this.financeService.listAuditLogs({ page, pageSize } as any, {
|
|
30
|
+
search,
|
|
31
|
+
action,
|
|
32
|
+
entity_table,
|
|
33
|
+
actor_user_id,
|
|
34
|
+
from,
|
|
35
|
+
to,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { McpContext, McpTool } from '@hed-hog/core';
|
|
2
|
+
import { Injectable } from '@nestjs/common';
|
|
3
|
+
import { FinanceService } from '../finance.service';
|
|
4
|
+
|
|
5
|
+
@Injectable()
|
|
6
|
+
export class FinanceBankAccountsMcpTools {
|
|
7
|
+
constructor(private readonly financeService: FinanceService) {}
|
|
8
|
+
|
|
9
|
+
@McpTool({
|
|
10
|
+
name: 'finance.bank-accounts.list',
|
|
11
|
+
description: 'Lists bank accounts with pagination.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
page: { type: 'number', description: 'Page number (default: 1)' },
|
|
16
|
+
pageSize: { type: 'number', description: 'Items per page (default: 20)' },
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
readOnly: true,
|
|
20
|
+
})
|
|
21
|
+
async list(args: Record<string, any>, _context: McpContext): Promise<any> {
|
|
22
|
+
return this.financeService.listBankAccounts(args as any);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@McpTool({
|
|
26
|
+
name: 'finance.bank-accounts.create',
|
|
27
|
+
description: 'Creates a bank account.',
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
bank: { type: 'string', description: 'Bank name' },
|
|
32
|
+
type: { type: 'string', description: 'Account type' },
|
|
33
|
+
currency_id: { type: 'number', description: 'Currency ID' },
|
|
34
|
+
opening_balance: { type: 'number', description: 'Opening balance' },
|
|
35
|
+
},
|
|
36
|
+
required: ['bank', 'type'],
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
async create(args: Record<string, any>, context: McpContext): Promise<any> {
|
|
40
|
+
return this.financeService.createBankAccount(args as any, context.userId);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@McpTool({
|
|
44
|
+
name: 'finance.bank-accounts.update',
|
|
45
|
+
description: 'Updates an existing bank account.',
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
id: { type: 'number', description: 'Bank account ID' },
|
|
50
|
+
bank: { type: 'string', description: 'Bank name' },
|
|
51
|
+
type: { type: 'string', description: 'Account type' },
|
|
52
|
+
currency_id: { type: 'number', description: 'Currency ID' },
|
|
53
|
+
},
|
|
54
|
+
required: ['id'],
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
async update(args: { id: number; [key: string]: any }, _context: McpContext): Promise<any> {
|
|
58
|
+
const { id, ...data } = args;
|
|
59
|
+
return this.financeService.updateBankAccount(id, data as any);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@McpTool({
|
|
63
|
+
name: 'finance.bank-accounts.delete',
|
|
64
|
+
description: 'Deletes a bank account.',
|
|
65
|
+
inputSchema: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
id: { type: 'number', description: 'Bank account ID' },
|
|
69
|
+
},
|
|
70
|
+
required: ['id'],
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
async delete(args: { id: number }, _context: McpContext): Promise<any> {
|
|
74
|
+
return this.financeService.deleteBankAccount(args.id);
|
|
75
|
+
}
|
|
76
|
+
}
|