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.
- package/classes/bank-transaction-item.d.ts +17 -0
- package/classes/bank-transaction-item.js +64 -0
- package/classes/bank-transaction-item.ts +66 -0
- package/classes/bank-uploaded-transaction.d.ts +17 -0
- package/classes/bank-uploaded-transaction.js +35 -0
- package/classes/bank-uploaded-transaction.ts +35 -0
- package/classes/inventory-item.d.ts +41 -0
- package/classes/inventory-item.js +44 -0
- package/classes/inventory-item.ts +63 -0
- package/classes/payable-account-item.d.ts +22 -0
- package/classes/payable-account-item.js +27 -0
- package/classes/payable-account-item.ts +35 -0
- package/classes/quickbook-item.d.ts +37 -0
- package/classes/quickbook-item.js +51 -0
- package/classes/quickbook-item.ts +59 -0
- package/classes/receivable-item.d.ts +26 -0
- package/classes/receivable-item.js +28 -0
- package/classes/receivable-item.ts +38 -0
- package/constants/date-formats.contsants.d.ts +1 -0
- package/constants/date-formats.contsants.js +4 -0
- package/constants/date-formats.contsants.ts +1 -0
- package/db/brokers.db.d.ts +185 -0
- package/db/brokers.db.js +35 -2
- package/db/brokers.db.ts +34 -1
- package/db/collateral-adjustments.db.d.ts +34 -0
- package/db/collateral-adjustments.db.js +52 -0
- package/db/collateral-adjustments.db.ts +54 -0
- package/db/collaterals.db.d.ts +1 -1
- package/db/equipment.db.d.ts +40 -0
- package/db/equipment.db.js +55 -0
- package/db/equipment.db.ts +56 -0
- package/db/financial-spreading.db.ts +2 -1
- package/db/groups.d.ts +5 -0
- package/db/groups.js +57 -0
- package/db/groups.ts +52 -0
- package/db/inventories.d.ts +91 -0
- package/db/inventories.js +449 -0
- package/db/inventories.ts +481 -0
- package/db/inventory-availability.d.ts +3 -0
- package/db/inventory-availability.js +103 -0
- package/db/inventory-availability.ts +113 -0
- package/db/new-summary.d.ts +31 -0
- package/db/new-summary.js +1295 -0
- package/db/new-summary.ts +1509 -0
- package/db/payable-accounts.d.ts +30 -0
- package/db/payable-accounts.js +55 -0
- package/db/payable-accounts.ts +50 -0
- package/db/reserve.db.d.ts +34 -0
- package/db/reserve.db.js +52 -0
- package/db/reserve.db.ts +48 -0
- package/db/uploads.db.d.ts +2 -0
- package/db/uploads.db.js +29 -0
- package/db/uploads.db.ts +24 -0
- package/helpers/main.helper.d.ts +31 -0
- package/helpers/main.helper.js +63 -0
- package/helpers/main.helper.ts +63 -0
- package/models/AccountPayableItem.model.d.ts +6 -6
- package/models/AllocatedBankTransaction.model.d.ts +54 -0
- package/models/AllocatedBankTransaction.model.js +70 -0
- package/models/AllocatedBankTransaction.model.ts +94 -0
- package/models/AllocatedData.model.d.ts +33 -0
- package/models/AllocatedData.model.js +19 -0
- package/models/AllocatedData.model.ts +24 -0
- package/models/BBCDate.model.d.ts +3 -3
- package/models/BBCSheet.model.d.ts +3 -3
- package/models/Banks.model.d.ts +3 -3
- package/models/Borrower.model.d.ts +3 -3
- package/models/BorrowerData.model.d.ts +3 -3
- package/models/BorrowerDataInsurance.model.d.ts +3 -3
- package/models/BorrowerDataTerm.model.d.ts +3 -3
- package/models/BorrowerSummary.model.js +1 -1
- package/models/BorrowerSummary.model.ts +1 -1
- package/models/CalandarDay.model.d.ts +40 -0
- package/models/CalandarDay.model.js +47 -0
- package/models/CalandarDay.model.ts +61 -0
- package/models/CashAllocationProduct.model.d.ts +119 -0
- package/models/CashAllocationProduct.model.js +102 -0
- package/models/CashAllocationProduct.model.ts +112 -0
- package/models/CashAllocationReference.model.d.ts +37 -0
- package/models/CashAllocationReference.model.js +27 -0
- package/models/CashAllocationReference.model.ts +40 -0
- package/models/CollateralAdjustment.model.d.ts +51 -0
- package/models/CollateralAdjustment.model.js +61 -0
- package/models/CollateralAdjustment.model.ts +98 -0
- package/models/Company.model.d.ts +35 -0
- package/models/Company.model.js +18 -0
- package/models/Company.model.ts +29 -0
- package/models/CustomerAPGroup.model.d.ts +32 -0
- package/models/CustomerAPGroup.model.js +24 -0
- package/models/CustomerAPGroup.model.ts +31 -0
- package/models/Equipment.model.d.ts +53 -0
- package/models/Equipment.model.js +140 -0
- package/models/Equipment.model.ts +172 -0
- package/models/FinancialCompliance.model.d.ts +39 -0
- package/models/FinancialCompliance.model.js +64 -0
- package/models/FinancialCompliance.model.ts +78 -0
- package/models/FinancialComplianceBorrower.model.d.ts +58 -0
- package/models/FinancialComplianceBorrower.model.js +82 -0
- package/models/FinancialComplianceBorrower.model.ts +118 -0
- package/models/FinancialIndexes.model.d.ts +36 -0
- package/models/FinancialIndexes.model.js +27 -0
- package/models/FinancialIndexes.model.ts +37 -0
- package/models/Inventory.model.d.ts +18 -18
- package/models/InventoryAvailability.model.d.ts +21 -21
- package/models/InventoryAvailabilityItem.model.d.ts +6 -6
- package/models/InventoryItem.model.d.ts +24 -24
- package/models/InventoryManualEntry.model.d.ts +9 -9
- package/models/InventorySeasonalRates.model.d.ts +3 -3
- package/models/LoanBroker.model.d.ts +3 -3
- package/models/LoanCharges.model.d.ts +12 -12
- package/models/LoanProducts.model.d.ts +9 -9
- package/models/LoanStatementStatus.model.d.ts +35 -0
- package/models/LoanStatementStatus.model.js +34 -0
- package/models/LoanStatementStatus.model.ts +45 -0
- package/models/LoanStatementTransaction.model.d.ts +9 -9
- package/models/LoanTransactionFile.model.d.ts +41 -0
- package/models/LoanTransactionFile.model.js +44 -0
- package/models/LoanTransactionFile.model.ts +61 -0
- package/models/MappedGroup.model.d.ts +37 -0
- package/models/MappedGroup.model.js +33 -0
- package/models/MappedGroup.model.ts +46 -0
- package/models/MonthEndData.Model.d.ts +41 -0
- package/models/MonthEndData.Model.js +42 -0
- package/models/MonthEndData.Model.ts +53 -0
- package/models/OrganizationEmails.model.d.ts +44 -0
- package/models/OrganizationEmails.model.js +40 -0
- package/models/OrganizationEmails.model.ts +54 -0
- package/models/ProductBroker.model.d.ts +9 -9
- package/models/QuickbooksAccount.model.d.ts +39 -0
- package/models/QuickbooksAccount.model.js +43 -0
- package/models/QuickbooksAccount.model.ts +57 -0
- package/models/Receivable.model.d.ts +12 -12
- package/models/ReceivableAvailability.model.d.ts +54 -54
- package/models/ReceivableAvailabilityItem.model.d.ts +57 -57
- package/models/ReceivableItem.model.d.ts +6 -6
- package/models/Reserve.model.d.ts +51 -0
- package/models/Reserve.model.js +96 -0
- package/models/Reserve.model.ts +125 -0
- package/models/TermLoan.model.d.ts +3 -3
- package/models/TermLoanCalculated.model.d.ts +6 -6
- package/models/TransactionAttachedFile.Model.d.ts +35 -0
- package/models/TransactionAttachedFile.Model.js +37 -0
- package/models/TransactionAttachedFile.Model.ts +48 -0
- package/models/UploadedBankTransaction.model.d.ts +56 -0
- package/models/UploadedBankTransaction.model.js +78 -0
- package/models/UploadedBankTransaction.model.ts +110 -0
- package/models/UploadedData.model.d.ts +36 -0
- package/models/UploadedData.model.js +23 -0
- package/models/UploadedData.model.ts +35 -0
- package/models/UploadedFile.model.d.ts +40 -0
- package/models/UploadedFile.model.js +41 -0
- package/models/UploadedFile.model.ts +57 -0
- package/models/UploadedSheet.model.d.ts +46 -0
- package/models/UploadedSheet.model.js +27 -0
- package/models/UploadedSheet.model.ts +51 -0
- package/package.json +10 -1
- package/repositories/globals.repository.d.ts +8 -0
- package/repositories/globals.repository.js +24 -0
- package/repositories/globals.repository.ts +21 -0
- package/services/attached-files.service.d.ts +57 -0
- package/services/attached-files.service.js +103 -0
- package/services/attached-files.service.ts +123 -0
- package/services/availability.service.d.ts +77 -0
- package/services/availability.service.js +897 -0
- package/services/availability.service.ts +1034 -0
- package/services/bank-uploaded-transactions.service.d.ts +33 -0
- package/services/bank-uploaded-transactions.service.js +430 -0
- package/services/bank-uploaded-transactions.service.ts +475 -0
- package/services/banks.service.d.ts +36 -0
- package/services/banks.service.js +91 -0
- package/services/banks.service.ts +95 -0
- package/services/borrower-summary.service.d.ts +35 -0
- package/services/borrower-summary.service.js +310 -0
- package/services/borrower-summary.service.ts +334 -0
- package/services/borrowers.service.d.ts +103 -0
- package/services/borrowers.service.js +268 -0
- package/services/borrowers.service.ts +302 -0
- package/services/brokers.service.d.ts +212 -0
- package/services/brokers.service.js +160 -0
- package/services/brokers.service.ts +200 -0
- package/services/calendar.service.d.ts +53 -0
- package/services/calendar.service.js +108 -0
- package/services/calendar.service.ts +128 -0
- package/services/cash-allocation.service.d.ts +40 -0
- package/services/cash-allocation.service.js +92 -0
- package/services/cash-allocation.service.ts +105 -0
- package/services/collateral-adjustments.service.d.ts +38 -0
- package/services/collateral-adjustments.service.js +82 -0
- package/services/collateral-adjustments.service.ts +95 -0
- package/services/collaterals.service.d.ts +69 -0
- package/services/collaterals.service.js +279 -0
- package/services/collaterals.service.ts +319 -0
- package/services/companies.service.d.ts +5 -0
- package/services/companies.service.js +21 -0
- package/services/companies.service.ts +23 -0
- package/services/compliance-borrowers.service.d.ts +152 -0
- package/services/compliance-borrowers.service.js +569 -0
- package/services/compliance-borrowers.service.ts +617 -0
- package/services/equipment.service.d.ts +42 -0
- package/services/equipment.service.js +120 -0
- package/services/equipment.service.ts +149 -0
- package/services/file-manager.service.d.ts +44 -0
- package/services/file-manager.service.js +120 -0
- package/services/file-manager.service.ts +146 -0
- package/services/financial-compliance.service.d.ts +58 -0
- package/services/financial-compliance.service.js +281 -0
- package/services/financial-compliance.service.ts +309 -0
- package/services/financial-indexes.service.d.ts +20 -0
- package/services/financial-indexes.service.js +241 -0
- package/services/financial-indexes.service.ts +257 -0
- package/services/financial-spreading.service.d.ts +74 -0
- package/services/financial-spreading.service.js +450 -0
- package/services/financial-spreading.service.ts +517 -0
- package/services/globals.service.d.ts +5 -0
- package/services/globals.service.js +11 -0
- package/services/globals.service.ts +8 -0
- package/services/groups.service.d.ts +39 -0
- package/services/groups.service.js +65 -0
- package/services/groups.service.ts +64 -0
- package/services/inventory-availability.service.d.ts +13 -0
- package/services/inventory-availability.service.js +170 -0
- package/services/inventory-availability.service.ts +187 -0
- package/services/inventory.service.d.ts +118 -0
- package/services/inventory.service.js +239 -0
- package/services/inventory.service.ts +276 -0
- package/services/loan-charges.service.d.ts +83 -0
- package/services/loan-charges.service.js +343 -0
- package/services/loan-charges.service.ts +396 -0
- package/services/loan-payments.service.d.ts +94 -0
- package/services/loan-payments.service.js +485 -0
- package/services/loan-payments.service.ts +541 -0
- package/services/loan-products.service.d.ts +12 -0
- package/services/loan-products.service.js +55 -0
- package/services/loan-products.service.ts +58 -0
- package/services/loan-statement-balance.service.d.ts +16 -0
- package/services/loan-statement-balance.service.js +106 -0
- package/services/loan-statement-balance.service.ts +113 -0
- package/services/loan-statement-effects.service.d.ts +8 -0
- package/services/loan-statement-effects.service.js +42 -0
- package/services/loan-statement-effects.service.ts +41 -0
- package/services/loan-statement-status.service.d.ts +208 -0
- package/services/loan-statement-status.service.js +159 -0
- package/services/loan-statement-status.service.ts +177 -0
- package/services/loan-statement.service.d.ts +186 -0
- package/services/loan-statement.service.js +935 -0
- package/services/loan-statement.service.ts +1040 -0
- package/services/loan-transactions.service.d.ts +169 -0
- package/services/loan-transactions.service.js +941 -0
- package/services/loan-transactions.service.ts +1042 -0
- package/services/lock.service.d.ts +6 -0
- package/services/lock.service.js +45 -0
- package/services/lock.service.ts +45 -0
- package/services/manual-entry.service.d.ts +20 -0
- package/services/manual-entry.service.js +186 -0
- package/services/manual-entry.service.ts +201 -0
- package/services/month-end-data.service.d.ts +34 -0
- package/services/month-end-data.service.js +30 -0
- package/services/month-end-data.service.ts +35 -0
- package/services/nodemailer.service.d.ts +96 -0
- package/services/nodemailer.service.js +689 -0
- package/services/nodemailer.service.ts +774 -0
- package/services/organization-emails.service.d.ts +31 -0
- package/services/organization-emails.service.js +10 -0
- package/services/organization-emails.service.ts +7 -0
- package/services/organizations.service.d.ts +34 -0
- package/services/organizations.service.js +74 -0
- package/services/organizations.service.ts +84 -0
- package/services/pdf.service.d.ts +61 -0
- package/services/pdf.service.js +547 -0
- package/services/pdf.service.ts +642 -0
- package/services/quickbooks.service.d.ts +99 -0
- package/services/quickbooks.service.js +640 -0
- package/services/quickbooks.service.ts +734 -0
- package/services/reports/investor-summary.service.d.ts +28 -0
- package/services/reports/investor-summary.service.js +136 -0
- package/services/reports/investor-summary.service.ts +159 -0
- package/services/reports.service.d.ts +126 -0
- package/services/reports.service.js +584 -0
- package/services/reports.service.ts +702 -0
- package/services/reserve.service.d.ts +37 -0
- package/services/reserve.service.js +76 -0
- package/services/reserve.service.ts +79 -0
- package/services/sentry.service.d.ts +11 -0
- package/services/sentry.service.js +49 -0
- package/services/sentry.service.ts +33 -0
- package/services/signs.service.d.ts +69 -0
- package/services/signs.service.js +230 -0
- package/services/signs.service.ts +260 -0
- package/services/term-loan.service.d.ts +30 -0
- package/services/term-loan.service.js +614 -0
- package/services/term-loan.service.ts +696 -0
- package/services/uploads.service.d.ts +134 -0
- package/services/uploads.service.js +587 -0
- package/services/uploads.service.ts +643 -0
- package/services/user-logs.service.d.ts +23 -0
- package/services/user-logs.service.js +160 -0
- package/services/user-logs.service.ts +177 -0
- package/services/users.service.d.ts +4 -4
- package/services/yield.service.d.ts +46 -0
- package/services/yield.service.js +42 -12
- package/services/yield.service.ts +38 -8
- package/tsconfig.json +5 -5
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,1034 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import mongoose from 'mongoose';
|
|
4
|
+
import Decimal from 'decimal.js';
|
|
5
|
+
|
|
6
|
+
import { isValidId, normalizeObject } from '../helpers/mongo.helper';
|
|
7
|
+
import {
|
|
8
|
+
ReceivableAvailabilityModel,
|
|
9
|
+
ELimitSettings,
|
|
10
|
+
IReceivableAvailability,
|
|
11
|
+
IReceivableAvailabilityDoc,
|
|
12
|
+
IAvailabilityLimits,
|
|
13
|
+
IAvailabilitySettings,
|
|
14
|
+
IAvailabilitySummary,
|
|
15
|
+
IReceivableAvailabilityView,
|
|
16
|
+
} from '../models/ReceivableAvailability.model';
|
|
17
|
+
import { IUserDocument } from '../models/User.model';
|
|
18
|
+
import { roundToXDigits } from '../helpers/numbers.helper';
|
|
19
|
+
import { AccountPayableItemModel } from '../models/AccountPayableItem.model';
|
|
20
|
+
import { ECollaterals } from '../enums/collaterals.enum';
|
|
21
|
+
import { IReceivableItem, IReceivableItemDoc } from '../models/ReceivableItem.model';
|
|
22
|
+
import {
|
|
23
|
+
ReceivableAvailabilityItemModel,
|
|
24
|
+
IReceivableAvailabilityItem,
|
|
25
|
+
IReceivableAvailabilityItemView,
|
|
26
|
+
} from '../models/ReceivableAvailabilityItem.model';
|
|
27
|
+
import { IPaginatorOptions } from '../interfaces/collaterals.interface';
|
|
28
|
+
import { getCollateralDocsByBBC, ITEMS_PAGINATION } from '../db/collaterals.db';
|
|
29
|
+
import { IAvailabilitySignView } from '../models/AvailabilitySigns.model';
|
|
30
|
+
import { MODEL_NAMES } from '../models/_models';
|
|
31
|
+
import { setReceivableManualInputsToggle } from '../db/receivables.db';
|
|
32
|
+
import { getAvailabilitySigns } from '../db/availability.db';
|
|
33
|
+
import { IInventoryAvailabilitySummary, InventoryAvailabilityPaginators } from '../models/InventoryAvailability.model';
|
|
34
|
+
import { getBBCSheetsByType } from '../db/bbcSheets.db';
|
|
35
|
+
import { CustomerGroupModel } from '../models/CustomerGroup.model';
|
|
36
|
+
import { getPartialPaid } from '../queries/receivable/partially-paid-restated';
|
|
37
|
+
|
|
38
|
+
import MappedGroup from '../models/MappedGroup.model';
|
|
39
|
+
import CustomerAPGroup from '../models/CustomerAPGroup.model';
|
|
40
|
+
import { getReserveDocs } from '../db/reserve.db';
|
|
41
|
+
import { IReserveSummary } from '../models/Reserve.model';
|
|
42
|
+
import { IEquipmentSummary } from '../models/Equipment.model';
|
|
43
|
+
import { ICollateralAdjustmentSummary } from '../models/CollateralAdjustment.model';
|
|
44
|
+
import { CollateralAdjustmentsService } from './collateral-adjustments.service';
|
|
45
|
+
import { CollateralsService } from './collaterals.service';
|
|
46
|
+
import { EquipmentService } from './equipment.service';
|
|
47
|
+
import { InventoryAvailabilityService } from './inventory-availability.service';
|
|
48
|
+
import { LoanStatementService } from './loan-statement.service';
|
|
49
|
+
import { LoanTransactionsService } from './loan-transactions.service';
|
|
50
|
+
import { ManualEntryService } from './manual-entry.service';
|
|
51
|
+
import { ReserveService } from './reserve.service';
|
|
52
|
+
import { SignsService } from './signs.service';
|
|
53
|
+
import { EInventoryAvailabilityResults } from '../enums/inventory-availability-results.enum';
|
|
54
|
+
|
|
55
|
+
type IMappedSettings = {
|
|
56
|
+
[_key in ELimitSettings]: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface ICustomerConcentration {
|
|
60
|
+
EXCLUDED: ICustomerTotals,
|
|
61
|
+
INCLUDED: ICustomerTotals,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface ISummaryField {
|
|
65
|
+
[field: string]: {
|
|
66
|
+
fieldPath: string;
|
|
67
|
+
reverse?: boolean;
|
|
68
|
+
replacedHeader?: string;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface IAvailabilityFullSummary {
|
|
73
|
+
reserve: IReserveSummary;
|
|
74
|
+
equipment: IEquipmentSummary;
|
|
75
|
+
inventory: IInventoryAvailabilitySummary[];
|
|
76
|
+
receivable: IAvailabilitySummary;
|
|
77
|
+
loanBalances: {
|
|
78
|
+
TERM: number;
|
|
79
|
+
REVOLVER: number;
|
|
80
|
+
};
|
|
81
|
+
collateralAdjustment: {
|
|
82
|
+
summary: ICollateralAdjustmentSummary;
|
|
83
|
+
};
|
|
84
|
+
manualInputs: {
|
|
85
|
+
receivableAvailability: boolean;
|
|
86
|
+
inventoryAvailability: boolean;
|
|
87
|
+
};
|
|
88
|
+
accruedStatement: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface ICustomerTotals {
|
|
92
|
+
[customer: string]: {
|
|
93
|
+
preliminaryEligibleBalance: number;
|
|
94
|
+
permittedOver: number;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const START_PAGINATORS: InventoryAvailabilityPaginators = {
|
|
99
|
+
[EInventoryAvailabilityResults.UNIQ_ITEMS]: { pageSize: 20, pageIndex: 0 },
|
|
100
|
+
[EInventoryAvailabilityResults.NON_UNIQ_ITEMS]: { pageSize: 20, pageIndex: 0 },
|
|
101
|
+
[EInventoryAvailabilityResults.NOT_MATCHED_ITEMS]: { pageSize: 20, pageIndex: 0 },
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export class AvailabilityService {
|
|
105
|
+
|
|
106
|
+
constructor(
|
|
107
|
+
private readonly collateralAdjustmentsService: CollateralAdjustmentsService,
|
|
108
|
+
private readonly collateralsService: CollateralsService,
|
|
109
|
+
private readonly equipmentService: EquipmentService,
|
|
110
|
+
private readonly inventoryAvailabilityService: InventoryAvailabilityService,
|
|
111
|
+
private readonly loanStatementService: LoanStatementService,
|
|
112
|
+
private readonly loanTransactionsService: LoanTransactionsService,
|
|
113
|
+
private readonly manualEntryService: ManualEntryService,
|
|
114
|
+
private readonly reserveService: ReserveService,
|
|
115
|
+
private readonly signsService: SignsService,
|
|
116
|
+
) {
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async recombineGroups(calculationsSettings: IAvailabilitySettings): Promise<IMappedSettings> {
|
|
120
|
+
|
|
121
|
+
const mappedSettings = await Promise.all(Object.values(ELimitSettings).map(async (settingsKey) => {
|
|
122
|
+
const customerGroupMapped = await Promise.all(calculationsSettings[settingsKey].map(async (customerGroup) => {
|
|
123
|
+
const idIsValid = isValidId(customerGroup.customer);
|
|
124
|
+
if (idIsValid) {
|
|
125
|
+
const foundCustomerGroup = await CustomerGroupModel.findById(customerGroup.customer);
|
|
126
|
+
if (foundCustomerGroup) {
|
|
127
|
+
return foundCustomerGroup.items.reduce((innerAcc, item) => ({
|
|
128
|
+
...innerAcc,
|
|
129
|
+
[item]: customerGroup.limit,
|
|
130
|
+
}), {});
|
|
131
|
+
}
|
|
132
|
+
return { [customerGroup.customer]: customerGroup.limit };
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}));
|
|
136
|
+
const filtered = customerGroupMapped.filter((group) => !!group);
|
|
137
|
+
return filtered.reduce((lastAcc, item) => ({ ...lastAcc, ...item }), {});
|
|
138
|
+
}));
|
|
139
|
+
const groupedSettings = <IMappedSettings>Object.values(ELimitSettings).reduce((acc, settings, index) => ({
|
|
140
|
+
...acc,
|
|
141
|
+
[settings]: mappedSettings[index],
|
|
142
|
+
}), {});
|
|
143
|
+
Object.values(ELimitSettings).forEach((settingsKey) => {
|
|
144
|
+
calculationsSettings[settingsKey].forEach((customer) => {
|
|
145
|
+
const idIsValid = isValidId(customer.customer);
|
|
146
|
+
if (!idIsValid) {
|
|
147
|
+
groupedSettings[settingsKey][customer.customer] = customer.limit;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
return groupedSettings;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async calculateAvailability(bbcDateId: string, paginatorOptions: IPaginatorOptions, user: IUserDocument): Promise<IReceivableAvailabilityView> {
|
|
155
|
+
|
|
156
|
+
const calculationsSettings = await this.getCalculationSettings(bbcDateId);
|
|
157
|
+
|
|
158
|
+
const allReceivables = await getCollateralDocsByBBC([bbcDateId], ECollaterals.RECEIVABLE);
|
|
159
|
+
const receivables = allReceivables.pop();
|
|
160
|
+
|
|
161
|
+
const calculation: IReceivableAvailability = {
|
|
162
|
+
borrowerId: null,
|
|
163
|
+
bbcDateId: new mongoose.Types.ObjectId(bbcDateId),
|
|
164
|
+
userCalculatedId: user?._id ? new mongoose.Types.ObjectId(String(user._id)) : null,
|
|
165
|
+
calculatedAt: new Date(),
|
|
166
|
+
actual: true,
|
|
167
|
+
settings: calculationsSettings,
|
|
168
|
+
items: [],
|
|
169
|
+
summary: null,
|
|
170
|
+
totalItems: 0,
|
|
171
|
+
useManualInputs: false,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (!receivables) {
|
|
175
|
+
return <IReceivableAvailabilityView>calculation;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
calculation.borrowerId = new mongoose.Types.ObjectId(receivables.borrowerId.toString());
|
|
179
|
+
|
|
180
|
+
const allCustomerGroups = await CustomerGroupModel.find({ borrowerId: receivables.borrowerId }).lean();
|
|
181
|
+
|
|
182
|
+
const enrichedEnrichedSettings = await this.recombineGroups(calculationsSettings);
|
|
183
|
+
|
|
184
|
+
const getBucket = (bbcDate: Date, item: IReceivableItem, permittedDays: number, isExcluded = false) => {
|
|
185
|
+
const invoiceDate = dayjs(item.invoiceDate).startOf('day');
|
|
186
|
+
const actualDays = dayjs(bbcDate).startOf('day').diff(invoiceDate, 'day');
|
|
187
|
+
const data = {
|
|
188
|
+
bucketCurrent: 0,
|
|
189
|
+
bucket30: 0,
|
|
190
|
+
bucket60: 0,
|
|
191
|
+
permitted60: 0,
|
|
192
|
+
permittedOver: 0,
|
|
193
|
+
creditNotes: 0,
|
|
194
|
+
actualDays,
|
|
195
|
+
permittedDays,
|
|
196
|
+
};
|
|
197
|
+
if (isExcluded) {
|
|
198
|
+
return data;
|
|
199
|
+
}
|
|
200
|
+
if (item.invoiceAmount < 0) {
|
|
201
|
+
data.creditNotes = item.invoiceAmount;
|
|
202
|
+
return data;
|
|
203
|
+
}
|
|
204
|
+
if (actualDays > permittedDays) {
|
|
205
|
+
data.permittedOver = item.invoiceAmount;
|
|
206
|
+
return data;
|
|
207
|
+
}
|
|
208
|
+
if (actualDays > 60 && actualDays <= permittedDays) {
|
|
209
|
+
data.permitted60 = item.invoiceAmount;
|
|
210
|
+
return data;
|
|
211
|
+
}
|
|
212
|
+
if (actualDays > 30) {
|
|
213
|
+
data.bucket60 = item.invoiceAmount;
|
|
214
|
+
return data;
|
|
215
|
+
}
|
|
216
|
+
if (actualDays >= 1) {
|
|
217
|
+
data.bucket30 = item.invoiceAmount;
|
|
218
|
+
return data;
|
|
219
|
+
}
|
|
220
|
+
data.bucketCurrent = item.invoiceAmount;
|
|
221
|
+
return data;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const getExcludedCustomers = (items: IReceivableItemDoc[]) => {
|
|
225
|
+
const uniqueCustomers = Array.from(new Set(items.map((c) => c.customerTitle)));
|
|
226
|
+
return uniqueCustomers.reduce((acc: string[], customer) => {
|
|
227
|
+
if (Object.keys(enrichedEnrichedSettings.excludedCustomers).includes(customer)) {
|
|
228
|
+
return [...acc, customer];
|
|
229
|
+
}
|
|
230
|
+
return acc;
|
|
231
|
+
}, []);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const calculateCustomerConcentration = (items: IReceivableAvailabilityItem[], calculationsSettings: IAvailabilitySettings) => {
|
|
235
|
+
|
|
236
|
+
const uniqueCustomers = Array.from(new Set(items.map((c) => c.customerTitle)));
|
|
237
|
+
|
|
238
|
+
const uniqueCustomersWithTotal = items.reduce((acc, item) => {
|
|
239
|
+
const preliminaryEligibleBalance = item.preliminaryEligibleBalance;
|
|
240
|
+
const permittedOver = item.permittedOver;
|
|
241
|
+
if (acc[item.customerTitle]) {
|
|
242
|
+
return {
|
|
243
|
+
...acc,
|
|
244
|
+
[item.customerTitle]: {
|
|
245
|
+
preliminaryEligibleBalance: new Decimal(acc[item.customerTitle].preliminaryEligibleBalance).add(preliminaryEligibleBalance).toNumber(),
|
|
246
|
+
permittedOver: new Decimal(acc[item.customerTitle].permittedOver).add(permittedOver).toNumber(),
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
...acc,
|
|
252
|
+
[item.customerTitle]: {
|
|
253
|
+
preliminaryEligibleBalance: preliminaryEligibleBalance,
|
|
254
|
+
permittedOver: permittedOver,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}, <ICustomerTotals>{});
|
|
258
|
+
|
|
259
|
+
const uniqueCustomersWithTotalGrouped = Object.entries(uniqueCustomersWithTotal).reduce((acc: ICustomerConcentration, [customer, totals]) => {
|
|
260
|
+
if (Object.keys(enrichedEnrichedSettings.excludedCustomers).includes(customer)) {
|
|
261
|
+
return { INCLUDED: { ...acc.INCLUDED }, EXCLUDED: { ...acc.EXCLUDED, [customer]: totals } };
|
|
262
|
+
}
|
|
263
|
+
return { EXCLUDED: { ...acc.EXCLUDED }, INCLUDED: { ...acc.INCLUDED, [customer]: totals } };
|
|
264
|
+
}, { EXCLUDED: {}, INCLUDED: {} });
|
|
265
|
+
|
|
266
|
+
const preliminaryEligibleIncluded = Object
|
|
267
|
+
.values(uniqueCustomersWithTotalGrouped.INCLUDED)
|
|
268
|
+
.reduce((total: number, c) => {
|
|
269
|
+
return new Decimal(total).add(c.preliminaryEligibleBalance).toNumber();
|
|
270
|
+
}, 0);
|
|
271
|
+
|
|
272
|
+
const totalPermittedOver = Object
|
|
273
|
+
.values(uniqueCustomersWithTotalGrouped.INCLUDED)
|
|
274
|
+
.reduce((total: number, c) => {
|
|
275
|
+
return new Decimal(total).add(c.permittedOver).toNumber();
|
|
276
|
+
}, 0);
|
|
277
|
+
|
|
278
|
+
const totalExcluded = new Decimal(preliminaryEligibleIncluded).add(totalPermittedOver).toNumber();
|
|
279
|
+
|
|
280
|
+
const INCLUDED = {};
|
|
281
|
+
const updatedCustomers = items.map((c) => {
|
|
282
|
+
if (!excludedCustomers.includes(c.customerTitle)) {
|
|
283
|
+
INCLUDED[c.customerTitle] = {
|
|
284
|
+
concentration: new Decimal(uniqueCustomersWithTotal[c.customerTitle].preliminaryEligibleBalance).div(preliminaryEligibleIncluded).toNumber(),
|
|
285
|
+
total: uniqueCustomersWithTotal[c.customerTitle],
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const permittedConcentrationLimit = enrichedEnrichedSettings.permittedConcentration[c.customerTitle];
|
|
289
|
+
const permittedCustomerConcentration = permittedConcentrationLimit ? permittedConcentrationLimit : calculationsSettings.customerConcentrationLimit;
|
|
290
|
+
let customerConcentration: number;
|
|
291
|
+
if (calculationsSettings.excludeFromTotal) {
|
|
292
|
+
customerConcentration = totalExcluded === 0
|
|
293
|
+
? 0
|
|
294
|
+
: new Decimal(uniqueCustomersWithTotal[c.customerTitle].preliminaryEligibleBalance).add(uniqueCustomersWithTotal[c.customerTitle].permittedOver).div(totalExcluded).toNumber();
|
|
295
|
+
} else {
|
|
296
|
+
customerConcentration = preliminaryEligibleIncluded === 0
|
|
297
|
+
? 0 :
|
|
298
|
+
new Decimal(uniqueCustomersWithTotal[c.customerTitle].preliminaryEligibleBalance).div(preliminaryEligibleIncluded).toNumber();
|
|
299
|
+
}
|
|
300
|
+
return { ...c, customerConcentration, permittedCustomerConcentration };
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const uniqueCustomerConcentration = uniqueCustomers
|
|
304
|
+
.map((customer) => {
|
|
305
|
+
if (excludedCustomers.includes(customer)) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
const foundRow = updatedCustomers.find((c) => c.customerTitle === customer);
|
|
309
|
+
return {
|
|
310
|
+
customerTitle: foundRow.customerTitle,
|
|
311
|
+
customerGroup: foundRow.customerGroup,
|
|
312
|
+
concentration: foundRow.customerConcentration,
|
|
313
|
+
};
|
|
314
|
+
})
|
|
315
|
+
.filter((customer) => !!customer)
|
|
316
|
+
.filter((customer) => !!customer.customerGroup);
|
|
317
|
+
|
|
318
|
+
const groupsConcentration = uniqueCustomerConcentration.reduce((groupAcc, group) => {
|
|
319
|
+
return { ...groupAcc, [group.customerGroup]: (groupAcc[group.customerGroup] ?? 0) + group.concentration };
|
|
320
|
+
}, {});
|
|
321
|
+
|
|
322
|
+
updatedCustomers.forEach((customer) => {
|
|
323
|
+
const groupConcentration = groupsConcentration[customer.customerGroup];
|
|
324
|
+
if (groupConcentration) {
|
|
325
|
+
customer.customerConcentration = groupConcentration;
|
|
326
|
+
}
|
|
327
|
+
if (customer.excludedCustomer) {
|
|
328
|
+
customer.customerConcentration = 0;
|
|
329
|
+
}
|
|
330
|
+
customer.deductionPercent = customer.customerConcentration === 0
|
|
331
|
+
? 0
|
|
332
|
+
: Math.min(0, customer.permittedCustomerConcentration - customer.customerConcentration) / customer.customerConcentration;
|
|
333
|
+
const deduction = roundToXDigits(Math.abs(customer.grossAR * customer.deductionPercent));
|
|
334
|
+
customer.deduction = Math.min(deduction, customer.preliminaryEligibleBalance);
|
|
335
|
+
customer.balanceAfterDeduction = customer.preliminaryEligibleBalance - customer.deduction;
|
|
336
|
+
});
|
|
337
|
+
return { updatedCustomers, INCLUDED };
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const groupData = (items: IReceivableAvailabilityItem[], groupedBy: 'grossAR' | 'crossAge' | 'balanceAfterCrossAge' | 'balanceAfterPartially' = 'crossAge', byGroup = true) => {
|
|
341
|
+
const uniqueCustomers = Array.from(new Set(items.map((c) => byGroup ? c.customer : c.customerTitle)));
|
|
342
|
+
return uniqueCustomers.reduce((acc, customer) => {
|
|
343
|
+
const totals = items.reduce((innerAcc, c) => {
|
|
344
|
+
if (c[byGroup ? 'customer' : 'customerTitle'] === customer) {
|
|
345
|
+
const grossAR = roundToXDigits(innerAcc.grossAR + c.grossAR);
|
|
346
|
+
const permittedOver = roundToXDigits(innerAcc.permittedOver + c.permittedOver);
|
|
347
|
+
const crossAge = grossAR === 0 ? 0 : permittedOver / grossAR;
|
|
348
|
+
const balanceAfterCrossAge = roundToXDigits(innerAcc.balanceAfterCrossAge + c.balanceAfterCrossAge);
|
|
349
|
+
const balanceAfterExclusion = roundToXDigits(innerAcc.balanceAfterExclusion + c.balanceAfterExclusion);
|
|
350
|
+
return { grossAR, permittedOver, crossAge, balanceAfterCrossAge, balanceAfterExclusion };
|
|
351
|
+
}
|
|
352
|
+
return innerAcc;
|
|
353
|
+
}, { grossAR: 0, permittedOver: 0, crossAge: 0, balanceAfterCrossAge: 0, balanceAfterExclusion: 0 });
|
|
354
|
+
return { ...acc, [customer]: totals[groupedBy] };
|
|
355
|
+
}, {});
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const startDate = dayjs(new Date(receivables.bbcDate)).subtract(calculationsSettings.paidDays, 'day').toDate();
|
|
359
|
+
const endDate = new Date(receivables.bbcDate);
|
|
360
|
+
const partiallyPaidData = await getPartialPaid({
|
|
361
|
+
borrowerId: new mongoose.Types.ObjectId(String(receivables.borrowerId)),
|
|
362
|
+
startDate,
|
|
363
|
+
endDate,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const excludedCustomers = getExcludedCustomers(receivables.items);
|
|
367
|
+
|
|
368
|
+
const allInvoices = [...receivables.items];
|
|
369
|
+
|
|
370
|
+
let items: IReceivableAvailabilityItem[] = receivables.items.map((invoice: IReceivableItemDoc) => {
|
|
371
|
+
|
|
372
|
+
const permittedDaysLimit = enrichedEnrichedSettings.permittedExtendedARDays[invoice.customerTitle];
|
|
373
|
+
const permittedDays = permittedDaysLimit ? permittedDaysLimit : calculationsSettings.standardARDays;
|
|
374
|
+
|
|
375
|
+
const isExcluded = excludedCustomers.includes(invoice.customerTitle);
|
|
376
|
+
|
|
377
|
+
const data: IReceivableAvailabilityItemView = {
|
|
378
|
+
...getBucket(receivables.bbcDate, invoice, permittedDays, isExcluded),
|
|
379
|
+
customer: invoice.customerTitle.trim(),
|
|
380
|
+
invoice: invoice,
|
|
381
|
+
customerTitle: invoice.customerTitle.trim(),
|
|
382
|
+
invoiceId: invoice._id.toString(),
|
|
383
|
+
invoiceNumber: invoice.invoiceNumber,
|
|
384
|
+
invoiceDate: invoice.invoiceDate,
|
|
385
|
+
invoiceAmount: invoice.invoiceAmount,
|
|
386
|
+
grossAR: 0,
|
|
387
|
+
preliminaryEligibleBalance: 0,
|
|
388
|
+
permittedCustomerConcentration: 0,
|
|
389
|
+
customerConcentration: 0,
|
|
390
|
+
deductionPercent: 0,
|
|
391
|
+
deduction: 0,
|
|
392
|
+
balanceAfterDeduction: 0,
|
|
393
|
+
partiallyPaidDifference: false,
|
|
394
|
+
partiallyPaidDeduction: 0,
|
|
395
|
+
balanceAfterPartially: 0,
|
|
396
|
+
excludedCustomer: isExcluded,
|
|
397
|
+
excludedCustomerDeduction: 0,
|
|
398
|
+
balanceAfterExclusion: 0,
|
|
399
|
+
customerGroupId: null,
|
|
400
|
+
customerGroup: null,
|
|
401
|
+
contra: 0,
|
|
402
|
+
contraDeduction: 0,
|
|
403
|
+
balanceAfterContra: 0,
|
|
404
|
+
crossAge: 0,
|
|
405
|
+
crossAgeDeduction: 0,
|
|
406
|
+
balanceAfterCrossAge: 0,
|
|
407
|
+
waivedDeduction: 0,
|
|
408
|
+
finalResult: 0,
|
|
409
|
+
insuredComponent: 0,
|
|
410
|
+
uninsuredComponent: 0,
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const foundCustomerGroup = allCustomerGroups.find((group) => group.items.includes(data.customerTitle));
|
|
414
|
+
if (foundCustomerGroup) {
|
|
415
|
+
data.customer = foundCustomerGroup.groupName;
|
|
416
|
+
data.customerGroupId = foundCustomerGroup.id;
|
|
417
|
+
data.customerGroup = foundCustomerGroup.groupName;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const foundInvoice = partiallyPaidData.partiallyPaidInvoices.find((pInvoice) => {
|
|
421
|
+
return (pInvoice.invoiceNumber === invoice.invoiceNumber) && (pInvoice.customer === invoice.customerTitle);
|
|
422
|
+
});
|
|
423
|
+
if (foundInvoice) {
|
|
424
|
+
data.partiallyPaidDifference = foundInvoice.difference > 0;
|
|
425
|
+
data.partiallyPaidDeduction = invoice.invoiceAmount;
|
|
426
|
+
}
|
|
427
|
+
if (invoice.originalAmount !== 0 && invoice.originalAmount !== invoice.invoiceAmount) {
|
|
428
|
+
data.partiallyPaidDifference = true;
|
|
429
|
+
data.partiallyPaidDeduction = invoice.invoiceAmount;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const duplicateInvoices = allInvoices.filter((pInvoice) => {
|
|
433
|
+
return (pInvoice.invoiceNumber === invoice.invoiceNumber) && (pInvoice.customerTitle === invoice.customerTitle);
|
|
434
|
+
});
|
|
435
|
+
if (duplicateInvoices.length > 1) {
|
|
436
|
+
data.partiallyPaidDifference = true;
|
|
437
|
+
data.partiallyPaidDeduction = invoice.invoiceAmount;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
data.grossAR = isExcluded ? invoice.invoiceAmount : roundToXDigits(data.bucketCurrent + data.bucket30 + data.bucket60 + data.permitted60 + data.permittedOver);
|
|
441
|
+
data.preliminaryEligibleBalance = roundToXDigits(data.bucketCurrent + data.bucket30 + data.bucket60 + data.permitted60);
|
|
442
|
+
return data;
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
const groupedGross = groupData(items, 'grossAR', false);
|
|
446
|
+
|
|
447
|
+
({ updatedCustomers: items } = calculateCustomerConcentration(items, calculationsSettings));
|
|
448
|
+
|
|
449
|
+
const bbcSheets = await getBBCSheetsByType([bbcDateId], ECollaterals.ACCOUNT_PAYABLE);
|
|
450
|
+
const apData = await AccountPayableItemModel.aggregate([
|
|
451
|
+
{
|
|
452
|
+
$match:
|
|
453
|
+
{ bbcSheetId: { $in: bbcSheets.map((sheet) => sheet._id) } },
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
$group: {
|
|
457
|
+
'_id': '$customerName',
|
|
458
|
+
'itemSum': {
|
|
459
|
+
$sum: '$amount',
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
]);
|
|
464
|
+
|
|
465
|
+
const mappedGroups = await MappedGroup
|
|
466
|
+
.find({ borrowerId: receivables.borrowerId })
|
|
467
|
+
.lean();
|
|
468
|
+
const replacedMappedGroups = await Promise.all(mappedGroups.map(async (group) => {
|
|
469
|
+
const groupCopy = { ...group, items: [group.group1], customersAP: [group.group2] };
|
|
470
|
+
switch (group.groupType) {
|
|
471
|
+
case 'contras':
|
|
472
|
+
if (isValidId(group.group1)) {
|
|
473
|
+
const foundGroup = await CustomerGroupModel.findById(group.group1).lean();
|
|
474
|
+
groupCopy.items = foundGroup ? foundGroup.items.slice() : [];
|
|
475
|
+
}
|
|
476
|
+
if (isValidId(group.group2)) {
|
|
477
|
+
const foundGroup = await CustomerAPGroup.findById(group.group2).lean();
|
|
478
|
+
groupCopy.customersAP = foundGroup ? foundGroup.items.slice() : [];
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return groupCopy;
|
|
482
|
+
}));
|
|
483
|
+
|
|
484
|
+
const groupedData = groupData(items);
|
|
485
|
+
|
|
486
|
+
items.forEach((customer) => {
|
|
487
|
+
/** --- Excluded customer --- */
|
|
488
|
+
customer.excludedCustomerDeduction = customer.excludedCustomer ? customer.invoiceAmount : 0;
|
|
489
|
+
customer.balanceAfterExclusion = customer.excludedCustomer ? 0 : customer.invoiceAmount;
|
|
490
|
+
|
|
491
|
+
if (customer.excludedCustomer) {
|
|
492
|
+
customer.grossAR = 0;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** --- Partially paid --- */
|
|
496
|
+
if (customer.balanceAfterDeduction === 0) {
|
|
497
|
+
customer.partiallyPaidDeduction = 0;
|
|
498
|
+
}
|
|
499
|
+
customer.balanceAfterPartially = customer.partiallyPaidDifference ? 0 : customer.balanceAfterDeduction - customer.partiallyPaidDeduction;
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
const groupedBalanceAfterPartially = groupData(items, 'balanceAfterPartially', false);
|
|
503
|
+
items.forEach((customer) => {
|
|
504
|
+
/** --- Contra --- */
|
|
505
|
+
customer.balanceAfterContra = customer.balanceAfterPartially;
|
|
506
|
+
const mappedGroupMatches = replacedMappedGroups.filter((mappedGroup) => mappedGroup.items.includes(customer.customerTitle));
|
|
507
|
+
if (mappedGroupMatches && mappedGroupMatches.length) {
|
|
508
|
+
const contra = mappedGroupMatches.reduce((acc, mappedGroup) => {
|
|
509
|
+
return acc + mappedGroup.customersAP.reduce((innerAcc, customerAC) => {
|
|
510
|
+
const foundApData = apData.find((data) => data._id === customerAC);
|
|
511
|
+
if (foundApData) {
|
|
512
|
+
return foundApData.itemSum + innerAcc;
|
|
513
|
+
}
|
|
514
|
+
return innerAcc;
|
|
515
|
+
}, 0);
|
|
516
|
+
}, 0);
|
|
517
|
+
const contraClearDeduction = contra * calculationsSettings.contraProvision;
|
|
518
|
+
const eligibleBalance = groupedBalanceAfterPartially[customer.customerTitle] !== 0 ? customer.balanceAfterPartially / groupedBalanceAfterPartially[customer.customerTitle] : 0;
|
|
519
|
+
const contraDeduction = roundToXDigits(eligibleBalance * contraClearDeduction);
|
|
520
|
+
customer.contra = contra;
|
|
521
|
+
customer.contraDeduction = contraDeduction >= customer.balanceAfterPartially ? customer.balanceAfterPartially : contraDeduction;
|
|
522
|
+
customer.balanceAfterContra = customer.balanceAfterPartially - customer.contraDeduction;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** --- Cross age --- */
|
|
526
|
+
customer.crossAge = groupedData[customer.customer];
|
|
527
|
+
customer.crossAgeDeduction = customer.crossAge > calculationsSettings.crossAgeLimit ? customer.balanceAfterContra : 0;
|
|
528
|
+
customer.balanceAfterCrossAge = customer.crossAge > calculationsSettings.crossAgeLimit ? 0 : customer.balanceAfterContra;
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
/** --- Insurance --- */
|
|
532
|
+
const groupedBalanceAfterCrossAge = groupData(items, 'balanceAfterCrossAge', false);
|
|
533
|
+
items.forEach((customer) => {
|
|
534
|
+
const insured = enrichedEnrichedSettings.insuredCustomers[customer.customerTitle] ?? 0;
|
|
535
|
+
const totalGross = groupedGross[customer.customerTitle] ?? 0;
|
|
536
|
+
if (insured > totalGross) {
|
|
537
|
+
customer.waivedDeduction = calculationsSettings.waiveInsuredCustomers ? new Decimal(customer.deduction).add(customer.crossAgeDeduction).toNumber() : 0;
|
|
538
|
+
}
|
|
539
|
+
customer.finalResult = new Decimal((
|
|
540
|
+
calculationsSettings.waiveInsuredCustomers
|
|
541
|
+
? new Decimal(customer.balanceAfterCrossAge).add(customer.waivedDeduction).toNumber()
|
|
542
|
+
: customer.balanceAfterCrossAge
|
|
543
|
+
)).add(customer.creditNotes).toNumber();
|
|
544
|
+
customer.uninsuredComponent = customer.finalResult;
|
|
545
|
+
if (insured) {
|
|
546
|
+
const insuredAmount = groupedBalanceAfterCrossAge[customer.customerTitle] === 0
|
|
547
|
+
? 0
|
|
548
|
+
: new Decimal(insured).div(groupedBalanceAfterCrossAge[customer.customerTitle]).mul(customer.finalResult).toDecimalPlaces(2).toNumber();
|
|
549
|
+
const insuredAmountLimited = insuredAmount > customer.finalResult ? customer.finalResult : insuredAmount;
|
|
550
|
+
const uninsuredAmount = new Decimal(customer.finalResult).sub(insuredAmountLimited).toNumber();
|
|
551
|
+
customer.insuredComponent = isNaN(insuredAmountLimited) ? 0 : insuredAmountLimited;
|
|
552
|
+
customer.uninsuredComponent = isNaN(uninsuredAmount) ? 0 : uninsuredAmount;
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
calculation.items = items;
|
|
557
|
+
calculation.summary = await this.calculateAvailabilitySummary(calculation);
|
|
558
|
+
calculation.totalItems = await this.collateralsService.countCollateralDocsByBBC([bbcDateId], ECollaterals.RECEIVABLE);
|
|
559
|
+
await this.saveCalculatedAvailability(calculation);
|
|
560
|
+
return await this.getAvailability(bbcDateId, paginatorOptions, user);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
async getAvailability(bbcDateId: string, paginatorOptions: IPaginatorOptions, user: IUserDocument) {
|
|
564
|
+
return await this.foundOrCreateAvailability(bbcDateId, paginatorOptions, user);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
async getAvailabilityItemsWithFilter(bbcDateId: string, itemsFilter: string) {
|
|
568
|
+
const filtersMap = {
|
|
569
|
+
creditNotesNegative: 'creditNotes',
|
|
570
|
+
};
|
|
571
|
+
const filterField = filtersMap[itemsFilter] ? filtersMap[itemsFilter] : itemsFilter;
|
|
572
|
+
const emptyResponse = { itemsFilter: filterField, invoiceItems: [], customerItems: [] };
|
|
573
|
+
const allowedFilters = [
|
|
574
|
+
'creditNotes',
|
|
575
|
+
'excludedCustomerDeduction',
|
|
576
|
+
'permittedOver',
|
|
577
|
+
'deduction',
|
|
578
|
+
'partiallyPaidDeduction',
|
|
579
|
+
'contraDeduction',
|
|
580
|
+
'crossAgeDeduction',
|
|
581
|
+
'waivedDeduction',
|
|
582
|
+
];
|
|
583
|
+
if (!allowedFilters.includes(filterField)) {
|
|
584
|
+
return emptyResponse;
|
|
585
|
+
}
|
|
586
|
+
const foundAvailability = await this.findCalculatedAvailability(bbcDateId);
|
|
587
|
+
if (!foundAvailability) {
|
|
588
|
+
return emptyResponse;
|
|
589
|
+
}
|
|
590
|
+
const items = await ReceivableAvailabilityItemModel.aggregate<IReceivableAvailabilityItemView>([
|
|
591
|
+
{
|
|
592
|
+
$match: {
|
|
593
|
+
'availabilityId': foundAvailability._id,
|
|
594
|
+
[filterField]: { $ne: 0 },
|
|
595
|
+
},
|
|
596
|
+
}, {
|
|
597
|
+
$sort: {
|
|
598
|
+
'order': 1,
|
|
599
|
+
},
|
|
600
|
+
}, {
|
|
601
|
+
$lookup: {
|
|
602
|
+
from: MODEL_NAMES.receivableItems,
|
|
603
|
+
localField: 'invoiceId',
|
|
604
|
+
foreignField: '_id',
|
|
605
|
+
as: 'invoice',
|
|
606
|
+
},
|
|
607
|
+
}, {
|
|
608
|
+
$unwind: {
|
|
609
|
+
path: '$invoice',
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
]);
|
|
613
|
+
const invoiceItems = items
|
|
614
|
+
.map((item) => {
|
|
615
|
+
return {
|
|
616
|
+
invoiceNumber: item.invoice.invoiceNumber,
|
|
617
|
+
invoiceDate: item.invoice.invoiceDate,
|
|
618
|
+
customerTitle: item.invoice.customerTitle,
|
|
619
|
+
invoiceAmount: item.invoice.invoiceAmount,
|
|
620
|
+
};
|
|
621
|
+
})
|
|
622
|
+
.sort((a, b) => new Date(b.invoiceDate).valueOf() - new Date(a.invoiceDate).valueOf());
|
|
623
|
+
const customerItemsRaw = items.reduce((acc, item) => {
|
|
624
|
+
if (!acc[item.invoice.customerTitle]) {
|
|
625
|
+
return {
|
|
626
|
+
...acc,
|
|
627
|
+
[item.invoice.customerTitle]: item[filterField],
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
return {
|
|
631
|
+
...acc,
|
|
632
|
+
[item.invoice.customerTitle]: new Decimal(acc[item.invoice.customerTitle]).add(item[filterField]).toNumber(),
|
|
633
|
+
};
|
|
634
|
+
}, <{ [customerTitle: string]: number }>{});
|
|
635
|
+
const customerItemsTotal = Object
|
|
636
|
+
.values(customerItemsRaw)
|
|
637
|
+
.reduce((acc, itemValue) => new Decimal(acc).add(itemValue).toNumber(), 0);
|
|
638
|
+
const findConcentration = (customerTitle: string): number => {
|
|
639
|
+
const foundItem = items.find((item) => item.invoice.customerTitle === customerTitle);
|
|
640
|
+
if (!foundItem) {
|
|
641
|
+
return 0;
|
|
642
|
+
}
|
|
643
|
+
return new Decimal(foundItem.customerConcentration).mul(100).toDP(2).toNumber();
|
|
644
|
+
};
|
|
645
|
+
const customerItems = Object.entries(customerItemsRaw).map(([customerTitle, customerAmount]: [customerTitle: string, customerAmount: number]) => {
|
|
646
|
+
return {
|
|
647
|
+
customerTitle,
|
|
648
|
+
customerConcentration: itemsFilter === 'deduction' ? findConcentration(customerTitle) : null,
|
|
649
|
+
customerAmount,
|
|
650
|
+
};
|
|
651
|
+
});
|
|
652
|
+
const customerItemsWithTotal = customerItems.length > 0
|
|
653
|
+
? [...customerItems, { customerTitle: 'TOTAL', customerConcentration: null, customerAmount: customerItemsTotal }]
|
|
654
|
+
: customerItems;
|
|
655
|
+
return { itemsFilter: filterField, invoiceItems, customerItems: customerItemsWithTotal };
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
async foundOrCreateAvailability(bbcDateId: string, paginatorOptions: IPaginatorOptions, user: IUserDocument) {
|
|
659
|
+
const foundAvailability = await this.findCalculatedAvailability(bbcDateId);
|
|
660
|
+
if (foundAvailability) {
|
|
661
|
+
foundAvailability.items = await this.getAvailabilityItems(foundAvailability._id.toString(), paginatorOptions);
|
|
662
|
+
const uniqueCustomers = await this.getUniqueCustomers(foundAvailability._id.toString());
|
|
663
|
+
const bbcDateIdID = new mongoose.Types.ObjectId(bbcDateId);
|
|
664
|
+
foundAvailability.uniqueCustomers = uniqueCustomers.map((c) => c.customerTitle);
|
|
665
|
+
foundAvailability.totalItems = await this.collateralsService.countCollateralDocsByBBC([bbcDateId], ECollaterals.RECEIVABLE);
|
|
666
|
+
if (foundAvailability.useManualInputs) {
|
|
667
|
+
const { summary: manualSummary } = await this.manualEntryService.getReceivableManualSummary(bbcDateId);
|
|
668
|
+
foundAvailability.summary = manualSummary;
|
|
669
|
+
} else {
|
|
670
|
+
foundAvailability.summary = foundAvailability.summary ? foundAvailability.summary : <IAvailabilitySummary>{};
|
|
671
|
+
const reserves = await getReserveDocs([bbcDateIdID]);
|
|
672
|
+
const ARReserves = reserves.length ? reserves[0].summary?.RECEIVABLES?.amount : 0;
|
|
673
|
+
foundAvailability.summary.ARReserves = ARReserves;
|
|
674
|
+
foundAvailability.summary.ARAvailability = new Decimal(foundAvailability.summary.ARAvailability || 0).sub(ARReserves).toNumber();
|
|
675
|
+
}
|
|
676
|
+
return foundAvailability;
|
|
677
|
+
}
|
|
678
|
+
return await this.calculateAvailability(bbcDateId, paginatorOptions, user);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
async findCalculatedAvailability(bbcDateId: string): Promise<IReceivableAvailabilityView> {
|
|
682
|
+
const calculatedAvailability = await ReceivableAvailabilityModel.aggregate([
|
|
683
|
+
{
|
|
684
|
+
$match: {
|
|
685
|
+
'bbcDateId': new mongoose.Types.ObjectId(bbcDateId),
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
$lookup: {
|
|
690
|
+
from: MODEL_NAMES.BBCDates,
|
|
691
|
+
localField: 'bbcDateId',
|
|
692
|
+
foreignField: '_id',
|
|
693
|
+
as: 'bbc',
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
$unwind: {
|
|
698
|
+
path: '$bbc',
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
$addFields: {
|
|
703
|
+
'bbcDate': '$bbc.bbcDate',
|
|
704
|
+
},
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
$lookup: {
|
|
708
|
+
from: 'users',
|
|
709
|
+
localField: 'userCalculatedId',
|
|
710
|
+
foreignField: '_id',
|
|
711
|
+
as: 'userCalculated',
|
|
712
|
+
},
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
$unwind: {
|
|
716
|
+
path: '$userCalculated',
|
|
717
|
+
preserveNullAndEmptyArrays: true,
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
$group: {
|
|
722
|
+
'_id': '$_id',
|
|
723
|
+
'bbcDate': { $first: '$bbcDate' },
|
|
724
|
+
'bbcDateId': { $first: '$bbcDateId' },
|
|
725
|
+
'userCalculated': { $first: '$userCalculated' },
|
|
726
|
+
'calculatedAt': { $first: '$calculatedAt' },
|
|
727
|
+
'actual': { $first: '$actual' },
|
|
728
|
+
'settings': { $first: '$settings' },
|
|
729
|
+
'summary': { $first: '$summary' },
|
|
730
|
+
'useManualInputs': { $first: '$useManualInputs' },
|
|
731
|
+
},
|
|
732
|
+
},
|
|
733
|
+
]);
|
|
734
|
+
if (calculatedAvailability.length) {
|
|
735
|
+
return normalizeObject(calculatedAvailability[0], '_id', '_id');
|
|
736
|
+
}
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
async saveCalculatedAvailabilitySettings(bbcDateId: string, settings: IAvailabilitySettings): Promise<void> {
|
|
741
|
+
await ReceivableAvailabilityModel.findOneAndUpdate({ bbcDateId: bbcDateId }, {
|
|
742
|
+
actual: false,
|
|
743
|
+
settings: settings,
|
|
744
|
+
}, { upsert: true });
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
async saveLimitSettings(bbcDateId: string, limitSettings: {
|
|
748
|
+
[limitSettingsName: string]: IAvailabilityLimits[]
|
|
749
|
+
}): Promise<void> {
|
|
750
|
+
const availability = await this.findCalculatedAvailability(bbcDateId);
|
|
751
|
+
const updatedSettings = { ...availability.settings, ...limitSettings };
|
|
752
|
+
await this.saveCalculatedAvailabilitySettings(bbcDateId, updatedSettings);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
async calculateAvailabilitySummary(newCalculation: IReceivableAvailability): Promise<IAvailabilitySummary> {
|
|
756
|
+
const summaryFieldsOpt: ISummaryField = {
|
|
757
|
+
invoiceAmount: {
|
|
758
|
+
fieldPath: 'invoiceAmount',
|
|
759
|
+
replacedHeader: 'invoiceAmount',
|
|
760
|
+
},
|
|
761
|
+
excludedCustomerDeduction: {
|
|
762
|
+
fieldPath: 'excludedCustomerDeduction',
|
|
763
|
+
reverse: true,
|
|
764
|
+
},
|
|
765
|
+
creditNotes: {
|
|
766
|
+
fieldPath: 'creditNotes',
|
|
767
|
+
reverse: true,
|
|
768
|
+
},
|
|
769
|
+
grossAR: {
|
|
770
|
+
fieldPath: 'grossAR',
|
|
771
|
+
},
|
|
772
|
+
permittedOver: {
|
|
773
|
+
fieldPath: 'permittedOver',
|
|
774
|
+
reverse: true,
|
|
775
|
+
},
|
|
776
|
+
preliminaryEligibleBalance: {
|
|
777
|
+
fieldPath: 'preliminaryEligibleBalance',
|
|
778
|
+
},
|
|
779
|
+
creditNotesNegative: {
|
|
780
|
+
fieldPath: 'creditNotes',
|
|
781
|
+
},
|
|
782
|
+
deduction: {
|
|
783
|
+
fieldPath: 'deduction',
|
|
784
|
+
reverse: true,
|
|
785
|
+
},
|
|
786
|
+
partiallyPaidDeduction: {
|
|
787
|
+
fieldPath: 'partiallyPaidDeduction',
|
|
788
|
+
reverse: true,
|
|
789
|
+
},
|
|
790
|
+
contraDeduction: {
|
|
791
|
+
fieldPath: 'contraDeduction',
|
|
792
|
+
reverse: true,
|
|
793
|
+
},
|
|
794
|
+
crossAgeDeduction: {
|
|
795
|
+
fieldPath: 'crossAgeDeduction',
|
|
796
|
+
reverse: true,
|
|
797
|
+
},
|
|
798
|
+
balanceAfterCrossAge: {
|
|
799
|
+
fieldPath: 'balanceAfterCrossAge',
|
|
800
|
+
},
|
|
801
|
+
waivedDeduction: {
|
|
802
|
+
fieldPath: 'waivedDeduction',
|
|
803
|
+
},
|
|
804
|
+
finalResult: {
|
|
805
|
+
fieldPath: 'finalResult',
|
|
806
|
+
},
|
|
807
|
+
insuredComponent: {
|
|
808
|
+
fieldPath: 'insuredComponent',
|
|
809
|
+
},
|
|
810
|
+
uninsuredComponent: {
|
|
811
|
+
fieldPath: 'uninsuredComponent',
|
|
812
|
+
},
|
|
813
|
+
insuredAvailability: {
|
|
814
|
+
fieldPath: 'insuredAvailability',
|
|
815
|
+
},
|
|
816
|
+
uninsuredAvailability: {
|
|
817
|
+
fieldPath: 'uninsuredAvailability',
|
|
818
|
+
},
|
|
819
|
+
ARAvailability: {
|
|
820
|
+
fieldPath: 'ARAvailability',
|
|
821
|
+
},
|
|
822
|
+
};
|
|
823
|
+
const summary = Object.entries(summaryFieldsOpt).reduce((acc, [field, desc]) => {
|
|
824
|
+
const total = newCalculation.items.reduce((fieldTotal, customer) => {
|
|
825
|
+
if (desc.fieldPath === 'insuredAvailability') {
|
|
826
|
+
return new Decimal(customer.insuredComponent).mul(newCalculation.settings.insuredARAdvanceRate).add(fieldTotal).toDP(2).toNumber();
|
|
827
|
+
}
|
|
828
|
+
if (desc.fieldPath === 'uninsuredAvailability') {
|
|
829
|
+
return new Decimal(customer.uninsuredComponent).mul(newCalculation.settings.uninsuredARAdvanceRate).add(fieldTotal).toDP(2).toNumber();
|
|
830
|
+
}
|
|
831
|
+
if (desc.fieldPath === 'ARAvailability') {
|
|
832
|
+
const insuredPart = new Decimal(customer.insuredComponent).mul(newCalculation.settings.insuredARAdvanceRate);
|
|
833
|
+
const uninsuredPart = new Decimal(customer.uninsuredComponent).mul(newCalculation.settings.uninsuredARAdvanceRate);
|
|
834
|
+
return new Decimal(fieldTotal).add(insuredPart).add(uninsuredPart).toDP(2).toNumber();
|
|
835
|
+
}
|
|
836
|
+
if (_.get(customer, desc.fieldPath)) {
|
|
837
|
+
return new Decimal(fieldTotal).add(_.get(customer, desc.fieldPath)).toDP(2).toNumber();
|
|
838
|
+
}
|
|
839
|
+
return fieldTotal;
|
|
840
|
+
}, 0);
|
|
841
|
+
return { ...acc, [desc.replacedHeader ?? field]: desc.reverse ? total * -1 : total };
|
|
842
|
+
}, (<IAvailabilitySummary>{}));
|
|
843
|
+
// TODO quick solution for creditNotesNegative
|
|
844
|
+
summary.balanceAfterCrossAge = summary.balanceAfterCrossAge + summary.creditNotesNegative;
|
|
845
|
+
const insuredComponent = summary.insuredComponent === 0 ? 0 : summary.insuredComponent;
|
|
846
|
+
const uninsuredComponent = new Decimal(summary.finalResult).sub(insuredComponent).toNumber();
|
|
847
|
+
summary.insuredComponent = insuredComponent;
|
|
848
|
+
summary.uninsuredComponent = uninsuredComponent;
|
|
849
|
+
const insuredPart = new Decimal(insuredComponent).mul(newCalculation.settings.insuredARAdvanceRate);
|
|
850
|
+
const uninsuredPart = new Decimal(uninsuredComponent).mul(newCalculation.settings.uninsuredARAdvanceRate);
|
|
851
|
+
summary.ARAvailability = new Decimal(insuredPart).add(uninsuredPart).toDP(2).toNumber();
|
|
852
|
+
return summary;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
async saveCalculatedAvailability(newCalculation: IReceivableAvailability): Promise<void> {
|
|
856
|
+
const savedAvailability = await ReceivableAvailabilityModel.findOneAndReplace(
|
|
857
|
+
{ bbcDateId: newCalculation.bbcDateId },
|
|
858
|
+
newCalculation,
|
|
859
|
+
{ upsert: true, new: true },
|
|
860
|
+
);
|
|
861
|
+
await ReceivableAvailabilityItemModel.deleteMany({ availabilityId: savedAvailability._id });
|
|
862
|
+
const orderedItems = newCalculation.items.map((item, order) => (<IReceivableAvailabilityItem>{ ...item, order }));
|
|
863
|
+
await Promise.all(orderedItems.map(async (item) => {
|
|
864
|
+
const newCollateralItem = new ReceivableAvailabilityItemModel({
|
|
865
|
+
availabilityId: savedAvailability._id,
|
|
866
|
+
order: item.order,
|
|
867
|
+
...item,
|
|
868
|
+
});
|
|
869
|
+
try {
|
|
870
|
+
await newCollateralItem.save();
|
|
871
|
+
} catch (e) {
|
|
872
|
+
console.error(e);
|
|
873
|
+
}
|
|
874
|
+
}));
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
async getCalculationSettings(bbcDateId: string): Promise<IAvailabilitySettings> {
|
|
878
|
+
const availability = await ReceivableAvailabilityModel.findOne({ bbcDateId: bbcDateId }).lean();
|
|
879
|
+
if (!availability || !availability.settings || _.isEmpty(availability.settings)) {
|
|
880
|
+
return {
|
|
881
|
+
standardARDays: 90,
|
|
882
|
+
customerConcentrationLimit: 0.2,
|
|
883
|
+
crossAgeLimit: 0.25,
|
|
884
|
+
contraProvision: 1.25,
|
|
885
|
+
paidDays: 180,
|
|
886
|
+
insuredARAdvanceRate: 0,
|
|
887
|
+
uninsuredARAdvanceRate: 0,
|
|
888
|
+
excludeFromTotal: true,
|
|
889
|
+
waiveInsuredCustomers: true,
|
|
890
|
+
excludedCustomers: [],
|
|
891
|
+
insuredCustomers: [],
|
|
892
|
+
permittedConcentration: [],
|
|
893
|
+
permittedExtendedARDays: [],
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
return availability.settings;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
async getAllSettings(bbcDateId: string = null) {
|
|
900
|
+
const selectedAvailability = await ReceivableAvailabilityModel.findOne({ bbcDateId: bbcDateId }).lean();
|
|
901
|
+
const availabilities: IReceivableAvailabilityDoc[] = await ReceivableAvailabilityModel.aggregate([
|
|
902
|
+
{
|
|
903
|
+
'$match': {
|
|
904
|
+
'$and': [
|
|
905
|
+
{ 'borrowerId': new mongoose.Types.ObjectId(selectedAvailability.borrowerId) },
|
|
906
|
+
{ 'bbcDateId': { '$ne': new mongoose.Types.ObjectId(bbcDateId) } },
|
|
907
|
+
],
|
|
908
|
+
},
|
|
909
|
+
},
|
|
910
|
+
]);
|
|
911
|
+
return availabilities.reduce((acc, a) => {
|
|
912
|
+
const settings = Object.values(ELimitSettings).reduce((accSettings, limitSettings) => {
|
|
913
|
+
return { ...accSettings, [limitSettings]: a.settings[limitSettings] };
|
|
914
|
+
}, {});
|
|
915
|
+
return { ...acc, [a.bbcDateId.toString()]: settings };
|
|
916
|
+
}, {});
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
async getAvailabilityItems(availabilityId: string, paginatorOptions: IPaginatorOptions) {
|
|
920
|
+
return ReceivableAvailabilityItemModel.aggregate<IReceivableAvailabilityItemView>([
|
|
921
|
+
{
|
|
922
|
+
$match: {
|
|
923
|
+
'availabilityId': new mongoose.Types.ObjectId(availabilityId),
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
$sort: {
|
|
928
|
+
'order': 1,
|
|
929
|
+
},
|
|
930
|
+
},
|
|
931
|
+
...ITEMS_PAGINATION(paginatorOptions),
|
|
932
|
+
{
|
|
933
|
+
$lookup: {
|
|
934
|
+
from: MODEL_NAMES.receivableItems,
|
|
935
|
+
localField: 'invoiceId',
|
|
936
|
+
foreignField: '_id',
|
|
937
|
+
as: 'invoice',
|
|
938
|
+
},
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
$unwind: {
|
|
942
|
+
path: '$invoice',
|
|
943
|
+
},
|
|
944
|
+
},
|
|
945
|
+
]);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
async getUniqueCustomers(availabilityId: string) {
|
|
949
|
+
return ReceivableAvailabilityItemModel.aggregate<{ customerTitle: string }>([
|
|
950
|
+
{
|
|
951
|
+
$match: {
|
|
952
|
+
'availabilityId': new mongoose.Types.ObjectId(availabilityId),
|
|
953
|
+
},
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
$lookup: {
|
|
957
|
+
from: MODEL_NAMES.receivableItems,
|
|
958
|
+
localField: 'invoiceId',
|
|
959
|
+
foreignField: '_id',
|
|
960
|
+
as: 'invoice',
|
|
961
|
+
},
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
$unwind: {
|
|
965
|
+
path: '$invoice',
|
|
966
|
+
},
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
$group: {
|
|
970
|
+
'_id': null,
|
|
971
|
+
'customerTitle': {
|
|
972
|
+
$addToSet: '$invoice.customerTitle',
|
|
973
|
+
},
|
|
974
|
+
},
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
$project:
|
|
978
|
+
{
|
|
979
|
+
'_id': 0,
|
|
980
|
+
'customerTitle': 1,
|
|
981
|
+
},
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
$unwind:
|
|
985
|
+
{
|
|
986
|
+
path: '$customerTitle',
|
|
987
|
+
},
|
|
988
|
+
},
|
|
989
|
+
]);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
async getAllSummaries(bbcDateId: string, user: IUserDocument): Promise<IAvailabilityFullSummary> {
|
|
993
|
+
const equipment = await this.equipmentService.getEquipmentForBBC(bbcDateId);
|
|
994
|
+
const reserve = await this.reserveService.getReserve(bbcDateId);
|
|
995
|
+
const loanBalances = await this.loanTransactionsService.getLoanBalanceForBBCDateId(bbcDateId);
|
|
996
|
+
const collateralAdjustmentSummary = await this.collateralAdjustmentsService.getCollateralAdjustmentSummary(bbcDateId);
|
|
997
|
+
const inventoryAvailability = await this.inventoryAvailabilityService.getInventoryAvailability(bbcDateId, START_PAGINATORS);
|
|
998
|
+
const receivableAvailability = await this.getAvailability(
|
|
999
|
+
bbcDateId,
|
|
1000
|
+
{ pageIndex: null, pageSize: null },
|
|
1001
|
+
user,
|
|
1002
|
+
);
|
|
1003
|
+
const savedData = await this.signsService.getSavedData(bbcDateId);
|
|
1004
|
+
if (!savedData.accruedStatement) {
|
|
1005
|
+
savedData.accruedStatement = await this.loanStatementService.calculateAccruedStatementForBBC(bbcDateId);
|
|
1006
|
+
}
|
|
1007
|
+
return {
|
|
1008
|
+
reserve: reserve.summary,
|
|
1009
|
+
equipment: equipment.summary,
|
|
1010
|
+
inventory: inventoryAvailability.summary,
|
|
1011
|
+
receivable: receivableAvailability.summary,
|
|
1012
|
+
loanBalances,
|
|
1013
|
+
collateralAdjustment: { summary: collateralAdjustmentSummary },
|
|
1014
|
+
manualInputs: {
|
|
1015
|
+
receivableAvailability: receivableAvailability.useManualInputs,
|
|
1016
|
+
inventoryAvailability: inventoryAvailability.useManualInputs,
|
|
1017
|
+
},
|
|
1018
|
+
...savedData,
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
async isBBCEditable(bbcDateId: string): Promise<boolean> {
|
|
1023
|
+
const availabilitiesSigns = await getAvailabilitySigns([bbcDateId]);
|
|
1024
|
+
if (!availabilitiesSigns.length) {
|
|
1025
|
+
return true;
|
|
1026
|
+
}
|
|
1027
|
+
const aSign: IAvailabilitySignView = availabilitiesSigns[0];
|
|
1028
|
+
return aSign.signs.filter((s) => !s.revokedAt).length < aSign.requiredSigns;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
async toggleManualReceivableAvailability(bbcDateId: string, useManualInputs: boolean) {
|
|
1032
|
+
await setReceivableManualInputsToggle(bbcDateId, useManualInputs);
|
|
1033
|
+
}
|
|
1034
|
+
}
|