@hed-hog/finance 0.0.235 → 0.0.237

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 (75) hide show
  1. package/dist/dto/create-cost-center.dto.d.ts +4 -0
  2. package/dist/dto/create-cost-center.dto.d.ts.map +1 -0
  3. package/dist/dto/create-cost-center.dto.js +24 -0
  4. package/dist/dto/create-cost-center.dto.js.map +1 -0
  5. package/dist/dto/create-finance-category.dto.d.ts +6 -0
  6. package/dist/dto/create-finance-category.dto.d.ts.map +1 -0
  7. package/dist/dto/create-finance-category.dto.js +37 -0
  8. package/dist/dto/create-finance-category.dto.js.map +1 -0
  9. package/dist/dto/create-period-close.dto.d.ts +7 -0
  10. package/dist/dto/create-period-close.dto.d.ts.map +1 -0
  11. package/dist/dto/create-period-close.dto.js +44 -0
  12. package/dist/dto/create-period-close.dto.js.map +1 -0
  13. package/dist/dto/move-finance-category.dto.d.ts +5 -0
  14. package/dist/dto/move-finance-category.dto.d.ts.map +1 -0
  15. package/dist/dto/move-finance-category.dto.js +32 -0
  16. package/dist/dto/move-finance-category.dto.js.map +1 -0
  17. package/dist/dto/update-cost-center.dto.d.ts +5 -0
  18. package/dist/dto/update-cost-center.dto.d.ts.map +1 -0
  19. package/dist/dto/update-cost-center.dto.js +32 -0
  20. package/dist/dto/update-cost-center.dto.js.map +1 -0
  21. package/dist/dto/update-finance-category.dto.d.ts +7 -0
  22. package/dist/dto/update-finance-category.dto.d.ts.map +1 -0
  23. package/dist/dto/update-finance-category.dto.js +46 -0
  24. package/dist/dto/update-finance-category.dto.js.map +1 -0
  25. package/dist/finance-audit-logs.controller.d.ts +13 -0
  26. package/dist/finance-audit-logs.controller.d.ts.map +1 -0
  27. package/dist/finance-audit-logs.controller.js +54 -0
  28. package/dist/finance-audit-logs.controller.js.map +1 -0
  29. package/dist/finance-categories.controller.d.ts +42 -0
  30. package/dist/finance-categories.controller.d.ts.map +1 -0
  31. package/dist/finance-categories.controller.js +84 -0
  32. package/dist/finance-categories.controller.js.map +1 -0
  33. package/dist/finance-cost-centers.controller.d.ts +32 -0
  34. package/dist/finance-cost-centers.controller.d.ts.map +1 -0
  35. package/dist/finance-cost-centers.controller.js +72 -0
  36. package/dist/finance-cost-centers.controller.js.map +1 -0
  37. package/dist/finance-period-close.controller.d.ts +27 -0
  38. package/dist/finance-period-close.controller.d.ts.map +1 -0
  39. package/dist/finance-period-close.controller.js +64 -0
  40. package/dist/finance-period-close.controller.js.map +1 -0
  41. package/dist/finance.module.d.ts.map +1 -1
  42. package/dist/finance.module.js +8 -0
  43. package/dist/finance.module.js.map +1 -1
  44. package/dist/finance.service.d.ts +111 -0
  45. package/dist/finance.service.d.ts.map +1 -1
  46. package/dist/finance.service.js +446 -17
  47. package/dist/finance.service.js.map +1 -1
  48. package/dist/index.d.ts +4 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +4 -0
  51. package/dist/index.js.map +1 -1
  52. package/hedhog/data/route.yaml +108 -0
  53. package/hedhog/frontend/app/_components/person-field-with-create.tsx.ejs +627 -0
  54. package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +865 -883
  55. package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +838 -861
  56. package/hedhog/frontend/app/administration/audit-logs/page.tsx.ejs +309 -0
  57. package/hedhog/frontend/app/administration/categories/page.tsx.ejs +725 -0
  58. package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +378 -0
  59. package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +502 -0
  60. package/hedhog/frontend/messages/en.json +225 -0
  61. package/hedhog/frontend/messages/pt.json +225 -0
  62. package/package.json +5 -5
  63. package/src/dto/create-cost-center.dto.ts +9 -0
  64. package/src/dto/create-finance-category.dto.ts +21 -0
  65. package/src/dto/create-period-close.dto.ts +34 -0
  66. package/src/dto/move-finance-category.dto.ts +18 -0
  67. package/src/dto/update-cost-center.dto.ts +17 -0
  68. package/src/dto/update-finance-category.dto.ts +30 -0
  69. package/src/finance-audit-logs.controller.ts +30 -0
  70. package/src/finance-categories.controller.ts +52 -0
  71. package/src/finance-cost-centers.controller.ts +43 -0
  72. package/src/finance-period-close.controller.ts +34 -0
  73. package/src/finance.module.ts +8 -0
  74. package/src/finance.service.ts +578 -9
  75. package/src/index.ts +4 -0
@@ -572,6 +572,159 @@ let FinanceService = FinanceService_1 = class FinanceService {
572
572
  });
573
573
  return bankAccounts.map((bankAccount) => this.mapBankAccountToFront(bankAccount));
574
574
  }
575
+ async listCostCenters() {
576
+ const costCenters = await this.prisma.cost_center.findMany({
577
+ orderBy: [{ code: 'asc' }, { name: 'asc' }],
578
+ });
579
+ return costCenters.map((costCenter) => this.mapCostCenterToFront(costCenter));
580
+ }
581
+ async listAuditLogs(paginationParams, filters) {
582
+ var _a;
583
+ const actorUserId = (filters === null || filters === void 0 ? void 0 : filters.actor_user_id)
584
+ ? Number.parseInt(filters.actor_user_id, 10)
585
+ : undefined;
586
+ const fromDate = (filters === null || filters === void 0 ? void 0 : filters.from) ? new Date(filters.from) : undefined;
587
+ const toDate = (filters === null || filters === void 0 ? void 0 : filters.to) ? new Date(filters.to) : undefined;
588
+ const where = Object.assign(Object.assign(Object.assign(Object.assign({}, ((filters === null || filters === void 0 ? void 0 : filters.action) ? { action: filters.action } : {})), ((filters === null || filters === void 0 ? void 0 : filters.entity_table) ? { entity_table: filters.entity_table } : {})), (Number.isNaN(actorUserId) || !actorUserId
589
+ ? {}
590
+ : { actor_user_id: actorUserId })), ((fromDate || toDate) &&
591
+ !(fromDate && Number.isNaN(fromDate.getTime())) &&
592
+ !(toDate && Number.isNaN(toDate.getTime()))
593
+ ? {
594
+ created_at: Object.assign(Object.assign({}, (fromDate ? { gte: fromDate } : {})), (toDate ? { lte: toDate } : {})),
595
+ }
596
+ : {}));
597
+ const search = (_a = filters === null || filters === void 0 ? void 0 : filters.search) === null || _a === void 0 ? void 0 : _a.trim();
598
+ if (search) {
599
+ where.OR = [
600
+ { action: { contains: search, mode: 'insensitive' } },
601
+ { entity_table: { contains: search, mode: 'insensitive' } },
602
+ { entity_id: { contains: search, mode: 'insensitive' } },
603
+ { summary: { contains: search, mode: 'insensitive' } },
604
+ { ip_address: { contains: search, mode: 'insensitive' } },
605
+ ];
606
+ }
607
+ const paginated = await this.paginationService.paginatePrismaModel(this.prisma.audit_log, {
608
+ page: paginationParams === null || paginationParams === void 0 ? void 0 : paginationParams.page,
609
+ pageSize: paginationParams === null || paginationParams === void 0 ? void 0 : paginationParams.pageSize,
610
+ sortField: (paginationParams === null || paginationParams === void 0 ? void 0 : paginationParams.sortField) || 'created_at',
611
+ sortOrder: (paginationParams === null || paginationParams === void 0 ? void 0 : paginationParams.sortOrder) || 'desc',
612
+ validSortFields: [
613
+ 'id',
614
+ 'created_at',
615
+ 'action',
616
+ 'entity_table',
617
+ 'entity_id',
618
+ ],
619
+ where,
620
+ include: {
621
+ user: {
622
+ select: {
623
+ id: true,
624
+ name: true,
625
+ },
626
+ },
627
+ },
628
+ });
629
+ return Object.assign(Object.assign({}, paginated), { data: (paginated.data || []).map((log) => {
630
+ var _a, _b, _c;
631
+ return ({
632
+ id: String(log.id),
633
+ actorUserId: log.actor_user_id ? String(log.actor_user_id) : null,
634
+ actorName: ((_a = log.user) === null || _a === void 0 ? void 0 : _a.name) || '-',
635
+ actorEmail: '',
636
+ action: log.action,
637
+ entityTable: log.entity_table,
638
+ entityId: log.entity_id,
639
+ summary: log.summary || '',
640
+ ipAddress: log.ip_address || '',
641
+ beforeData: log.before_data || '',
642
+ afterData: log.after_data || '',
643
+ createdAt: ((_c = (_b = log.created_at) === null || _b === void 0 ? void 0 : _b.toISOString) === null || _c === void 0 ? void 0 : _c.call(_b)) || null,
644
+ });
645
+ }) });
646
+ }
647
+ async listFinanceCategories() {
648
+ const categories = await this.prisma.finance_category.findMany({
649
+ orderBy: [{ parent_id: 'asc' }, { updated_at: 'asc' }, { name: 'asc' }],
650
+ });
651
+ return categories.map((category) => this.mapFinanceCategoryToFront(category));
652
+ }
653
+ async listPeriodClose(paginationParams, filters) {
654
+ var _a, _b;
655
+ const fromDate = (filters === null || filters === void 0 ? void 0 : filters.from) ? new Date(filters.from) : undefined;
656
+ const toDate = (filters === null || filters === void 0 ? void 0 : filters.to) ? new Date(filters.to) : undefined;
657
+ const userNumericId = (filters === null || filters === void 0 ? void 0 : filters.user)
658
+ ? Number.parseInt(filters.user, 10)
659
+ : undefined;
660
+ const andConditions = [];
661
+ if ((filters === null || filters === void 0 ? void 0 : filters.status) && filters.status !== 'all') {
662
+ andConditions.push({ status: filters.status });
663
+ }
664
+ if ((fromDate && !Number.isNaN(fromDate.getTime())) ||
665
+ (toDate && !Number.isNaN(toDate.getTime()))) {
666
+ andConditions.push({
667
+ period_start: Object.assign({}, (fromDate && !Number.isNaN(fromDate.getTime())
668
+ ? { gte: fromDate }
669
+ : {})),
670
+ });
671
+ andConditions.push({
672
+ period_end: Object.assign({}, (toDate && !Number.isNaN(toDate.getTime()) ? { lte: toDate } : {})),
673
+ });
674
+ }
675
+ const search = (_a = filters === null || filters === void 0 ? void 0 : filters.search) === null || _a === void 0 ? void 0 : _a.trim();
676
+ if (search) {
677
+ andConditions.push({
678
+ OR: [
679
+ { notes: { contains: search, mode: 'insensitive' } },
680
+ { user: { is: { name: { contains: search, mode: 'insensitive' } } } },
681
+ {
682
+ user: { is: { email: { contains: search, mode: 'insensitive' } } },
683
+ },
684
+ ],
685
+ });
686
+ }
687
+ if ((_b = filters === null || filters === void 0 ? void 0 : filters.user) === null || _b === void 0 ? void 0 : _b.trim()) {
688
+ andConditions.push({
689
+ OR: [
690
+ ...(Number.isNaN(userNumericId) || !userNumericId
691
+ ? []
692
+ : [{ closed_by_user_id: userNumericId }]),
693
+ { user: { is: { name: { contains: filters.user, mode: 'insensitive' } } } },
694
+ {
695
+ user: {
696
+ is: { email: { contains: filters.user, mode: 'insensitive' } },
697
+ },
698
+ },
699
+ ],
700
+ });
701
+ }
702
+ const where = andConditions.length > 0 ? { AND: andConditions } : {};
703
+ const paginated = await this.paginationService.paginatePrismaModel(this.prisma.period_close, {
704
+ page: paginationParams === null || paginationParams === void 0 ? void 0 : paginationParams.page,
705
+ pageSize: paginationParams === null || paginationParams === void 0 ? void 0 : paginationParams.pageSize,
706
+ sortField: (paginationParams === null || paginationParams === void 0 ? void 0 : paginationParams.sortField) || 'period_start',
707
+ sortOrder: (paginationParams === null || paginationParams === void 0 ? void 0 : paginationParams.sortOrder) || 'desc',
708
+ validSortFields: [
709
+ 'id',
710
+ 'period_start',
711
+ 'period_end',
712
+ 'status',
713
+ 'closed_at',
714
+ 'created_at',
715
+ ],
716
+ where,
717
+ include: {
718
+ user: {
719
+ select: {
720
+ id: true,
721
+ name: true,
722
+ },
723
+ },
724
+ },
725
+ });
726
+ return Object.assign(Object.assign({}, paginated), { data: (paginated.data || []).map((period) => this.mapPeriodCloseToFront(period)) });
727
+ }
575
728
  async listBankStatements(bankAccountId) {
576
729
  const statements = await this.prisma.bank_statement_line.findMany({
577
730
  where: Object.assign({}, (bankAccountId ? { bank_account_id: bankAccountId } : {})),
@@ -644,6 +797,74 @@ let FinanceService = FinanceService_1 = class FinanceService {
644
797
  });
645
798
  return this.mapBankAccountToFront(account);
646
799
  }
800
+ async createCostCenter(data) {
801
+ const code = await this.generateCostCenterCode(data.name);
802
+ const created = await this.prisma.cost_center.create({
803
+ data: {
804
+ code,
805
+ name: data.name.trim(),
806
+ status: 'active',
807
+ },
808
+ });
809
+ return this.mapCostCenterToFront(created);
810
+ }
811
+ async createFinanceCategory(data) {
812
+ if (data.parent_id) {
813
+ await this.ensureFinanceCategoryExists(data.parent_id);
814
+ }
815
+ const created = await this.prisma.finance_category.create({
816
+ data: {
817
+ code: await this.generateFinanceCategoryCode(),
818
+ name: data.name.trim(),
819
+ kind: this.mapCategoryKindFromPt(data.kind),
820
+ status: 'active',
821
+ parent_id: data.parent_id || null,
822
+ },
823
+ });
824
+ return this.mapFinanceCategoryToFront(created);
825
+ }
826
+ async createPeriodClose(data, userId) {
827
+ var _a;
828
+ const periodStart = new Date(data.period_start);
829
+ const periodEnd = new Date(data.period_end);
830
+ if (Number.isNaN(periodStart.getTime()) ||
831
+ Number.isNaN(periodEnd.getTime())) {
832
+ throw new common_1.BadRequestException('Invalid period dates');
833
+ }
834
+ if (periodStart > periodEnd) {
835
+ throw new common_1.BadRequestException('period_start must be before period_end');
836
+ }
837
+ const overlapped = await this.prisma.period_close.findFirst({
838
+ where: {
839
+ period_start: { lte: periodEnd },
840
+ period_end: { gte: periodStart },
841
+ },
842
+ select: { id: true },
843
+ });
844
+ if (overlapped) {
845
+ throw new common_1.BadRequestException('There is already a period in this range');
846
+ }
847
+ const status = data.status || 'closed';
848
+ const created = await this.prisma.period_close.create({
849
+ data: {
850
+ period_start: periodStart,
851
+ period_end: periodEnd,
852
+ status,
853
+ closed_at: status === 'closed' ? new Date() : null,
854
+ closed_by_user_id: status === 'closed' ? userId || null : null,
855
+ notes: ((_a = data.notes) === null || _a === void 0 ? void 0 : _a.trim()) || null,
856
+ },
857
+ include: {
858
+ user: {
859
+ select: {
860
+ id: true,
861
+ name: true,
862
+ },
863
+ },
864
+ },
865
+ });
866
+ return this.mapPeriodCloseToFront(created);
867
+ }
647
868
  async updateBankAccount(id, data) {
648
869
  const current = await this.prisma.bank_account.findUnique({
649
870
  where: { id },
@@ -673,6 +894,82 @@ let FinanceService = FinanceService_1 = class FinanceService {
673
894
  });
674
895
  return this.mapBankAccountToFront(updated);
675
896
  }
897
+ async updateCostCenter(id, data) {
898
+ var _a;
899
+ const current = await this.prisma.cost_center.findUnique({
900
+ where: { id },
901
+ select: { id: true },
902
+ });
903
+ if (!current) {
904
+ throw new common_1.NotFoundException('Cost center not found');
905
+ }
906
+ const updated = await this.prisma.cost_center.update({
907
+ where: { id },
908
+ data: {
909
+ name: (_a = data.name) === null || _a === void 0 ? void 0 : _a.trim(),
910
+ status: data.status,
911
+ },
912
+ });
913
+ return this.mapCostCenterToFront(updated);
914
+ }
915
+ async updateFinanceCategory(id, data) {
916
+ var _a;
917
+ const current = await this.prisma.finance_category.findUnique({
918
+ where: { id },
919
+ select: { id: true },
920
+ });
921
+ if (!current) {
922
+ throw new common_1.NotFoundException('Finance category not found');
923
+ }
924
+ if (data.parent_id) {
925
+ await this.ensureFinanceCategoryExists(data.parent_id);
926
+ await this.ensureNoFinanceCategoryCycle(id, data.parent_id);
927
+ }
928
+ const updated = await this.prisma.finance_category.update({
929
+ where: { id },
930
+ data: {
931
+ name: (_a = data.name) === null || _a === void 0 ? void 0 : _a.trim(),
932
+ kind: data.kind ? this.mapCategoryKindFromPt(data.kind) : undefined,
933
+ parent_id: data.parent_id,
934
+ status: data.status,
935
+ },
936
+ });
937
+ return this.mapFinanceCategoryToFront(updated);
938
+ }
939
+ async moveFinanceCategory(id, data) {
940
+ var _a;
941
+ const current = await this.prisma.finance_category.findUnique({
942
+ where: { id },
943
+ select: { id: true, parent_id: true },
944
+ });
945
+ if (!current) {
946
+ throw new common_1.NotFoundException('Finance category not found');
947
+ }
948
+ const targetParentId = data.parent_id || null;
949
+ if (targetParentId) {
950
+ await this.ensureFinanceCategoryExists(targetParentId);
951
+ await this.ensureNoFinanceCategoryCycle(id, targetParentId);
952
+ }
953
+ await this.prisma.finance_category.update({
954
+ where: { id },
955
+ data: { parent_id: targetParentId },
956
+ });
957
+ const siblings = await this.prisma.finance_category.findMany({
958
+ where: { parent_id: targetParentId },
959
+ select: { id: true },
960
+ orderBy: [{ updated_at: 'asc' }, { name: 'asc' }],
961
+ });
962
+ const siblingIds = siblings.map((item) => item.id).filter((item) => item !== id);
963
+ const targetPosition = Math.max(0, Math.min((_a = data.position) !== null && _a !== void 0 ? _a : siblingIds.length, siblingIds.length));
964
+ siblingIds.splice(targetPosition, 0, id);
965
+ await this.prisma.$transaction(siblingIds.map((categoryId) => this.prisma.finance_category.update({
966
+ where: { id: categoryId },
967
+ data: {
968
+ parent_id: targetParentId,
969
+ },
970
+ })));
971
+ return { success: true };
972
+ }
676
973
  async deleteBankAccount(id) {
677
974
  const current = await this.prisma.bank_account.findUnique({
678
975
  where: { id },
@@ -689,6 +986,38 @@ let FinanceService = FinanceService_1 = class FinanceService {
689
986
  });
690
987
  return { success: true };
691
988
  }
989
+ async deleteCostCenter(id) {
990
+ const current = await this.prisma.cost_center.findUnique({
991
+ where: { id },
992
+ select: { id: true },
993
+ });
994
+ if (!current) {
995
+ throw new common_1.NotFoundException('Cost center not found');
996
+ }
997
+ await this.prisma.cost_center.update({
998
+ where: { id },
999
+ data: {
1000
+ status: 'inactive',
1001
+ },
1002
+ });
1003
+ return { success: true };
1004
+ }
1005
+ async deleteFinanceCategory(id) {
1006
+ const current = await this.prisma.finance_category.findUnique({
1007
+ where: { id },
1008
+ select: { id: true },
1009
+ });
1010
+ if (!current) {
1011
+ throw new common_1.NotFoundException('Finance category not found');
1012
+ }
1013
+ await this.prisma.finance_category.update({
1014
+ where: { id },
1015
+ data: {
1016
+ status: 'inactive',
1017
+ },
1018
+ });
1019
+ return { success: true };
1020
+ }
692
1021
  async listTitles(titleType, paginationParams, status) {
693
1022
  const prismaStatus = this.mapStatusFromPt(status);
694
1023
  const where = {
@@ -807,25 +1136,14 @@ let FinanceService = FinanceService_1 = class FinanceService {
807
1136
  }
808
1137
  async loadPeople() {
809
1138
  const people = await this.prisma.person.findMany({
810
- include: {
811
- document: {
812
- select: {
813
- value: true,
814
- },
815
- take: 1,
816
- },
817
- },
818
1139
  orderBy: { name: 'asc' },
819
1140
  });
820
- return people.map((person) => {
821
- var _a;
822
- return ({
823
- id: String(person.id),
824
- nome: person.name,
825
- tipo: 'ambos',
826
- documento: ((_a = person.document[0]) === null || _a === void 0 ? void 0 : _a.value) || '',
827
- });
828
- });
1141
+ return people.map((person) => ({
1142
+ id: String(person.id),
1143
+ nome: person.name,
1144
+ tipo: 'ambos',
1145
+ documento: '',
1146
+ }));
829
1147
  }
830
1148
  async loadCategories() {
831
1149
  const categories = await this.prisma.finance_category.findMany({
@@ -1047,6 +1365,70 @@ let FinanceService = FinanceService_1 = class FinanceService {
1047
1365
  ativo: bankAccount.status === 'active',
1048
1366
  };
1049
1367
  }
1368
+ mapCostCenterToFront(costCenter) {
1369
+ return {
1370
+ id: String(costCenter.id),
1371
+ codigo: costCenter.code,
1372
+ nome: costCenter.name,
1373
+ status: costCenter.status,
1374
+ ativo: costCenter.status === 'active',
1375
+ };
1376
+ }
1377
+ mapCategoryKindToPt(kind) {
1378
+ const kindMap = {
1379
+ revenue: 'receita',
1380
+ expense: 'despesa',
1381
+ transfer: 'transferencia',
1382
+ adjustment: 'ajuste',
1383
+ other: 'outro',
1384
+ };
1385
+ return kindMap[kind] || 'outro';
1386
+ }
1387
+ mapCategoryKindFromPt(kind) {
1388
+ const normalized = (kind || '').toLowerCase();
1389
+ const kindMap = {
1390
+ receita: 'revenue',
1391
+ revenue: 'revenue',
1392
+ despesa: 'expense',
1393
+ expense: 'expense',
1394
+ transferencia: 'transfer',
1395
+ transferência: 'transfer',
1396
+ transfer: 'transfer',
1397
+ ajuste: 'adjustment',
1398
+ adjustment: 'adjustment',
1399
+ outro: 'other',
1400
+ other: 'other',
1401
+ };
1402
+ return kindMap[normalized] || 'other';
1403
+ }
1404
+ mapFinanceCategoryToFront(category) {
1405
+ return {
1406
+ id: String(category.id),
1407
+ codigo: category.code,
1408
+ nome: category.name,
1409
+ parentId: category.parent_id ? String(category.parent_id) : null,
1410
+ natureza: this.mapCategoryKindToPt(category.kind),
1411
+ status: category.status,
1412
+ ativo: category.status === 'active',
1413
+ };
1414
+ }
1415
+ mapPeriodCloseToFront(period) {
1416
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1417
+ return {
1418
+ id: String(period.id),
1419
+ periodStart: ((_b = (_a = period.period_start) === null || _a === void 0 ? void 0 : _a.toISOString) === null || _b === void 0 ? void 0 : _b.call(_a)) || null,
1420
+ periodEnd: ((_d = (_c = period.period_end) === null || _c === void 0 ? void 0 : _c.toISOString) === null || _d === void 0 ? void 0 : _d.call(_c)) || null,
1421
+ status: period.status,
1422
+ closedAt: ((_f = (_e = period.closed_at) === null || _e === void 0 ? void 0 : _e.toISOString) === null || _f === void 0 ? void 0 : _f.call(_e)) || null,
1423
+ closedByUserId: period.closed_by_user_id
1424
+ ? String(period.closed_by_user_id)
1425
+ : null,
1426
+ closedByName: ((_g = period.user) === null || _g === void 0 ? void 0 : _g.name) || '-',
1427
+ closedByEmail: '',
1428
+ notes: period.notes || '',
1429
+ createdAt: ((_j = (_h = period.created_at) === null || _h === void 0 ? void 0 : _h.toISOString) === null || _j === void 0 ? void 0 : _j.call(_h)) || null,
1430
+ };
1431
+ }
1050
1432
  mapStatementStatusToPt(status) {
1051
1433
  const statusMap = {
1052
1434
  pending: 'pendente',
@@ -1067,6 +1449,53 @@ let FinanceService = FinanceService_1 = class FinanceService {
1067
1449
  .padStart(4, '0');
1068
1450
  return `${bankPrefix || 'ACC'}-${accountSuffix}`;
1069
1451
  }
1452
+ async generateCostCenterCode(name) {
1453
+ const sanitizedName = (name || 'CENTRO')
1454
+ .trim()
1455
+ .replace(/\s+/g, '-')
1456
+ .replace(/[^A-Za-z0-9-]/g, '')
1457
+ .toUpperCase();
1458
+ const basePrefix = sanitizedName.slice(0, 8) || 'CENTRO';
1459
+ const lastCostCenter = await this.prisma.cost_center.findFirst({
1460
+ orderBy: { id: 'desc' },
1461
+ select: { id: true },
1462
+ });
1463
+ const suffix = String(((lastCostCenter === null || lastCostCenter === void 0 ? void 0 : lastCostCenter.id) || 0) + 1).padStart(4, '0');
1464
+ return `${basePrefix}-${suffix}`;
1465
+ }
1466
+ async generateFinanceCategoryCode() {
1467
+ const lastCategory = await this.prisma.finance_category.findFirst({
1468
+ orderBy: { id: 'desc' },
1469
+ select: { id: true },
1470
+ });
1471
+ const suffix = String(((lastCategory === null || lastCategory === void 0 ? void 0 : lastCategory.id) || 0) + 1).padStart(4, '0');
1472
+ return `CAT-${suffix}`;
1473
+ }
1474
+ async ensureFinanceCategoryExists(id) {
1475
+ const category = await this.prisma.finance_category.findUnique({
1476
+ where: { id },
1477
+ select: { id: true },
1478
+ });
1479
+ if (!category) {
1480
+ throw new common_1.NotFoundException('Finance category not found');
1481
+ }
1482
+ }
1483
+ async ensureNoFinanceCategoryCycle(categoryId, targetParentId) {
1484
+ if (categoryId === targetParentId) {
1485
+ throw new common_1.BadRequestException('Category cannot be parent of itself');
1486
+ }
1487
+ let currentParentId = targetParentId;
1488
+ while (currentParentId) {
1489
+ if (currentParentId === categoryId) {
1490
+ throw new common_1.BadRequestException('Invalid parent category hierarchy');
1491
+ }
1492
+ const parent = await this.prisma.finance_category.findUnique({
1493
+ where: { id: currentParentId },
1494
+ select: { parent_id: true },
1495
+ });
1496
+ currentParentId = (parent === null || parent === void 0 ? void 0 : parent.parent_id) || null;
1497
+ }
1498
+ }
1070
1499
  toCents(value) {
1071
1500
  return Math.round(value * 100);
1072
1501
  }