gemcap-be-common 1.2.140 → 1.3.0

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 (303) hide show
  1. package/classes/bank-transaction-item.d.ts +17 -0
  2. package/classes/bank-transaction-item.js +64 -0
  3. package/classes/bank-transaction-item.ts +66 -0
  4. package/classes/bank-uploaded-transaction.d.ts +17 -0
  5. package/classes/bank-uploaded-transaction.js +35 -0
  6. package/classes/bank-uploaded-transaction.ts +35 -0
  7. package/classes/inventory-item.d.ts +41 -0
  8. package/classes/inventory-item.js +44 -0
  9. package/classes/inventory-item.ts +63 -0
  10. package/classes/payable-account-item.d.ts +22 -0
  11. package/classes/payable-account-item.js +27 -0
  12. package/classes/payable-account-item.ts +35 -0
  13. package/classes/quickbook-item.d.ts +37 -0
  14. package/classes/quickbook-item.js +51 -0
  15. package/classes/quickbook-item.ts +59 -0
  16. package/classes/receivable-item.d.ts +26 -0
  17. package/classes/receivable-item.js +28 -0
  18. package/classes/receivable-item.ts +38 -0
  19. package/constants/date-formats.contsants.d.ts +1 -0
  20. package/constants/date-formats.contsants.js +4 -0
  21. package/constants/date-formats.contsants.ts +1 -0
  22. package/db/brokers.db.d.ts +185 -0
  23. package/db/brokers.db.js +35 -2
  24. package/db/brokers.db.ts +34 -1
  25. package/db/collateral-adjustments.db.d.ts +34 -0
  26. package/db/collateral-adjustments.db.js +52 -0
  27. package/db/collateral-adjustments.db.ts +54 -0
  28. package/db/collaterals.db.d.ts +1 -1
  29. package/db/equipment.db.d.ts +40 -0
  30. package/db/equipment.db.js +55 -0
  31. package/db/equipment.db.ts +56 -0
  32. package/db/financial-spreading.db.ts +2 -1
  33. package/db/groups.d.ts +5 -0
  34. package/db/groups.js +57 -0
  35. package/db/groups.ts +52 -0
  36. package/db/inventories.d.ts +91 -0
  37. package/db/inventories.js +449 -0
  38. package/db/inventories.ts +481 -0
  39. package/db/inventory-availability.d.ts +3 -0
  40. package/db/inventory-availability.js +103 -0
  41. package/db/inventory-availability.ts +113 -0
  42. package/db/new-summary.d.ts +31 -0
  43. package/db/new-summary.js +1295 -0
  44. package/db/new-summary.ts +1509 -0
  45. package/db/payable-accounts.d.ts +30 -0
  46. package/db/payable-accounts.js +55 -0
  47. package/db/payable-accounts.ts +50 -0
  48. package/db/reserve.db.d.ts +34 -0
  49. package/db/reserve.db.js +52 -0
  50. package/db/reserve.db.ts +48 -0
  51. package/db/uploads.db.d.ts +2 -0
  52. package/db/uploads.db.js +29 -0
  53. package/db/uploads.db.ts +24 -0
  54. package/helpers/main.helper.d.ts +31 -0
  55. package/helpers/main.helper.js +63 -0
  56. package/helpers/main.helper.ts +63 -0
  57. package/models/AccountPayableItem.model.d.ts +6 -6
  58. package/models/AllocatedBankTransaction.model.d.ts +54 -0
  59. package/models/AllocatedBankTransaction.model.js +70 -0
  60. package/models/AllocatedBankTransaction.model.ts +94 -0
  61. package/models/AllocatedData.model.d.ts +33 -0
  62. package/models/AllocatedData.model.js +19 -0
  63. package/models/AllocatedData.model.ts +24 -0
  64. package/models/BBCDate.model.d.ts +3 -3
  65. package/models/BBCSheet.model.d.ts +3 -3
  66. package/models/Banks.model.d.ts +3 -3
  67. package/models/Borrower.model.d.ts +3 -3
  68. package/models/BorrowerData.model.d.ts +3 -3
  69. package/models/BorrowerDataInsurance.model.d.ts +3 -3
  70. package/models/BorrowerDataTerm.model.d.ts +3 -3
  71. package/models/BorrowerSummary.model.js +1 -1
  72. package/models/BorrowerSummary.model.ts +1 -1
  73. package/models/CalandarDay.model.d.ts +40 -0
  74. package/models/CalandarDay.model.js +47 -0
  75. package/models/CalandarDay.model.ts +61 -0
  76. package/models/CashAllocationProduct.model.d.ts +119 -0
  77. package/models/CashAllocationProduct.model.js +102 -0
  78. package/models/CashAllocationProduct.model.ts +112 -0
  79. package/models/CashAllocationReference.model.d.ts +37 -0
  80. package/models/CashAllocationReference.model.js +27 -0
  81. package/models/CashAllocationReference.model.ts +40 -0
  82. package/models/CollateralAdjustment.model.d.ts +51 -0
  83. package/models/CollateralAdjustment.model.js +61 -0
  84. package/models/CollateralAdjustment.model.ts +98 -0
  85. package/models/Company.model.d.ts +35 -0
  86. package/models/Company.model.js +18 -0
  87. package/models/Company.model.ts +29 -0
  88. package/models/CustomerAPGroup.model.d.ts +32 -0
  89. package/models/CustomerAPGroup.model.js +24 -0
  90. package/models/CustomerAPGroup.model.ts +31 -0
  91. package/models/Equipment.model.d.ts +53 -0
  92. package/models/Equipment.model.js +140 -0
  93. package/models/Equipment.model.ts +172 -0
  94. package/models/FinancialCompliance.model.d.ts +39 -0
  95. package/models/FinancialCompliance.model.js +64 -0
  96. package/models/FinancialCompliance.model.ts +78 -0
  97. package/models/FinancialComplianceBorrower.model.d.ts +58 -0
  98. package/models/FinancialComplianceBorrower.model.js +82 -0
  99. package/models/FinancialComplianceBorrower.model.ts +118 -0
  100. package/models/FinancialIndexes.model.d.ts +36 -0
  101. package/models/FinancialIndexes.model.js +27 -0
  102. package/models/FinancialIndexes.model.ts +37 -0
  103. package/models/Inventory.model.d.ts +18 -18
  104. package/models/InventoryAvailability.model.d.ts +21 -21
  105. package/models/InventoryAvailabilityItem.model.d.ts +6 -6
  106. package/models/InventoryItem.model.d.ts +24 -24
  107. package/models/InventoryManualEntry.model.d.ts +9 -9
  108. package/models/InventorySeasonalRates.model.d.ts +3 -3
  109. package/models/LoanBroker.model.d.ts +3 -3
  110. package/models/LoanCharges.model.d.ts +12 -12
  111. package/models/LoanProducts.model.d.ts +9 -9
  112. package/models/LoanStatementStatus.model.d.ts +35 -0
  113. package/models/LoanStatementStatus.model.js +34 -0
  114. package/models/LoanStatementStatus.model.ts +45 -0
  115. package/models/LoanStatementTransaction.model.d.ts +9 -9
  116. package/models/LoanTransactionFile.model.d.ts +41 -0
  117. package/models/LoanTransactionFile.model.js +44 -0
  118. package/models/LoanTransactionFile.model.ts +61 -0
  119. package/models/MappedGroup.model.d.ts +37 -0
  120. package/models/MappedGroup.model.js +33 -0
  121. package/models/MappedGroup.model.ts +46 -0
  122. package/models/MonthEndData.Model.d.ts +41 -0
  123. package/models/MonthEndData.Model.js +42 -0
  124. package/models/MonthEndData.Model.ts +53 -0
  125. package/models/OrganizationEmails.model.d.ts +44 -0
  126. package/models/OrganizationEmails.model.js +40 -0
  127. package/models/OrganizationEmails.model.ts +54 -0
  128. package/models/ProductBroker.model.d.ts +9 -9
  129. package/models/QuickbooksAccount.model.d.ts +39 -0
  130. package/models/QuickbooksAccount.model.js +43 -0
  131. package/models/QuickbooksAccount.model.ts +57 -0
  132. package/models/Receivable.model.d.ts +12 -12
  133. package/models/ReceivableAvailability.model.d.ts +54 -54
  134. package/models/ReceivableAvailabilityItem.model.d.ts +57 -57
  135. package/models/ReceivableItem.model.d.ts +6 -6
  136. package/models/Reserve.model.d.ts +51 -0
  137. package/models/Reserve.model.js +96 -0
  138. package/models/Reserve.model.ts +125 -0
  139. package/models/TermLoan.model.d.ts +3 -3
  140. package/models/TermLoanCalculated.model.d.ts +6 -6
  141. package/models/TransactionAttachedFile.Model.d.ts +35 -0
  142. package/models/TransactionAttachedFile.Model.js +37 -0
  143. package/models/TransactionAttachedFile.Model.ts +48 -0
  144. package/models/UploadedBankTransaction.model.d.ts +56 -0
  145. package/models/UploadedBankTransaction.model.js +78 -0
  146. package/models/UploadedBankTransaction.model.ts +110 -0
  147. package/models/UploadedData.model.d.ts +36 -0
  148. package/models/UploadedData.model.js +23 -0
  149. package/models/UploadedData.model.ts +35 -0
  150. package/models/UploadedFile.model.d.ts +40 -0
  151. package/models/UploadedFile.model.js +41 -0
  152. package/models/UploadedFile.model.ts +57 -0
  153. package/models/UploadedSheet.model.d.ts +46 -0
  154. package/models/UploadedSheet.model.js +27 -0
  155. package/models/UploadedSheet.model.ts +51 -0
  156. package/package.json +10 -1
  157. package/repositories/globals.repository.d.ts +8 -0
  158. package/repositories/globals.repository.js +24 -0
  159. package/repositories/globals.repository.ts +21 -0
  160. package/services/attached-files.service.d.ts +57 -0
  161. package/services/attached-files.service.js +103 -0
  162. package/services/attached-files.service.ts +123 -0
  163. package/services/availability.service.d.ts +77 -0
  164. package/services/availability.service.js +897 -0
  165. package/services/availability.service.ts +1034 -0
  166. package/services/bank-uploaded-transactions.service.d.ts +33 -0
  167. package/services/bank-uploaded-transactions.service.js +430 -0
  168. package/services/bank-uploaded-transactions.service.ts +475 -0
  169. package/services/banks.service.d.ts +36 -0
  170. package/services/banks.service.js +91 -0
  171. package/services/banks.service.ts +95 -0
  172. package/services/borrower-summary.service.d.ts +35 -0
  173. package/services/borrower-summary.service.js +310 -0
  174. package/services/borrower-summary.service.ts +334 -0
  175. package/services/borrowers.service.d.ts +103 -0
  176. package/services/borrowers.service.js +268 -0
  177. package/services/borrowers.service.ts +302 -0
  178. package/services/brokers.service.d.ts +212 -0
  179. package/services/brokers.service.js +160 -0
  180. package/services/brokers.service.ts +200 -0
  181. package/services/calendar.service.d.ts +53 -0
  182. package/services/calendar.service.js +108 -0
  183. package/services/calendar.service.ts +128 -0
  184. package/services/cash-allocation.service.d.ts +40 -0
  185. package/services/cash-allocation.service.js +92 -0
  186. package/services/cash-allocation.service.ts +105 -0
  187. package/services/collateral-adjustments.service.d.ts +38 -0
  188. package/services/collateral-adjustments.service.js +82 -0
  189. package/services/collateral-adjustments.service.ts +95 -0
  190. package/services/collaterals.service.d.ts +69 -0
  191. package/services/collaterals.service.js +279 -0
  192. package/services/collaterals.service.ts +319 -0
  193. package/services/companies.service.d.ts +5 -0
  194. package/services/companies.service.js +21 -0
  195. package/services/companies.service.ts +23 -0
  196. package/services/compliance-borrowers.service.d.ts +152 -0
  197. package/services/compliance-borrowers.service.js +569 -0
  198. package/services/compliance-borrowers.service.ts +617 -0
  199. package/services/equipment.service.d.ts +42 -0
  200. package/services/equipment.service.js +120 -0
  201. package/services/equipment.service.ts +149 -0
  202. package/services/file-manager.service.d.ts +44 -0
  203. package/services/file-manager.service.js +120 -0
  204. package/services/file-manager.service.ts +146 -0
  205. package/services/financial-compliance.service.d.ts +58 -0
  206. package/services/financial-compliance.service.js +281 -0
  207. package/services/financial-compliance.service.ts +309 -0
  208. package/services/financial-indexes.service.d.ts +20 -0
  209. package/services/financial-indexes.service.js +241 -0
  210. package/services/financial-indexes.service.ts +257 -0
  211. package/services/financial-spreading.service.d.ts +74 -0
  212. package/services/financial-spreading.service.js +450 -0
  213. package/services/financial-spreading.service.ts +517 -0
  214. package/services/globals.service.d.ts +5 -0
  215. package/services/globals.service.js +11 -0
  216. package/services/globals.service.ts +8 -0
  217. package/services/groups.service.d.ts +39 -0
  218. package/services/groups.service.js +65 -0
  219. package/services/groups.service.ts +64 -0
  220. package/services/inventory-availability.service.d.ts +13 -0
  221. package/services/inventory-availability.service.js +170 -0
  222. package/services/inventory-availability.service.ts +187 -0
  223. package/services/inventory.service.d.ts +118 -0
  224. package/services/inventory.service.js +239 -0
  225. package/services/inventory.service.ts +276 -0
  226. package/services/loan-charges.service.d.ts +83 -0
  227. package/services/loan-charges.service.js +343 -0
  228. package/services/loan-charges.service.ts +396 -0
  229. package/services/loan-payments.service.d.ts +94 -0
  230. package/services/loan-payments.service.js +485 -0
  231. package/services/loan-payments.service.ts +541 -0
  232. package/services/loan-products.service.d.ts +12 -0
  233. package/services/loan-products.service.js +55 -0
  234. package/services/loan-products.service.ts +58 -0
  235. package/services/loan-statement-balance.service.d.ts +16 -0
  236. package/services/loan-statement-balance.service.js +106 -0
  237. package/services/loan-statement-balance.service.ts +113 -0
  238. package/services/loan-statement-effects.service.d.ts +8 -0
  239. package/services/loan-statement-effects.service.js +42 -0
  240. package/services/loan-statement-effects.service.ts +41 -0
  241. package/services/loan-statement-status.service.d.ts +208 -0
  242. package/services/loan-statement-status.service.js +159 -0
  243. package/services/loan-statement-status.service.ts +177 -0
  244. package/services/loan-statement.service.d.ts +186 -0
  245. package/services/loan-statement.service.js +935 -0
  246. package/services/loan-statement.service.ts +1040 -0
  247. package/services/loan-transactions.service.d.ts +169 -0
  248. package/services/loan-transactions.service.js +941 -0
  249. package/services/loan-transactions.service.ts +1042 -0
  250. package/services/lock.service.d.ts +6 -0
  251. package/services/lock.service.js +45 -0
  252. package/services/lock.service.ts +45 -0
  253. package/services/manual-entry.service.d.ts +20 -0
  254. package/services/manual-entry.service.js +186 -0
  255. package/services/manual-entry.service.ts +201 -0
  256. package/services/month-end-data.service.d.ts +34 -0
  257. package/services/month-end-data.service.js +30 -0
  258. package/services/month-end-data.service.ts +35 -0
  259. package/services/nodemailer.service.d.ts +96 -0
  260. package/services/nodemailer.service.js +689 -0
  261. package/services/nodemailer.service.ts +774 -0
  262. package/services/organization-emails.service.d.ts +31 -0
  263. package/services/organization-emails.service.js +10 -0
  264. package/services/organization-emails.service.ts +7 -0
  265. package/services/organizations.service.d.ts +34 -0
  266. package/services/organizations.service.js +74 -0
  267. package/services/organizations.service.ts +84 -0
  268. package/services/pdf.service.d.ts +61 -0
  269. package/services/pdf.service.js +547 -0
  270. package/services/pdf.service.ts +642 -0
  271. package/services/quickbooks.service.d.ts +99 -0
  272. package/services/quickbooks.service.js +640 -0
  273. package/services/quickbooks.service.ts +734 -0
  274. package/services/reports/investor-summary.service.d.ts +28 -0
  275. package/services/reports/investor-summary.service.js +136 -0
  276. package/services/reports/investor-summary.service.ts +159 -0
  277. package/services/reports.service.d.ts +126 -0
  278. package/services/reports.service.js +584 -0
  279. package/services/reports.service.ts +702 -0
  280. package/services/reserve.service.d.ts +37 -0
  281. package/services/reserve.service.js +76 -0
  282. package/services/reserve.service.ts +79 -0
  283. package/services/sentry.service.d.ts +11 -0
  284. package/services/sentry.service.js +49 -0
  285. package/services/sentry.service.ts +33 -0
  286. package/services/signs.service.d.ts +69 -0
  287. package/services/signs.service.js +230 -0
  288. package/services/signs.service.ts +260 -0
  289. package/services/term-loan.service.d.ts +30 -0
  290. package/services/term-loan.service.js +614 -0
  291. package/services/term-loan.service.ts +696 -0
  292. package/services/uploads.service.d.ts +134 -0
  293. package/services/uploads.service.js +587 -0
  294. package/services/uploads.service.ts +643 -0
  295. package/services/user-logs.service.d.ts +23 -0
  296. package/services/user-logs.service.js +160 -0
  297. package/services/user-logs.service.ts +177 -0
  298. package/services/users.service.d.ts +4 -4
  299. package/services/yield.service.d.ts +46 -0
  300. package/services/yield.service.js +42 -12
  301. package/services/yield.service.ts +38 -8
  302. package/tsconfig.json +5 -5
  303. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,734 @@
1
+ import mongoose from 'mongoose';
2
+ import dayjs from 'dayjs';
3
+ import Decimal from 'decimal.js';
4
+ import _ from 'lodash';
5
+
6
+ import { IProductBrokerDocWithBroker } from '../models/ProductBroker.model';
7
+ import { ELoanChargeType } from '../enums/loan-charge-type.enum';
8
+ import { ELoanTypes } from '../enums/loan-types.enum';
9
+ import { LoanProduct } from '../models/LoanProducts.model';
10
+ import { BorrowerModel } from '../models/Borrower.model';
11
+ import { ETermLoanStatus, TermLoanModel } from '../models/TermLoan.model';
12
+ import { EChargeType } from '../db/reports.db';
13
+ import { IBankView } from '../models/Banks.model';
14
+
15
+ import { IQuickbooksAccount, QuickbooksAccount } from '../models/QuickbooksAccount.model';
16
+ import { IQuickbooksUploadItem } from '../classes/quickbook-item';
17
+ import { IUploadedBankTransaction } from '../models/UploadedBankTransaction.model';
18
+ import { BanksService } from './banks.service';
19
+ import { BankUploadedTransactionsService } from './bank-uploaded-transactions.service';
20
+ import { BorrowerService } from './borrowers.service';
21
+ import { BrokersService } from './brokers.service';
22
+ import { CashAllocationService } from './cash-allocation.service';
23
+ import { CompaniesService } from './companies.service';
24
+ import { LoanChargesService } from './loan-charges.service';
25
+ import { LoanPaymentsService } from './loan-payments.service';
26
+
27
+ export type QuickBookReportType = 'accrual' | 'payment' | 'cash';
28
+
29
+ interface IQBReportTransaction {
30
+ service: string;
31
+ id: string;
32
+ type: string;
33
+ date: string;
34
+ account: string;
35
+ class: string;
36
+ amount: number | string;
37
+ document: string;
38
+ memo: string;
39
+ productId?: string;
40
+ location?: string;
41
+ }
42
+
43
+ interface IQBOReportTransaction {
44
+ journalNumber: string;
45
+ journalDate: string;
46
+ memo: string;
47
+ account: string;
48
+ amount: number | string;
49
+ description: string;
50
+ name: string;
51
+ location: string;
52
+ class: string;
53
+ }
54
+
55
+
56
+ interface IUploadedBankTransactionWithMemo extends IUploadedBankTransaction {
57
+ splitMemo: string;
58
+ }
59
+
60
+ const headersIIF: IQBReportTransaction[] = [
61
+ {
62
+ service: '!TRNS',
63
+ id: 'TRNSID',
64
+ type: 'TRNSTYPE',
65
+ date: 'DATE',
66
+ account: 'ACCNT',
67
+ class: 'CLASS',
68
+ amount: 'AMOUNT',
69
+ document: 'DOCNUM',
70
+ memo: 'MEMO',
71
+ },
72
+ {
73
+ service: '!SPL',
74
+ id: 'SPLID',
75
+ type: 'TRNSTYPE',
76
+ date: 'DATE',
77
+ account: 'ACCNT',
78
+ class: 'CLASS',
79
+ amount: 'AMOUNT',
80
+ document: 'DOCNUM',
81
+ memo: 'MEMO',
82
+ },
83
+ {
84
+ service: '!ENDTRNS',
85
+ id: '',
86
+ type: '',
87
+ date: '',
88
+ account: '',
89
+ class: '',
90
+ amount: '',
91
+ document: '',
92
+ memo: '',
93
+ },
94
+ ];
95
+
96
+ const IIF: IQBReportTransaction[] = [
97
+ {
98
+ service: 'ENDTRNS',
99
+ id: '',
100
+ type: '',
101
+ date: '',
102
+ account: '',
103
+ class: '',
104
+ amount: '',
105
+ document: '',
106
+ memo: '',
107
+ },
108
+ ];
109
+
110
+ const headersCSV = (): IQBOReportTransaction => {
111
+ return {
112
+ journalNumber: 'Journal No',
113
+ journalDate: 'Journal Date',
114
+ memo: 'Memo',
115
+ account: 'Account',
116
+ amount: 'Amount',
117
+ description: 'Description',
118
+ name: 'Name',
119
+ location: 'Location',
120
+ class: 'Class',
121
+ };
122
+ };
123
+
124
+ const transactionsDateFormat = 'DDMMMYYYY';
125
+ const IIFDateFormat = 'M/D/YYYY';
126
+
127
+ export class QuickbooksService {
128
+
129
+ constructor(
130
+ private readonly banksService: BanksService,
131
+ private readonly bankUploadedTransactionsService: BankUploadedTransactionsService,
132
+ private readonly borrowerService: BorrowerService,
133
+ private readonly brokersService: BrokersService,
134
+ private readonly cashAllocationService: CashAllocationService,
135
+ private readonly companiesService: CompaniesService,
136
+ private readonly loanChargesService: LoanChargesService,
137
+ private readonly loanPaymentsService: LoanPaymentsService,
138
+ ) {
139
+ }
140
+
141
+ async uploadAccounts(accounts: IQuickbooksUploadItem[], deleteOld: boolean) {
142
+ const accountsWithId = accounts.map((account) => ({ _id: 'new_', ...account }));
143
+ await this.saveAccounts(accountsWithId, deleteOld);
144
+ }
145
+
146
+ async getAllAccounts() {
147
+ return QuickbooksAccount
148
+ .find({}, { createdAt: 0, updatedAt: 0, __v: 0 })
149
+ .sort({ order: 1 }).lean();
150
+ }
151
+
152
+ async saveAccounts(accounts: (IQuickbooksAccount & { _id: string })[], deleteOld: boolean) {
153
+ if (!accounts.length) {
154
+ return;
155
+ }
156
+ if (deleteOld) {
157
+ const companyId = accounts[0].companyId;
158
+ // console.log({ companyId });
159
+ // const existingAccounts = await QuickbooksAccount.find({ companyId: new mongoose.Types.ObjectId(companyId) });
160
+ // console.log({ existingAccounts });
161
+ const accountIds = accounts
162
+ .filter((account) => !account._id.startsWith('new_'))
163
+ .map((account) => new mongoose.Types.ObjectId(account._id));
164
+ await QuickbooksAccount.deleteMany({ _id: { $nin: accountIds }, companyId });
165
+ }
166
+
167
+ const accountSavePromises = accounts.map(async (account, order) => {
168
+ if (account._id.startsWith('new_')) {
169
+ const { _id, ...accountWithoutId } = account;
170
+ return QuickbooksAccount.create({ ...accountWithoutId, order });
171
+ } else {
172
+ return QuickbooksAccount.findByIdAndUpdate(account._id, { ...account, order }, { new: true, upsert: true });
173
+ }
174
+ });
175
+ return Promise.all(accountSavePromises);
176
+ }
177
+
178
+ async getAllProducts() {
179
+ const borrowers = await this.borrowerService.getActiveBorrowers();
180
+ const borrowerIds = borrowers.map((borrower) => borrower._id);
181
+ return LoanProduct.aggregate([
182
+ {
183
+ '$match': {
184
+ borrowerId: { $in: borrowerIds },
185
+ },
186
+ }, {
187
+ $lookup: {
188
+ from: 'borrowers',
189
+ localField: 'borrowerId',
190
+ foreignField: '_id',
191
+ as: 'borrower',
192
+ },
193
+ }, {
194
+ $unwind: {
195
+ path: '$borrower',
196
+ },
197
+ }, {
198
+ $project: {
199
+ '_id': 0,
200
+ 'data': {
201
+ $mergeObjects: [
202
+ {
203
+ $arrayToObject: {
204
+ $filter: {
205
+ input: { $objectToArray: '$$ROOT' },
206
+ cond: { $in: ['$$this.k', ['name', '_id', 'type']] },
207
+ },
208
+ },
209
+ }, {
210
+ 'clientName': '$borrower.name',
211
+ 'productAccrualStatus': '$borrower.accrualStatus',
212
+ },
213
+ ],
214
+ },
215
+ },
216
+ }, {
217
+ $replaceRoot: {
218
+ 'newRoot': '$data',
219
+ },
220
+ }, {
221
+ $project: {
222
+ '_id': 0,
223
+ 'productId': '$_id',
224
+ 'productName': '$name',
225
+ 'productType': '$type',
226
+ 'clientName': 1,
227
+ 'productAccrualStatus': 1,
228
+ },
229
+ }, {
230
+ $sort: {
231
+ 'clientName': 1,
232
+ 'order': 1,
233
+ },
234
+ }, {
235
+ $addFields: {
236
+ 'isSelected': null,
237
+ },
238
+ }, {
239
+ $lookup: {
240
+ from: 'term_loans',
241
+ let: {
242
+ productIdVar: '$productId',
243
+ productTypeVar: '$productType',
244
+ },
245
+ pipeline: [
246
+ {
247
+ $match: {
248
+ $expr: {
249
+ $and: [
250
+ {
251
+ $eq: [
252
+ '$productId',
253
+ '$$productIdVar',
254
+ ],
255
+ },
256
+ {
257
+ $ne: [
258
+ '$$productTypeVar',
259
+ 'REVOLVER',
260
+ ],
261
+ },
262
+ {
263
+ $eq: ['$actual', true],
264
+ },
265
+ ],
266
+ },
267
+ },
268
+ },
269
+ ],
270
+ as: 'term',
271
+ },
272
+ }, {
273
+ $unwind: {
274
+ path: '$term',
275
+ preserveNullAndEmptyArrays: true,
276
+ },
277
+ }, {
278
+ $addFields: {
279
+ selectable: {
280
+ $cond: {
281
+ if: '$term',
282
+ then: {
283
+ $eq: [
284
+ '$term.calculationStatus',
285
+ 'CALCULATED',
286
+ ],
287
+ },
288
+ else: true,
289
+ },
290
+ },
291
+ },
292
+ },
293
+ ]);
294
+ }
295
+
296
+ async getChargeTransactionsForAccrual(productIds: string[], reportDate: Date) {
297
+ if (productIds.length === 0) {
298
+ return [];
299
+ }
300
+ const transactionsFilter = {
301
+ productIds: productIds,
302
+ start: dayjs(reportDate).utcOffset(0).startOf('month').toDate(),
303
+ end: dayjs(reportDate).utcOffset(0).endOf('month').toDate(),
304
+ };
305
+ return await this.loanChargesService.getLoanStatementsForProduct(transactionsFilter);
306
+ }
307
+
308
+ async getChargeTransactionsForPayments(productIds: string[], reportDate: Date) {
309
+ const start = dayjs(reportDate).utcOffset(0).startOf('day').toDate();
310
+ const end = dayjs(reportDate).utcOffset(0).endOf('day').toDate();
311
+ const payments = await this.loanPaymentsService.getPaymentsForPeriod(productIds, start, end);
312
+ if (!payments.length) {
313
+ return [];
314
+ }
315
+ const transactionIds = payments.reduce((acc, payment) => [...acc, ...payment.paid.map((p) => p.statementId)], []);
316
+ const transactionsFilter = { productIds, transactionIds, start, end };
317
+ return await this.loanChargesService.getLoanStatementsForIdsAndCharge(transactionsFilter);
318
+ }
319
+
320
+ async getPrincipalTransactionsForPayments(productId: string, reportDate: Date) {
321
+ const start = dayjs(reportDate).utcOffset(0).startOf('day').toDate();
322
+ const end = dayjs(reportDate).utcOffset(0).endOf('day').toDate();
323
+ const payments = await this.loanPaymentsService.getPaymentsForPeriod([productId], start, end);
324
+ if (!payments.length) {
325
+ return <{ [settlementCode: string]: number }>{};
326
+ }
327
+ return payments.reduce((acc, payment) => {
328
+ const settlementCode = payment.settlementCode ? payment.settlementCode : null;
329
+ const amount = payment.loanTransactions.reduce((acc, transaction) => new Decimal(acc).add(transaction.amount).toNumber(), 0);
330
+ return {
331
+ ...acc,
332
+ [settlementCode]: amount,
333
+ };
334
+ }, <{ [settlementCode: string]: number }>{});
335
+ }
336
+
337
+ async getQuickbooksReport(params: {
338
+ reportType: QuickBookReportType,
339
+ productIds: string[],
340
+ chargeTypes: string[],
341
+ reportDate: Date,
342
+ format: 'IIF' | 'CSV',
343
+ }) {
344
+ const reportErrorsSet = new Set<string>();
345
+ await Promise.all(params.productIds.map(async (productId) => {
346
+ const product = await this.loanChargesService.getLoanProductById(productId);
347
+ if (product.type === ELoanTypes.TERM) {
348
+ const termLoan = await TermLoanModel.findOne({ productId, actual: true }).lean();
349
+ if (termLoan && termLoan.calculationStatus !== ETermLoanStatus.CALCULATED) {
350
+ reportErrorsSet.add(`${product.name} is not calculated`);
351
+ }
352
+ }
353
+ }));
354
+ if (reportErrorsSet.size > 0) {
355
+ return { reportData: [], reportErrors: Array.from(reportErrorsSet) };
356
+ }
357
+
358
+ const { reportType, productIds, chargeTypes, reportDate } = params;
359
+ const reportDateFormatted = dayjs(reportDate).utcOffset(0).format('MM/DD/YYYY').toUpperCase();
360
+ const reportDateTrFormatted = dayjs(reportDate).utcOffset(0).format(transactionsDateFormat).toUpperCase();
361
+ const currentDateTrFormatted = dayjs(new Date).utcOffset(0).format(transactionsDateFormat).toUpperCase();
362
+ const isAccrual = reportType === 'accrual';
363
+ const mainData: IQBReportTransaction[] = [];
364
+
365
+ const trTemplate: IQBReportTransaction = {
366
+ service: 'SPL',
367
+ id: '',
368
+ type: 'GENERAL JOURNAL',
369
+ date: reportDateFormatted,
370
+ account: '',
371
+ class: '',
372
+ amount: 0,
373
+ document: '',
374
+ memo: '',
375
+ };
376
+
377
+ await Promise.all(productIds.map(async (productId) => {
378
+ const product = await this.loanChargesService.getLoanProductById(productId);
379
+ const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
380
+ const filteredCharges = charges.filter((charge) => chargeTypes.includes(charge.chargeType));
381
+ const productBrokers = await this.brokersService.getProductBrokers(productId);
382
+ const borrower = await BorrowerModel.findById(product.borrowerId).lean();
383
+
384
+ const allTransactions = isAccrual
385
+ ? await this.getChargeTransactionsForAccrual([productId], reportDate)
386
+ : await this.getChargeTransactionsForPayments([productId], reportDate);
387
+
388
+ await Promise.all(filteredCharges.map(async (charge) => {
389
+ const chargeTransactions = allTransactions.filter((tr) => tr.chargeId.toString() === charge._id.toString());
390
+ if (!chargeTransactions.length) {
391
+ return;
392
+ }
393
+
394
+ const totalAmountGroups = isAccrual
395
+ ? { [charge.PLCode]: chargeTransactions.reduce((acc, tr) => new Decimal(acc).add(tr.amount).toNumber(), 0) }
396
+ : chargeTransactions.reduce((acc, tr) => {
397
+ if (tr.settlementCode) {
398
+ return {
399
+ ...acc,
400
+ [tr.settlementCode]: new Decimal(acc[tr.settlementCode] || 0).add(tr.amount).toNumber(),
401
+ };
402
+ }
403
+ return {
404
+ ...acc,
405
+ [charge.PLCode]: new Decimal(acc[charge.PLCode] || 0).add(tr.amount).toNumber(),
406
+ };
407
+ }, <{ [PLCode: string]: number }>{});
408
+
409
+ await Promise.all(Object.entries(totalAmountGroups).map(async ([PLCode, totalAmountValue]) => {
410
+ if (totalAmountValue === 0) {
411
+ return;
412
+ }
413
+ let totalAmount = product.isParticipant ? -totalAmountValue : totalAmountValue;
414
+ totalAmount = isAccrual ? totalAmount : -totalAmount;
415
+ const quickbooksAccountBS = await QuickbooksAccount.findOne({ accountCode: charge.code }).lean();
416
+ const quickbooksAccountPL = await QuickbooksAccount.findOne({ accountCode: PLCode }).lean();
417
+ if (!quickbooksAccountBS || !quickbooksAccountPL) {
418
+ if (!quickbooksAccountBS) {
419
+ reportErrorsSet.add(charge.code);
420
+ }
421
+ if (!quickbooksAccountPL) {
422
+ reportErrorsSet.add(PLCode);
423
+ }
424
+ return;
425
+ }
426
+ const tr: IQBReportTransaction = {
427
+ ...trTemplate,
428
+ productId,
429
+ class: borrower.code,
430
+ memo: `${borrower.code} ${product.isParticipant ? 'PARTICIPANT' : ''} ${EChargeType[charge.chargeType].toUpperCase()} ${product.type.toUpperCase()} - TRANS: ${reportDateTrFormatted} - POST: ${currentDateTrFormatted}`,
431
+ };
432
+
433
+ tr.account = quickbooksAccountBS.fullName;
434
+ tr.amount = totalAmount;
435
+ mainData.push({ ...tr });
436
+
437
+ tr.account = quickbooksAccountPL.fullName;
438
+ tr.amount = -totalAmount;
439
+ mainData.push({ ...tr });
440
+
441
+ if (chargeTypes.includes('BROKERS') && isAccrual) {
442
+
443
+ const handleFee = async (broker: IProductBrokerDocWithBroker, shareName: 'adminShare' | 'interestShare' | 'otherShare') => {
444
+
445
+ const quickbooksAccountBrokerBS = await QuickbooksAccount.findOne({ accountCode: broker.BSCode }).lean();
446
+ const quickbooksAccountBrokerPL = await QuickbooksAccount.findOne({ accountCode: broker.PLCode }).lean();
447
+ if (!quickbooksAccountBrokerBS || !quickbooksAccountBrokerPL) {
448
+ if (!quickbooksAccountBrokerBS) {
449
+ reportErrorsSet.add(broker.BSCode);
450
+ }
451
+ if (!quickbooksAccountBrokerPL) {
452
+ reportErrorsSet.add(broker.PLCode);
453
+ }
454
+ return;
455
+ }
456
+
457
+ const tr: IQBReportTransaction = {
458
+ ...trTemplate,
459
+ productId,
460
+ class: borrower.code,
461
+ amount: new Decimal(-totalAmount).mul(broker[shareName]).toDP(2).toNumber(),
462
+ memo: `${borrower.code} ${EChargeType[charge.chargeType].toUpperCase()} BROKER ${broker.order + 1}`,
463
+ };
464
+
465
+ tr.account = quickbooksAccountBrokerBS.fullName;
466
+ mainData.push({ ...tr });
467
+
468
+ tr.account = quickbooksAccountBrokerPL.fullName;
469
+ tr.amount = -tr.amount;
470
+ mainData.push({ ...tr });
471
+ };
472
+
473
+ const handlers = {
474
+ [ELoanChargeType.ADMIN_FEE]: async (broker: IProductBrokerDocWithBroker) => await handleFee(broker, 'adminShare'),
475
+ [ELoanChargeType.INTEREST_FEE]: async (broker: IProductBrokerDocWithBroker) => await handleFee(broker, 'interestShare'),
476
+ [ELoanChargeType.OTHER]: async (broker: IProductBrokerDocWithBroker) => await handleFee(broker, 'otherShare'),
477
+ };
478
+ await Promise.all(productBrokers.map(async (broker) => {
479
+ if (handlers[charge.chargeType]) {
480
+ await handlers[charge.chargeType](broker);
481
+ }
482
+ }));
483
+
484
+ }
485
+ }));
486
+ }));
487
+
488
+ const principalPayments = isAccrual || product.type === ELoanTypes.REVOLVER
489
+ ? {}
490
+ : await this.getPrincipalTransactionsForPayments(productId, reportDate);
491
+
492
+ await Promise.all(Object.entries(principalPayments).map(async ([settlementCode, amount]) => {
493
+ const tr: IQBReportTransaction = {
494
+ ...trTemplate,
495
+ productId,
496
+ class: borrower.code,
497
+ amount: product.isParticipant ? +amount : -+amount,
498
+ memo: `${borrower.code} ${product.isParticipant ? 'PARTICIPANT' : ''} PRINCIPAL PAID TERM - TRANS: ${reportDateTrFormatted} - POST: ${currentDateTrFormatted}`,
499
+ };
500
+
501
+ if (settlementCode === 'null') {
502
+ const products = await this.loanChargesService.getLoanProducts(borrower._id.toString());
503
+ const revolverProduct = products.find((product) => product.type === ELoanTypes.REVOLVER);
504
+ if (revolverProduct) {
505
+ settlementCode = revolverProduct.code;
506
+ }
507
+ }
508
+
509
+ const quickbooksAccountPaymentBS = await QuickbooksAccount.findOne({ accountCode: product.code }).lean();
510
+ const quickbooksAccountPaymentPL = await QuickbooksAccount.findOne({ accountCode: settlementCode }).lean();
511
+ if (!quickbooksAccountPaymentBS || !quickbooksAccountPaymentPL) {
512
+ if (!quickbooksAccountPaymentBS) {
513
+ reportErrorsSet.add(product.code);
514
+ }
515
+ if (!quickbooksAccountPaymentPL) {
516
+ reportErrorsSet.add(settlementCode);
517
+ }
518
+ return;
519
+ }
520
+ tr.account = quickbooksAccountPaymentBS.fullName;
521
+ tr.amount = -tr.amount;
522
+ mainData.push({ ...tr });
523
+
524
+ tr.amount = -tr.amount;
525
+ tr.account = quickbooksAccountPaymentPL.fullName;
526
+ mainData.push({ ...tr });
527
+ }));
528
+ }));
529
+
530
+ const groupedData = _.groupBy(mainData, (obj) => obj.service + obj.id + obj.type + obj.date + obj.account + obj.class + obj.document + obj.memo + obj.productId);
531
+
532
+ const mergedData: IQBReportTransaction[] = Object.keys(groupedData).reduce((acc, key) => {
533
+ const group = groupedData[key];
534
+ const mergedObject: IQBReportTransaction = {
535
+ account: group[0].account,
536
+ class: group[0].class,
537
+ date: group[0].date,
538
+ document: group[0].document,
539
+ memo: group[0].memo,
540
+ service: group[0].service,
541
+ type: group[0].type,
542
+ id: group[0].id,
543
+ amount: group.reduce((acc, curr) => new Decimal(acc).add(+curr.amount).toNumber(), 0),
544
+ };
545
+ return [...acc, mergedObject];
546
+ }, <IQBReportTransaction[]>[]);
547
+
548
+ const reportData = (_.orderBy(mergedData, ['class', 'memo', 'account'], ['asc', 'asc', 'asc'])).filter((record) => record.amount !== 0);
549
+ if (reportData.length > 0) {
550
+ reportData[0].service = 'TRNS';
551
+ }
552
+
553
+ return { reportData, reportErrors: Array.from(reportErrorsSet).map((code) => `No account for ${code}`) };
554
+ }
555
+
556
+ async getQuickbooksCashReport(params: {
557
+ reportType: QuickBookReportType,
558
+ productIds: string[],
559
+ chargeTypes: string[],
560
+ reportDate: Date,
561
+ format: 'IIF' | 'CSV',
562
+ companyId: string,
563
+ }) {
564
+ const reportErrorsSet = new Set<string>();
565
+ const { reportType, productIds, chargeTypes, reportDate } = params;
566
+ const reportDateFormatted = dayjs(reportDate).utcOffset(0).format('MM/DD/YYYY').toUpperCase();
567
+ const reportDateTrFormatted = dayjs(reportDate).utcOffset(0).format(transactionsDateFormat).toUpperCase();
568
+
569
+ const startDate = dayjs(reportDate).utcOffset(0).startOf('day').toDate();
570
+ const endDate = dayjs(reportDate).utcOffset(0).endOf('day').toDate();
571
+ const transactions = await this.bankUploadedTransactionsService.getUploadedBankTransactions(startDate, endDate);
572
+ const { references, products } = await this.cashAllocationService.getAllCashAllocations();
573
+ const allAccounts = await this.getAllAccounts();
574
+ const companyAccounts = allAccounts.filter((acc) => acc.companyId?.toString() === params.companyId);
575
+ const banks = await this.banksService.getBanks();
576
+ const banksMap = new Map<string, IBankView>();
577
+ banks.forEach((bank) => banksMap.set(bank.bankAccountNumber, bank));
578
+
579
+ const reportData = transactions
580
+ .reduce((acc, transaction) => {
581
+ // if (!transaction.isConverted) { // TODO only for converted?
582
+ // return acc;
583
+ // }
584
+ if (transaction.splitTransactions.length) {
585
+ const restTransaction: IUploadedBankTransactionWithMemo = { ...transaction, splitMemo: '' };
586
+ const splitTransactions: IUploadedBankTransactionWithMemo[] = transaction.splitTransactions.map((splitTransaction) => {
587
+ restTransaction.amount = new Decimal(restTransaction.amount).sub(splitTransaction.amount).toNumber();
588
+ return {
589
+ ...transaction,
590
+ reference: splitTransaction.reference,
591
+ amount: splitTransaction.amount,
592
+ splitMemo: splitTransaction.memo ?? '',
593
+ };
594
+ });
595
+ if (restTransaction.amount !== 0) {
596
+ return [...acc, restTransaction, ...splitTransactions];
597
+ }
598
+ return [...acc, ...splitTransactions];
599
+ }
600
+ return [...acc, { ...transaction, splitMemo: '' }];
601
+ }, <IUploadedBankTransactionWithMemo[]>[])
602
+ .reduce((acc, transaction) => {
603
+ const foundReference = references.find((ref) => transaction.reference.toLowerCase().includes(ref.reference.toLowerCase()));
604
+ if (!foundReference || !foundReference.cashAllocationProductId) {
605
+ return acc;
606
+ }
607
+ const foundProduct = products.find((product) => product._id.toString() === foundReference.cashAllocationProductId.toString());
608
+ const accounts = foundProduct.accounts[params.companyId];
609
+ if (!accounts) {
610
+ return acc;
611
+ }
612
+ const findAccountForBank = (): IQuickbooksAccount => {
613
+ const foundBank = banksMap.get(transaction.accountNumber);
614
+ if (!foundBank) {
615
+ return null;
616
+ }
617
+ const accountCodeId = foundBank.ledgerAccountCodes[params.companyId];
618
+ if (!accountCodeId) {
619
+ return null;
620
+ }
621
+ const account = companyAccounts.find((account) => account._id.toString() === accountCodeId.toString());
622
+ return account;
623
+ };
624
+ if (!accounts.accountId1 || !accounts.accountId2) {
625
+ return acc;
626
+ }
627
+ const account1 = accounts.accountId1 === 'BANK'
628
+ ? findAccountForBank()
629
+ : companyAccounts.find((acc) => acc._id.toString() === accounts.accountId1.toString());
630
+ if (!account1) {
631
+ return acc;
632
+ }
633
+ const account2 = accounts.accountId2 === 'BANK'
634
+ ? findAccountForBank()
635
+ : companyAccounts.find((acc) => acc._id.toString() === accounts.accountId2.toString());
636
+ if (!account2) {
637
+ return acc;
638
+ }
639
+ const reportTransactions: Omit<IQBReportTransaction, 'account' | 'memo'> = {
640
+ type: 'GENERAL JOURNAL',
641
+ date: dayjs(transaction.date).format(IIFDateFormat),
642
+ class: foundProduct.class,
643
+ amount: transaction.amount,
644
+ document: '',
645
+ location: foundProduct.location,
646
+ id: '',
647
+ service: 'SPL',
648
+ };
649
+ const reportTransactions1: IQBReportTransaction = {
650
+ ...reportTransactions,
651
+ account: account1?.fullName ?? 'BANK',
652
+ amount: -reportTransactions.amount,
653
+ memo: +reportTransactions.amount > 0 ? foundProduct.DRMemo : foundProduct.CRMemo,
654
+ };
655
+ const reportTransactions2: IQBReportTransaction = {
656
+ ...reportTransactions,
657
+ account: account2?.fullName ?? 'BANK',
658
+ amount: reportTransactions.amount,
659
+ memo: +reportTransactions.amount > 0 ? foundProduct.DRMemo : foundProduct.CRMemo,
660
+ };
661
+ return [...acc, reportTransactions1, reportTransactions2];
662
+ }, <IQBReportTransaction[]>[]);
663
+
664
+ if (reportData.length > 0) {
665
+ reportData[0].service = 'TRNS';
666
+ }
667
+
668
+ return { reportData, reportErrors: Array.from(reportErrorsSet).map((code) => `No account for ${code}`) };
669
+ }
670
+
671
+ convertQuickbooksReport(reportData: IQBReportTransaction[], format: 'IIF' | 'CSV', memo: string) {
672
+ switch (format) {
673
+ case 'IIF': {
674
+ const fullReport = [...headersIIF, ...reportData, ...IIF];
675
+
676
+ let iifContent = '';
677
+ const getReportString = (transaction: IQBReportTransaction) => {
678
+ const fields = [
679
+ transaction.service,
680
+ transaction.id,
681
+ transaction.type,
682
+ transaction.date,
683
+ transaction.account,
684
+ transaction.class,
685
+ transaction.amount,
686
+ transaction.document,
687
+ transaction.memo,
688
+ ];
689
+ const joinedString = fields.join('\t');
690
+ iifContent += `${joinedString}\n`;
691
+ };
692
+
693
+ fullReport.forEach(getReportString);
694
+ return iifContent;
695
+ }
696
+ case 'CSV': {
697
+ const ref = `REF${Math.floor(Math.random() * (999 - 100 + 1)) + 100}`;
698
+ const qboReport: IQBOReportTransaction[] = reportData.map((transaction) => ({
699
+ journalNumber: `${transaction.date} ${ref}`,
700
+ journalDate: transaction.date,
701
+ memo,
702
+ account: transaction.account,
703
+ amount: transaction.amount,
704
+ description: transaction.memo,
705
+ name: '',
706
+ location: transaction.location,
707
+ class: transaction.class,
708
+ }));
709
+ const fullReport = [headersCSV(), ...qboReport];
710
+ return JSON.stringify({ fullReport });
711
+ }
712
+ }
713
+ }
714
+
715
+ async reassignAccounts() {
716
+ const companies = await this.companiesService.getCompanies();
717
+ const accounts = await QuickbooksAccount.find().lean();
718
+ for (const [index, company] of companies.entries()) {
719
+ if (index === 0) {
720
+ await Promise.all(accounts.map(async (account) => {
721
+ await QuickbooksAccount.findByIdAndUpdate(account._id, { companyId: company._id });
722
+ }));
723
+ } else {
724
+ await Promise.all(accounts.map(async (account) => {
725
+ const { _id, ...rest } = account;
726
+ console.log(rest);
727
+ const newAccount = new QuickbooksAccount({ ...rest });
728
+ newAccount.companyId = company._id;
729
+ await newAccount.save();
730
+ }));
731
+ }
732
+ }
733
+ }
734
+ }