@grupolapa/cotizador-sdk 0.1.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/README.md +125 -0
- package/dist/bundle-payment-schedule.calc.d.ts +2 -0
- package/dist/bundle-payment-schedule.calc.js +160 -0
- package/dist/calculate-quote.d.ts +2 -0
- package/dist/calculate-quote.js +382 -0
- package/dist/corporate-actions.d.ts +95 -0
- package/dist/corporate-actions.js +87 -0
- package/dist/corporate-rent.d.ts +5 -0
- package/dist/corporate-rent.js +43 -0
- package/dist/delivery-date.d.ts +6 -0
- package/dist/delivery-date.js +22 -0
- package/dist/entrada.d.ts +111 -0
- package/dist/entrada.js +139 -0
- package/dist/format.d.ts +7 -0
- package/dist/format.js +37 -0
- package/dist/http.d.ts +28 -0
- package/dist/http.js +133 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +22 -0
- package/dist/installment-allocation.d.ts +30 -0
- package/dist/installment-allocation.js +69 -0
- package/dist/inventory-availability.d.ts +10 -0
- package/dist/inventory-availability.js +105 -0
- package/dist/live-inventory-availability.d.ts +16 -0
- package/dist/live-inventory-availability.js +88 -0
- package/dist/payment-schemes.d.ts +22 -0
- package/dist/payment-schemes.js +99 -0
- package/dist/post-delivery-value.d.ts +3 -0
- package/dist/post-delivery-value.js +61 -0
- package/dist/product-types.d.ts +16 -0
- package/dist/product-types.js +82 -0
- package/dist/query-state.d.ts +5 -0
- package/dist/query-state.js +157 -0
- package/dist/quote-outcome-graph.d.ts +31 -0
- package/dist/quote-outcome-graph.js +113 -0
- package/dist/quote.d.ts +19 -0
- package/dist/quote.js +18 -0
- package/dist/selection-state.d.ts +34 -0
- package/dist/selection-state.js +156 -0
- package/dist/server.d.ts +16 -0
- package/dist/server.js +17 -0
- package/dist/sitemap.d.ts +22 -0
- package/dist/sitemap.js +45 -0
- package/dist/social-quote.d.ts +8 -0
- package/dist/social-quote.js +51 -0
- package/dist/tax.d.ts +5 -0
- package/dist/tax.js +11 -0
- package/dist/types.d.ts +502 -0
- package/dist/types.js +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { buildCorporatePlan, type EntradaActionSchemeId } from "./entrada.js";
|
|
2
|
+
import type { AppContent, InventoryAvailabilityByUnitId, InventoryUnit } from "./types.js";
|
|
3
|
+
export declare const CORPORATE_ACTION_MIN_COUNT = 1;
|
|
4
|
+
export declare const CORPORATE_ACTION_MAX_COUNT = 5;
|
|
5
|
+
export declare const CORPORATE_ACTION_DEFAULT_SCHEME_ID: "contado";
|
|
6
|
+
export declare const CORPORATE_ACTION_MIN_ENGANCHE_SCHEME_ID = "amplio";
|
|
7
|
+
export type CorporateActionPlan = ReturnType<typeof buildCorporatePlan>;
|
|
8
|
+
export declare function isCorporateActionUnit(unit: InventoryUnit | null | undefined, content: AppContent): boolean;
|
|
9
|
+
export declare function getCorporateActionTotalShares({ availabilityByUnitId, content, unit, }: {
|
|
10
|
+
availabilityByUnitId: InventoryAvailabilityByUnitId;
|
|
11
|
+
content: AppContent;
|
|
12
|
+
unit: InventoryUnit;
|
|
13
|
+
}): number;
|
|
14
|
+
export declare function clampCorporateActionCount(value: number): number;
|
|
15
|
+
export declare function parseCorporateActionCount(value: string | undefined): number;
|
|
16
|
+
export declare function normalizeCorporateActionSchemeId(value: string | null | undefined): EntradaActionSchemeId;
|
|
17
|
+
export declare function buildCorporateActionPlan({ actionCount, availabilityByUnitId, content, schemeId, unit, }: {
|
|
18
|
+
actionCount: number;
|
|
19
|
+
availabilityByUnitId: InventoryAvailabilityByUnitId;
|
|
20
|
+
content: AppContent;
|
|
21
|
+
schemeId: EntradaActionSchemeId | string | null | undefined;
|
|
22
|
+
unit: InventoryUnit;
|
|
23
|
+
}): {
|
|
24
|
+
capitalMXN: number;
|
|
25
|
+
engancheMXN: number;
|
|
26
|
+
m2: number;
|
|
27
|
+
monthlyCount: 0 | 5 | 13;
|
|
28
|
+
monthlyPaymentMXN: number;
|
|
29
|
+
numActions: number;
|
|
30
|
+
operationMonthlyReturnMXN: number;
|
|
31
|
+
operationAnnualReturnNetMXN: number;
|
|
32
|
+
operationMonthlyReturnNetMXN: number;
|
|
33
|
+
operationReturnHorizonNetMXN: number;
|
|
34
|
+
obraReturnMonthlyMXN: number;
|
|
35
|
+
obraReturnMonthlyNetMXN: number;
|
|
36
|
+
obraReturnNetMXN: number;
|
|
37
|
+
patrimonioTotalMXN: number;
|
|
38
|
+
plusvaliaGainMXN: number;
|
|
39
|
+
pricePerActionMXN: number;
|
|
40
|
+
projectedAssetValueMXN: number;
|
|
41
|
+
scheme: {
|
|
42
|
+
readonly id: "contado";
|
|
43
|
+
readonly description: "Liquidas tu acción en una sola exhibición y accedes a tasa preferencial desde el mes 1.";
|
|
44
|
+
readonly engancheRatio: 1;
|
|
45
|
+
readonly label: "Contado";
|
|
46
|
+
readonly monthlyCount: 0;
|
|
47
|
+
readonly monthlyRatio: 0;
|
|
48
|
+
readonly tag: "Máximo rendimiento";
|
|
49
|
+
readonly tasaObra: 0.08;
|
|
50
|
+
readonly tasaObraLabel: "8%";
|
|
51
|
+
} | {
|
|
52
|
+
readonly id: "medio";
|
|
53
|
+
readonly description: "Enganche de $120K y 5 mensualidades de $40K. Rendimiento del 6% anual una vez liquidado el capital.";
|
|
54
|
+
readonly engancheRatio: number;
|
|
55
|
+
readonly label: "Financiado 5 meses";
|
|
56
|
+
readonly monthlyCount: 5;
|
|
57
|
+
readonly monthlyRatio: number;
|
|
58
|
+
readonly tag: "Balance";
|
|
59
|
+
readonly tasaObra: 0.06;
|
|
60
|
+
readonly tasaObraLabel: "6%";
|
|
61
|
+
} | {
|
|
62
|
+
readonly id: "amplio";
|
|
63
|
+
readonly description: "Enganche de $60K y 13 mensualidades de $20K. Rendimiento del 4% anual una vez liquidado el capital.";
|
|
64
|
+
readonly engancheRatio: number;
|
|
65
|
+
readonly label: "Financiado 13 meses";
|
|
66
|
+
readonly monthlyCount: 13;
|
|
67
|
+
readonly monthlyRatio: number;
|
|
68
|
+
readonly tag: "Más accesible";
|
|
69
|
+
readonly tasaObra: 0.04;
|
|
70
|
+
readonly tasaObraLabel: "4%";
|
|
71
|
+
};
|
|
72
|
+
totalMonths: 0 | 5 | 13;
|
|
73
|
+
totalPaidMXN: number;
|
|
74
|
+
};
|
|
75
|
+
export declare function getCorporateActionUnitDisplay({ availabilityByUnitId, content, unit, }: {
|
|
76
|
+
availabilityByUnitId: InventoryAvailabilityByUnitId;
|
|
77
|
+
content: AppContent;
|
|
78
|
+
unit: InventoryUnit;
|
|
79
|
+
}): {
|
|
80
|
+
m2PerAction: number;
|
|
81
|
+
monthlyRentPerActionMXN: number;
|
|
82
|
+
pricePerActionMXN: number;
|
|
83
|
+
totalShares: number;
|
|
84
|
+
};
|
|
85
|
+
export declare function buildCorporateActionProductLabel(plan: CorporateActionPlan): string;
|
|
86
|
+
export declare function buildCorporateActionCheckoutSelection(plan: CorporateActionPlan): {
|
|
87
|
+
capitalMXN: number;
|
|
88
|
+
engancheMXN: number;
|
|
89
|
+
m2: number;
|
|
90
|
+
monthlyPaymentMXN: number;
|
|
91
|
+
numActions: number;
|
|
92
|
+
operationMonthlyReturnNetMXN: number;
|
|
93
|
+
pricePerActionMXN: number;
|
|
94
|
+
schemeId: "contado" | "medio" | "amplio";
|
|
95
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { isCorporateRentalUnit } from "./corporate-rent.js";
|
|
2
|
+
import { buildCorporatePlan, ENTRADA_ACTION_SCHEMES, getCorporateM2PerAction, getCorporatePricePerActionMXN, } from "./entrada.js";
|
|
3
|
+
import { getInventoryAvailability } from "./inventory-availability.js";
|
|
4
|
+
export const CORPORATE_ACTION_MIN_COUNT = 1;
|
|
5
|
+
export const CORPORATE_ACTION_MAX_COUNT = 5;
|
|
6
|
+
export const CORPORATE_ACTION_DEFAULT_SCHEME_ID = ENTRADA_ACTION_SCHEMES[0].id;
|
|
7
|
+
export const CORPORATE_ACTION_MIN_ENGANCHE_SCHEME_ID = "amplio";
|
|
8
|
+
function getProductTypeForUnit(content, unit) {
|
|
9
|
+
return (content.productTypes.find((productType) => productType.id === unit.productTypeId) ?? null);
|
|
10
|
+
}
|
|
11
|
+
export function isCorporateActionUnit(unit, content) {
|
|
12
|
+
if (!unit) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const productType = getProductTypeForUnit(content, unit);
|
|
16
|
+
const usesShareInventory = unit.inventoryMode === "shares" ||
|
|
17
|
+
productType?.inventoryMode === "shares" ||
|
|
18
|
+
Boolean(productType?.shareInventory);
|
|
19
|
+
if (!usesShareInventory) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return (unit.id === content.entryInventory.corporateUnitId ||
|
|
23
|
+
isCorporateRentalUnit(unit));
|
|
24
|
+
}
|
|
25
|
+
export function getCorporateActionTotalShares({ availabilityByUnitId, content, unit, }) {
|
|
26
|
+
const availability = getInventoryAvailability(unit, availabilityByUnitId);
|
|
27
|
+
const productType = getProductTypeForUnit(content, unit);
|
|
28
|
+
return Math.max(availability?.share?.total ?? productType?.shareInventory?.totalShares ?? 1, 1);
|
|
29
|
+
}
|
|
30
|
+
export function clampCorporateActionCount(value) {
|
|
31
|
+
if (!Number.isFinite(value)) {
|
|
32
|
+
return CORPORATE_ACTION_MIN_COUNT;
|
|
33
|
+
}
|
|
34
|
+
return Math.min(CORPORATE_ACTION_MAX_COUNT, Math.max(CORPORATE_ACTION_MIN_COUNT, Math.round(value)));
|
|
35
|
+
}
|
|
36
|
+
export function parseCorporateActionCount(value) {
|
|
37
|
+
return clampCorporateActionCount(Number(value));
|
|
38
|
+
}
|
|
39
|
+
export function normalizeCorporateActionSchemeId(value) {
|
|
40
|
+
return (ENTRADA_ACTION_SCHEMES.find((scheme) => scheme.id === value)?.id ??
|
|
41
|
+
CORPORATE_ACTION_DEFAULT_SCHEME_ID);
|
|
42
|
+
}
|
|
43
|
+
export function buildCorporateActionPlan({ actionCount, availabilityByUnitId, content, schemeId, unit, }) {
|
|
44
|
+
return buildCorporatePlan({
|
|
45
|
+
inflationRate: content.financeRules.corporateInflationRate,
|
|
46
|
+
numActions: clampCorporateActionCount(actionCount),
|
|
47
|
+
requestedSchemeId: schemeId,
|
|
48
|
+
rentalRate: content.financeRules.accionesRentalRate,
|
|
49
|
+
totalShares: getCorporateActionTotalShares({
|
|
50
|
+
availabilityByUnitId,
|
|
51
|
+
content,
|
|
52
|
+
unit,
|
|
53
|
+
}),
|
|
54
|
+
unit,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
export function getCorporateActionUnitDisplay({ availabilityByUnitId, content, unit, }) {
|
|
58
|
+
const totalShares = getCorporateActionTotalShares({
|
|
59
|
+
availabilityByUnitId,
|
|
60
|
+
content,
|
|
61
|
+
unit,
|
|
62
|
+
});
|
|
63
|
+
const pricePerActionMXN = getCorporatePricePerActionMXN(unit, totalShares);
|
|
64
|
+
const m2PerAction = getCorporateM2PerAction(unit, totalShares);
|
|
65
|
+
return {
|
|
66
|
+
m2PerAction,
|
|
67
|
+
monthlyRentPerActionMXN: (pricePerActionMXN * content.financeRules.accionesRentalRate) / 12,
|
|
68
|
+
pricePerActionMXN,
|
|
69
|
+
totalShares,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function buildCorporateActionProductLabel(plan) {
|
|
73
|
+
const actionLabel = `${plan.numActions} ${plan.numActions === 1 ? "acción Serie B" : "acciones Serie B"}`;
|
|
74
|
+
return `Acciones corporativo · ${actionLabel}`;
|
|
75
|
+
}
|
|
76
|
+
export function buildCorporateActionCheckoutSelection(plan) {
|
|
77
|
+
return {
|
|
78
|
+
capitalMXN: plan.totalPaidMXN,
|
|
79
|
+
engancheMXN: plan.engancheMXN,
|
|
80
|
+
m2: plan.m2,
|
|
81
|
+
monthlyPaymentMXN: plan.monthlyPaymentMXN,
|
|
82
|
+
numActions: plan.numActions,
|
|
83
|
+
operationMonthlyReturnNetMXN: plan.operationMonthlyReturnNetMXN,
|
|
84
|
+
pricePerActionMXN: plan.pricePerActionMXN,
|
|
85
|
+
schemeId: plan.scheme.id,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { InventoryUnit } from "./types.js";
|
|
2
|
+
export declare const DEFAULT_CORPORATE_INFLATION_RATE = 0.04;
|
|
3
|
+
export declare function isCorporateRentalUnit(unit: Pick<InventoryUnit, "category" | "name" | "productTypeName" | "unitNumber">): boolean;
|
|
4
|
+
export declare function getEscalatedAnnualAmountMXN(baseAnnualAmountMXN: number, annualEscalationRate: number, yearNumber: number): number;
|
|
5
|
+
export declare function getEscalatedAmountOverHorizonMXN(baseAnnualAmountMXN: number, annualEscalationRate: number, years: number): number;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const DEFAULT_CORPORATE_INFLATION_RATE = 0.04;
|
|
2
|
+
const CORPORATE_RENT_CATEGORY_TOKENS = new Set(["corporativo", "acciones"]);
|
|
3
|
+
function normalizeComparableLabel(value) {
|
|
4
|
+
return (value ?? "")
|
|
5
|
+
.normalize("NFD")
|
|
6
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.trim()
|
|
9
|
+
.replace(/\s+/g, " ");
|
|
10
|
+
}
|
|
11
|
+
function matchesCorporateToken(value) {
|
|
12
|
+
const normalizedValue = normalizeComparableLabel(value);
|
|
13
|
+
if (!normalizedValue) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (CORPORATE_RENT_CATEGORY_TOKENS.has(normalizedValue)) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return [...CORPORATE_RENT_CATEGORY_TOKENS].some((token) => normalizedValue.includes(token));
|
|
20
|
+
}
|
|
21
|
+
export function isCorporateRentalUnit(unit) {
|
|
22
|
+
return (matchesCorporateToken(unit.category) ||
|
|
23
|
+
matchesCorporateToken(unit.productTypeName) ||
|
|
24
|
+
matchesCorporateToken(unit.name) ||
|
|
25
|
+
matchesCorporateToken(unit.unitNumber));
|
|
26
|
+
}
|
|
27
|
+
export function getEscalatedAnnualAmountMXN(baseAnnualAmountMXN, annualEscalationRate, yearNumber) {
|
|
28
|
+
return (baseAnnualAmountMXN *
|
|
29
|
+
Math.pow(1 + Math.max(annualEscalationRate, 0), Math.max(yearNumber - 1, 0)));
|
|
30
|
+
}
|
|
31
|
+
export function getEscalatedAmountOverHorizonMXN(baseAnnualAmountMXN, annualEscalationRate, years) {
|
|
32
|
+
let totalMXN = 0;
|
|
33
|
+
let remainingYears = Math.max(years, 0);
|
|
34
|
+
let yearNumber = 1;
|
|
35
|
+
while (remainingYears > 0) {
|
|
36
|
+
const yearFraction = Math.min(remainingYears, 1);
|
|
37
|
+
totalMXN +=
|
|
38
|
+
getEscalatedAnnualAmountMXN(baseAnnualAmountMXN, annualEscalationRate, yearNumber) * yearFraction;
|
|
39
|
+
remainingYears -= yearFraction;
|
|
40
|
+
yearNumber += 1;
|
|
41
|
+
}
|
|
42
|
+
return totalMXN;
|
|
43
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function normalizeDeliveryDateInput(value: string | null | undefined): string | undefined;
|
|
2
|
+
export declare function parseDeliveryDateParts(value: string): {
|
|
3
|
+
year: number;
|
|
4
|
+
month: number;
|
|
5
|
+
} | null;
|
|
6
|
+
export declare function isValidDeliveryDateString(value: string): boolean;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function normalizeDeliveryDateInput(value) {
|
|
2
|
+
const trimmed = value?.trim();
|
|
3
|
+
return trimmed ? trimmed : undefined;
|
|
4
|
+
}
|
|
5
|
+
export function parseDeliveryDateParts(value) {
|
|
6
|
+
const match = /^(\d{4})-(\d{2})$/.exec(value.trim());
|
|
7
|
+
if (!match) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const year = Number(match[1]);
|
|
11
|
+
const month = Number(match[2]);
|
|
12
|
+
if (!Number.isFinite(year) ||
|
|
13
|
+
!Number.isFinite(month) ||
|
|
14
|
+
month < 1 ||
|
|
15
|
+
month > 12) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return { year, month };
|
|
19
|
+
}
|
|
20
|
+
export function isValidDeliveryDateString(value) {
|
|
21
|
+
return parseDeliveryDateParts(value) !== null;
|
|
22
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { InventoryUnit } from "./types.js";
|
|
2
|
+
export declare const ENTRADA_BOOKING_AMOUNT_MXN = 10000;
|
|
3
|
+
export declare const APARTMENT_TOTAL_MONTHS = 36;
|
|
4
|
+
export declare const ENTRADA_ACTION_SCHEMES: readonly [{
|
|
5
|
+
readonly id: "contado";
|
|
6
|
+
readonly description: "Liquidas tu acción en una sola exhibición y accedes a tasa preferencial desde el mes 1.";
|
|
7
|
+
readonly engancheRatio: 1;
|
|
8
|
+
readonly label: "Contado";
|
|
9
|
+
readonly monthlyCount: 0;
|
|
10
|
+
readonly monthlyRatio: 0;
|
|
11
|
+
readonly tag: "Máximo rendimiento";
|
|
12
|
+
readonly tasaObra: 0.08;
|
|
13
|
+
readonly tasaObraLabel: "8%";
|
|
14
|
+
}, {
|
|
15
|
+
readonly id: "medio";
|
|
16
|
+
readonly description: "Enganche de $120K y 5 mensualidades de $40K. Rendimiento del 6% anual una vez liquidado el capital.";
|
|
17
|
+
readonly engancheRatio: number;
|
|
18
|
+
readonly label: "Financiado 5 meses";
|
|
19
|
+
readonly monthlyCount: 5;
|
|
20
|
+
readonly monthlyRatio: number;
|
|
21
|
+
readonly tag: "Balance";
|
|
22
|
+
readonly tasaObra: 0.06;
|
|
23
|
+
readonly tasaObraLabel: "6%";
|
|
24
|
+
}, {
|
|
25
|
+
readonly id: "amplio";
|
|
26
|
+
readonly description: "Enganche de $60K y 13 mensualidades de $20K. Rendimiento del 4% anual una vez liquidado el capital.";
|
|
27
|
+
readonly engancheRatio: number;
|
|
28
|
+
readonly label: "Financiado 13 meses";
|
|
29
|
+
readonly monthlyCount: 13;
|
|
30
|
+
readonly monthlyRatio: number;
|
|
31
|
+
readonly tag: "Más accesible";
|
|
32
|
+
readonly tasaObra: 0.04;
|
|
33
|
+
readonly tasaObraLabel: "4%";
|
|
34
|
+
}];
|
|
35
|
+
type EntradaActionScheme = (typeof ENTRADA_ACTION_SCHEMES)[number];
|
|
36
|
+
export type EntradaActionSchemeId = EntradaActionScheme["id"];
|
|
37
|
+
export declare function getApartmentPartValueMXN(unit: InventoryUnit): number;
|
|
38
|
+
export declare function buildApartmentPlan(unit: InventoryUnit, requestedParts: number, rentalRate?: number): {
|
|
39
|
+
deliveryValueMXN: number;
|
|
40
|
+
engancheMXN: number;
|
|
41
|
+
fullLotValueMXN: number;
|
|
42
|
+
monthlyPaymentMXN: number;
|
|
43
|
+
numParts: number;
|
|
44
|
+
rentaMensualMXN: number;
|
|
45
|
+
saldoFinalMXN: number;
|
|
46
|
+
totalMonths: number;
|
|
47
|
+
totalToPayMXN: number;
|
|
48
|
+
yourValueMXN: number;
|
|
49
|
+
};
|
|
50
|
+
export declare function getCorporatePricePerActionMXN(unit: InventoryUnit, totalShares: number): number;
|
|
51
|
+
export declare function getCorporateM2PerAction(unit: InventoryUnit, totalShares: number): number;
|
|
52
|
+
export declare function buildCorporatePlan(args: {
|
|
53
|
+
inflationRate: number;
|
|
54
|
+
numActions: number;
|
|
55
|
+
requestedSchemeId: EntradaActionSchemeId | string | null | undefined;
|
|
56
|
+
rentalRate: number;
|
|
57
|
+
totalShares: number;
|
|
58
|
+
unit: InventoryUnit;
|
|
59
|
+
}): {
|
|
60
|
+
capitalMXN: number;
|
|
61
|
+
engancheMXN: number;
|
|
62
|
+
m2: number;
|
|
63
|
+
monthlyCount: 0 | 5 | 13;
|
|
64
|
+
monthlyPaymentMXN: number;
|
|
65
|
+
numActions: number;
|
|
66
|
+
operationMonthlyReturnMXN: number;
|
|
67
|
+
operationAnnualReturnNetMXN: number;
|
|
68
|
+
operationMonthlyReturnNetMXN: number;
|
|
69
|
+
operationReturnHorizonNetMXN: number;
|
|
70
|
+
obraReturnMonthlyMXN: number;
|
|
71
|
+
obraReturnMonthlyNetMXN: number;
|
|
72
|
+
obraReturnNetMXN: number;
|
|
73
|
+
patrimonioTotalMXN: number;
|
|
74
|
+
plusvaliaGainMXN: number;
|
|
75
|
+
pricePerActionMXN: number;
|
|
76
|
+
projectedAssetValueMXN: number;
|
|
77
|
+
scheme: {
|
|
78
|
+
readonly id: "contado";
|
|
79
|
+
readonly description: "Liquidas tu acción en una sola exhibición y accedes a tasa preferencial desde el mes 1.";
|
|
80
|
+
readonly engancheRatio: 1;
|
|
81
|
+
readonly label: "Contado";
|
|
82
|
+
readonly monthlyCount: 0;
|
|
83
|
+
readonly monthlyRatio: 0;
|
|
84
|
+
readonly tag: "Máximo rendimiento";
|
|
85
|
+
readonly tasaObra: 0.08;
|
|
86
|
+
readonly tasaObraLabel: "8%";
|
|
87
|
+
} | {
|
|
88
|
+
readonly id: "medio";
|
|
89
|
+
readonly description: "Enganche de $120K y 5 mensualidades de $40K. Rendimiento del 6% anual una vez liquidado el capital.";
|
|
90
|
+
readonly engancheRatio: number;
|
|
91
|
+
readonly label: "Financiado 5 meses";
|
|
92
|
+
readonly monthlyCount: 5;
|
|
93
|
+
readonly monthlyRatio: number;
|
|
94
|
+
readonly tag: "Balance";
|
|
95
|
+
readonly tasaObra: 0.06;
|
|
96
|
+
readonly tasaObraLabel: "6%";
|
|
97
|
+
} | {
|
|
98
|
+
readonly id: "amplio";
|
|
99
|
+
readonly description: "Enganche de $60K y 13 mensualidades de $20K. Rendimiento del 4% anual una vez liquidado el capital.";
|
|
100
|
+
readonly engancheRatio: number;
|
|
101
|
+
readonly label: "Financiado 13 meses";
|
|
102
|
+
readonly monthlyCount: 13;
|
|
103
|
+
readonly monthlyRatio: number;
|
|
104
|
+
readonly tag: "Más accesible";
|
|
105
|
+
readonly tasaObra: 0.04;
|
|
106
|
+
readonly tasaObraLabel: "4%";
|
|
107
|
+
};
|
|
108
|
+
totalMonths: 0 | 5 | 13;
|
|
109
|
+
totalPaidMXN: number;
|
|
110
|
+
};
|
|
111
|
+
export {};
|
package/dist/entrada.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { getUnitListPriceMXN } from "./payment-schemes.js";
|
|
2
|
+
import { getEscalatedAmountOverHorizonMXN } from "./corporate-rent.js";
|
|
3
|
+
const APARTMENT_TOTAL_PARTS = 6;
|
|
4
|
+
const APARTMENT_COPROP_PRICE_MULTIPLIER = 1.05;
|
|
5
|
+
const APARTMENT_ENGANCHE_RATIO = 60000 / 325000;
|
|
6
|
+
const APARTMENT_SALDO_FINAL_RATIO = 60000 / 325000;
|
|
7
|
+
const APARTMENT_MENSUALIDAD_RATIO = 6000 / 325000;
|
|
8
|
+
const APARTMENT_TOTAL_RENT_RATIO = 18000 / 1950000;
|
|
9
|
+
const APARTMENT_DELIVERY_VALUE_RATIO = 2900000 / 1950000;
|
|
10
|
+
const CORPORATE_PLUSVALIA_ANUAL = 0.1;
|
|
11
|
+
const CORPORATE_ISR_RETENCION = 0.1;
|
|
12
|
+
const CORPORATE_HORIZONTE_YEARS = 7;
|
|
13
|
+
const CORPORATE_OBRA_MESES = 39;
|
|
14
|
+
export const ENTRADA_BOOKING_AMOUNT_MXN = 10000;
|
|
15
|
+
export const APARTMENT_TOTAL_MONTHS = 36;
|
|
16
|
+
export const ENTRADA_ACTION_SCHEMES = [
|
|
17
|
+
{
|
|
18
|
+
id: "contado",
|
|
19
|
+
description: "Liquidas tu acción en una sola exhibición y accedes a tasa preferencial desde el mes 1.",
|
|
20
|
+
engancheRatio: 1,
|
|
21
|
+
label: "Contado",
|
|
22
|
+
monthlyCount: 0,
|
|
23
|
+
monthlyRatio: 0,
|
|
24
|
+
tag: "Máximo rendimiento",
|
|
25
|
+
tasaObra: 0.08,
|
|
26
|
+
tasaObraLabel: "8%",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "medio",
|
|
30
|
+
description: "Enganche de $120K y 5 mensualidades de $40K. Rendimiento del 6% anual una vez liquidado el capital.",
|
|
31
|
+
engancheRatio: 120000 / 320000,
|
|
32
|
+
label: "Financiado 5 meses",
|
|
33
|
+
monthlyCount: 5,
|
|
34
|
+
monthlyRatio: 40000 / 320000,
|
|
35
|
+
tag: "Balance",
|
|
36
|
+
tasaObra: 0.06,
|
|
37
|
+
tasaObraLabel: "6%",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "amplio",
|
|
41
|
+
description: "Enganche de $60K y 13 mensualidades de $20K. Rendimiento del 4% anual una vez liquidado el capital.",
|
|
42
|
+
engancheRatio: 60000 / 320000,
|
|
43
|
+
label: "Financiado 13 meses",
|
|
44
|
+
monthlyCount: 13,
|
|
45
|
+
monthlyRatio: 20000 / 320000,
|
|
46
|
+
tag: "Más accesible",
|
|
47
|
+
tasaObra: 0.04,
|
|
48
|
+
tasaObraLabel: "4%",
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
function roundCurrency(value) {
|
|
52
|
+
return Math.round(value);
|
|
53
|
+
}
|
|
54
|
+
function clamp(value, min, max) {
|
|
55
|
+
return Math.min(max, Math.max(min, value));
|
|
56
|
+
}
|
|
57
|
+
function getActionScheme(schemeId) {
|
|
58
|
+
return (ENTRADA_ACTION_SCHEMES.find((scheme) => scheme.id === schemeId) ??
|
|
59
|
+
ENTRADA_ACTION_SCHEMES[0]);
|
|
60
|
+
}
|
|
61
|
+
export function getApartmentPartValueMXN(unit) {
|
|
62
|
+
return getUnitListPriceMXN(unit) / APARTMENT_TOTAL_PARTS;
|
|
63
|
+
}
|
|
64
|
+
export function buildApartmentPlan(unit, requestedParts, rentalRate) {
|
|
65
|
+
const numParts = clamp(requestedParts, 1, APARTMENT_TOTAL_PARTS);
|
|
66
|
+
const fullLotValueMXN = getUnitListPriceMXN(unit) * APARTMENT_COPROP_PRICE_MULTIPLIER;
|
|
67
|
+
const partValueMXN = fullLotValueMXN / APARTMENT_TOTAL_PARTS;
|
|
68
|
+
const totalRentMonthlyMXN = typeof rentalRate === "number"
|
|
69
|
+
? (fullLotValueMXN * Math.max(rentalRate, 0)) / 12
|
|
70
|
+
: fullLotValueMXN * APARTMENT_TOTAL_RENT_RATIO;
|
|
71
|
+
const deliveryValueMXN = fullLotValueMXN * APARTMENT_DELIVERY_VALUE_RATIO;
|
|
72
|
+
const yourValueMXN = partValueMXN * numParts;
|
|
73
|
+
const engancheMXN = roundCurrency(yourValueMXN * APARTMENT_ENGANCHE_RATIO);
|
|
74
|
+
const monthlyPaymentMXN = roundCurrency(yourValueMXN * APARTMENT_MENSUALIDAD_RATIO);
|
|
75
|
+
const saldoFinalMXN = roundCurrency(yourValueMXN * APARTMENT_SALDO_FINAL_RATIO);
|
|
76
|
+
return {
|
|
77
|
+
deliveryValueMXN: roundCurrency((deliveryValueMXN / APARTMENT_TOTAL_PARTS) * numParts),
|
|
78
|
+
engancheMXN,
|
|
79
|
+
fullLotValueMXN: roundCurrency(fullLotValueMXN),
|
|
80
|
+
monthlyPaymentMXN,
|
|
81
|
+
numParts,
|
|
82
|
+
rentaMensualMXN: roundCurrency((totalRentMonthlyMXN / APARTMENT_TOTAL_PARTS) * numParts),
|
|
83
|
+
saldoFinalMXN,
|
|
84
|
+
totalMonths: APARTMENT_TOTAL_MONTHS,
|
|
85
|
+
totalToPayMXN: engancheMXN + monthlyPaymentMXN * APARTMENT_TOTAL_MONTHS + saldoFinalMXN,
|
|
86
|
+
yourValueMXN: roundCurrency(yourValueMXN),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export function getCorporatePricePerActionMXN(unit, totalShares) {
|
|
90
|
+
return getUnitListPriceMXN(unit) / Math.max(totalShares, 1);
|
|
91
|
+
}
|
|
92
|
+
export function getCorporateM2PerAction(unit, totalShares) {
|
|
93
|
+
return unit.areaM2 / Math.max(totalShares, 1);
|
|
94
|
+
}
|
|
95
|
+
export function buildCorporatePlan(args) {
|
|
96
|
+
const scheme = getActionScheme(args.requestedSchemeId);
|
|
97
|
+
const numActions = clamp(args.numActions, 1, 5);
|
|
98
|
+
const pricePerActionMXN = getCorporatePricePerActionMXN(args.unit, args.totalShares);
|
|
99
|
+
const m2PerAction = getCorporateM2PerAction(args.unit, args.totalShares);
|
|
100
|
+
const capitalMXN = pricePerActionMXN * numActions;
|
|
101
|
+
const engancheMXN = roundCurrency(capitalMXN * scheme.engancheRatio);
|
|
102
|
+
const monthlyPaymentMXN = roundCurrency(capitalMXN * scheme.monthlyRatio);
|
|
103
|
+
const monthsLiquidated = CORPORATE_OBRA_MESES - scheme.monthlyCount;
|
|
104
|
+
const obraMonthlyReturnMXN = (capitalMXN * scheme.tasaObra) / 12;
|
|
105
|
+
const obraMonthlyReturnNetMXN = obraMonthlyReturnMXN * (1 - CORPORATE_ISR_RETENCION);
|
|
106
|
+
const obraReturnNetMXN = obraMonthlyReturnNetMXN * monthsLiquidated;
|
|
107
|
+
const obraYearsFloat = CORPORATE_OBRA_MESES / 12;
|
|
108
|
+
const operationYears = CORPORATE_HORIZONTE_YEARS - obraYearsFloat;
|
|
109
|
+
const operationMonthlyReturnMXN = (capitalMXN * Math.max(args.rentalRate, 0)) / 12;
|
|
110
|
+
const operationMonthlyReturnNetMXN = operationMonthlyReturnMXN * (1 - CORPORATE_ISR_RETENCION);
|
|
111
|
+
const operationAnnualReturnNetMXN = operationMonthlyReturnNetMXN * 12;
|
|
112
|
+
const operationReturnHorizonNetMXN = getEscalatedAmountOverHorizonMXN(operationAnnualReturnNetMXN, args.inflationRate, operationYears);
|
|
113
|
+
const projectedAssetValueMXN = capitalMXN *
|
|
114
|
+
Math.pow(1 + CORPORATE_PLUSVALIA_ANUAL, CORPORATE_HORIZONTE_YEARS);
|
|
115
|
+
const plusvaliaGainMXN = projectedAssetValueMXN - capitalMXN;
|
|
116
|
+
const patrimonioTotalMXN = projectedAssetValueMXN + obraReturnNetMXN + operationReturnHorizonNetMXN;
|
|
117
|
+
return {
|
|
118
|
+
capitalMXN: roundCurrency(capitalMXN),
|
|
119
|
+
engancheMXN,
|
|
120
|
+
m2: m2PerAction * numActions,
|
|
121
|
+
monthlyCount: scheme.monthlyCount,
|
|
122
|
+
monthlyPaymentMXN,
|
|
123
|
+
numActions,
|
|
124
|
+
operationMonthlyReturnMXN: roundCurrency(operationMonthlyReturnMXN),
|
|
125
|
+
operationAnnualReturnNetMXN: roundCurrency(operationAnnualReturnNetMXN),
|
|
126
|
+
operationMonthlyReturnNetMXN: roundCurrency(operationMonthlyReturnNetMXN),
|
|
127
|
+
operationReturnHorizonNetMXN: roundCurrency(operationReturnHorizonNetMXN),
|
|
128
|
+
obraReturnMonthlyMXN: roundCurrency(obraMonthlyReturnMXN),
|
|
129
|
+
obraReturnMonthlyNetMXN: roundCurrency(obraMonthlyReturnNetMXN),
|
|
130
|
+
obraReturnNetMXN: roundCurrency(obraReturnNetMXN),
|
|
131
|
+
patrimonioTotalMXN: roundCurrency(patrimonioTotalMXN),
|
|
132
|
+
plusvaliaGainMXN: roundCurrency(plusvaliaGainMXN),
|
|
133
|
+
pricePerActionMXN: roundCurrency(pricePerActionMXN),
|
|
134
|
+
projectedAssetValueMXN: roundCurrency(projectedAssetValueMXN),
|
|
135
|
+
scheme,
|
|
136
|
+
totalMonths: scheme.monthlyCount,
|
|
137
|
+
totalPaidMXN: roundCurrency(capitalMXN),
|
|
138
|
+
};
|
|
139
|
+
}
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function formatCurrency(value: number): string;
|
|
2
|
+
export declare function formatCurrencyPrecise(value: number): string;
|
|
3
|
+
export declare function formatCompactCurrency(value: number): string;
|
|
4
|
+
export declare function formatPricePerM2(value: number): string;
|
|
5
|
+
export declare function formatArea(value: number): string;
|
|
6
|
+
export declare function formatPercent(value: number): string;
|
|
7
|
+
export declare function formatYears(value: number): string;
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const currencyFormatter = new Intl.NumberFormat("es-MX", {
|
|
2
|
+
style: "currency",
|
|
3
|
+
currency: "MXN",
|
|
4
|
+
maximumFractionDigits: 0,
|
|
5
|
+
});
|
|
6
|
+
const preciseCurrencyFormatter = new Intl.NumberFormat("es-MX", {
|
|
7
|
+
style: "currency",
|
|
8
|
+
currency: "MXN",
|
|
9
|
+
minimumFractionDigits: 2,
|
|
10
|
+
maximumFractionDigits: 2,
|
|
11
|
+
});
|
|
12
|
+
export function formatCurrency(value) {
|
|
13
|
+
return currencyFormatter.format(value);
|
|
14
|
+
}
|
|
15
|
+
export function formatCurrencyPrecise(value) {
|
|
16
|
+
return preciseCurrencyFormatter.format(value);
|
|
17
|
+
}
|
|
18
|
+
export function formatCompactCurrency(value) {
|
|
19
|
+
const absolute = Math.abs(value);
|
|
20
|
+
if (absolute >= 1000000) {
|
|
21
|
+
const sign = value < 0 ? "-" : "";
|
|
22
|
+
return `${sign}$${(absolute / 1000000).toFixed(2)}M`;
|
|
23
|
+
}
|
|
24
|
+
return formatCurrency(value);
|
|
25
|
+
}
|
|
26
|
+
export function formatPricePerM2(value) {
|
|
27
|
+
return `${formatCurrencyPrecise(value)}/m²`;
|
|
28
|
+
}
|
|
29
|
+
export function formatArea(value) {
|
|
30
|
+
return `${value.toFixed(2)} m²`;
|
|
31
|
+
}
|
|
32
|
+
export function formatPercent(value) {
|
|
33
|
+
return `${value}%`;
|
|
34
|
+
}
|
|
35
|
+
export function formatYears(value) {
|
|
36
|
+
return `${value} año${value === 1 ? "" : "s"}`;
|
|
37
|
+
}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CotizadorApiBaseOptions, CotizadorApiErrorBody, CotizadorInventoryRequest, CotizadorSiteRequest, CotizadorSitemapRequest, CreatePublicReservationRequest, CreatePublicSessionRequest, GetPublicReservationRequest, PublicInventoryResponse, PublicInstancesResponse, PublicPaymentMethodsResponse, PublicReservationStatus, PublicSessionResponse, PublicSitemapResponse, PublicSnapshotResponse } from "./types.js";
|
|
2
|
+
type Credential = {
|
|
3
|
+
kind: "apiKey";
|
|
4
|
+
value: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "session";
|
|
7
|
+
value: string;
|
|
8
|
+
};
|
|
9
|
+
export declare class CotizadorApiError extends Error {
|
|
10
|
+
code?: string;
|
|
11
|
+
fieldErrors?: Record<string, string>;
|
|
12
|
+
response: Response;
|
|
13
|
+
status: number;
|
|
14
|
+
constructor(response: Response, body: CotizadorApiErrorBody);
|
|
15
|
+
}
|
|
16
|
+
export declare function createCotizadorApiRequester(options: CotizadorApiBaseOptions): {
|
|
17
|
+
getAgentGuide: () => Promise<Record<string, unknown>>;
|
|
18
|
+
getOpenApi: () => Promise<Record<string, unknown>>;
|
|
19
|
+
listInstances: (args: Pick<CotizadorSiteRequest, "lookup" | "siteKey">) => Promise<PublicInstancesResponse>;
|
|
20
|
+
getSnapshot: (args: CotizadorSiteRequest) => Promise<PublicSnapshotResponse>;
|
|
21
|
+
getInventory: (args: CotizadorInventoryRequest) => Promise<PublicInventoryResponse>;
|
|
22
|
+
getSitemap: (args: CotizadorSitemapRequest) => Promise<PublicSitemapResponse>;
|
|
23
|
+
getPaymentMethods: (args: CotizadorSiteRequest) => Promise<PublicPaymentMethodsResponse>;
|
|
24
|
+
createSession: (args: CreatePublicSessionRequest, credential: Credential) => Promise<PublicSessionResponse>;
|
|
25
|
+
createReservation: (args: CreatePublicReservationRequest, credential: Credential) => Promise<PublicReservationStatus>;
|
|
26
|
+
getReservation: (args: GetPublicReservationRequest, credential: Credential) => Promise<PublicReservationStatus>;
|
|
27
|
+
};
|
|
28
|
+
export {};
|