hvp-shared 6.68.0 → 6.70.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.
@@ -49,5 +49,12 @@ export declare const MONTH_WORK_DAYS: number;
49
49
  export declare const DAILY_WORK_HOURS = 8;
50
50
  /** Working days in a week (alias for clarity) */
51
51
  export declare const WEEK_WORK_DAYS = 6;
52
- /** Calculate hourly rate from monthly fixed income: income / 30 / 8 */
52
+ /** Weeks per month (52 / 12 4.333) used for hourly rate calculation */
53
+ export declare const WEEKS_PER_MONTH: number;
54
+ /**
55
+ * Calculate hourly rate from monthly fixed income.
56
+ * Formula: income / (52/12) / 48
57
+ * Divides by work-weeks per month and max weekly hours (48).
58
+ * Hourly workers are NOT paid for rest days, so we use actual work hours, not calendar days.
59
+ */
53
60
  export declare function calculateHourlyRate(jobFixedIncome: number): number;
@@ -11,7 +11,7 @@
11
11
  * - Derived/calculated (computed from other constants)
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.WEEK_WORK_DAYS = exports.DAILY_WORK_HOURS = exports.MONTH_WORK_DAYS = exports.MONTH_DAYS = exports.WEEKS_IN_MONTH = exports.AVERAGE_WORK_DAYS_PER_MONTH = exports.EXCELLENCE_BONUS_RATE = exports.EXCELLENCE_BONUS_MIN_CLOSINGS = exports.DEDUCTION_INVALID_WITHDRAWAL = exports.DEDUCTION_INVALID_CLOSING = exports.BONUS_PER_CLOSING = exports.BONUS_SMALL_CLOSING = exports.BONUS_LARGE_CLOSING = exports.CLOSING_LARGE_THRESHOLD = exports.COMMISSION_SENIORITY_BONUS_PER_SEMESTER = exports.COMMISSION_PERCENTAGE_FOR_YEAR_END_BONUS = exports.YEAR_END_BONUS_DAYS = exports.ANNUAL_RAISE_PERCENT = exports.WORKING_DAYS_IN_A_WEEK = exports.MAX_DOUBLE_PLAY_WORK_WEEK_LIMIT = exports.MAX_WORK_WEEK_LIMIT = exports.HOLIDAY_OR_REST_EXTRA_PAY_PERCENTAGE = exports.SUNDAY_BONUS_PERCENTAGE = exports.VACATION_BONUS_PERCENTAGE = void 0;
14
+ exports.WEEKS_PER_MONTH = exports.WEEK_WORK_DAYS = exports.DAILY_WORK_HOURS = exports.MONTH_WORK_DAYS = exports.MONTH_DAYS = exports.WEEKS_IN_MONTH = exports.AVERAGE_WORK_DAYS_PER_MONTH = exports.EXCELLENCE_BONUS_RATE = exports.EXCELLENCE_BONUS_MIN_CLOSINGS = exports.DEDUCTION_INVALID_WITHDRAWAL = exports.DEDUCTION_INVALID_CLOSING = exports.BONUS_PER_CLOSING = exports.BONUS_SMALL_CLOSING = exports.BONUS_LARGE_CLOSING = exports.CLOSING_LARGE_THRESHOLD = exports.COMMISSION_SENIORITY_BONUS_PER_SEMESTER = exports.COMMISSION_PERCENTAGE_FOR_YEAR_END_BONUS = exports.YEAR_END_BONUS_DAYS = exports.ANNUAL_RAISE_PERCENT = exports.WORKING_DAYS_IN_A_WEEK = exports.MAX_DOUBLE_PLAY_WORK_WEEK_LIMIT = exports.MAX_WORK_WEEK_LIMIT = exports.HOLIDAY_OR_REST_EXTRA_PAY_PERCENTAGE = exports.SUNDAY_BONUS_PERCENTAGE = exports.VACATION_BONUS_PERCENTAGE = void 0;
15
15
  exports.calculateHourlyRate = calculateHourlyRate;
16
16
  // ── Mexican Labor Law (not configurable) ──
17
17
  /** Prima vacacional: 25% of vacation days' salary (Art. 80 LFT) */
@@ -61,9 +61,16 @@ exports.DAILY_WORK_HOURS = 8;
61
61
  /** Working days in a week (alias for clarity) */
62
62
  exports.WEEK_WORK_DAYS = 6;
63
63
  // ── Utility Functions ──
64
- /** Calculate hourly rate from monthly fixed income: income / 30 / 8 */
64
+ /** Weeks per month (52 / 12 4.333) used for hourly rate calculation */
65
+ exports.WEEKS_PER_MONTH = 52 / 12;
66
+ /**
67
+ * Calculate hourly rate from monthly fixed income.
68
+ * Formula: income / (52/12) / 48
69
+ * Divides by work-weeks per month and max weekly hours (48).
70
+ * Hourly workers are NOT paid for rest days, so we use actual work hours, not calendar days.
71
+ */
65
72
  function calculateHourlyRate(jobFixedIncome) {
66
73
  if (!jobFixedIncome || jobFixedIncome <= 0)
67
74
  return 0;
68
- return Math.round((jobFixedIncome / exports.MONTH_DAYS / exports.DAILY_WORK_HOURS) * 100) / 100;
75
+ return Math.round((jobFixedIncome / exports.WEEKS_PER_MONTH / exports.MAX_WORK_WEEK_LIMIT) * 100) / 100;
69
76
  }
@@ -20,3 +20,4 @@ export * from './qvet-cash-flow.enums';
20
20
  export * from './time-off.enums';
21
21
  export * from './time-off.constants';
22
22
  export * from './hris.constants';
23
+ export * from './payroll-features.constants';
@@ -36,3 +36,4 @@ __exportStar(require("./qvet-cash-flow.enums"), exports);
36
36
  __exportStar(require("./time-off.enums"), exports);
37
37
  __exportStar(require("./time-off.constants"), exports);
38
38
  __exportStar(require("./hris.constants"), exports);
39
+ __exportStar(require("./payroll-features.constants"), exports);
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Payroll Features by Employment Type (GH#204)
3
+ *
4
+ * Defines which payroll calculation features each employment type supports
5
+ * and how legal concepts are renamed for hourly-based types.
6
+ *
7
+ * Used by:
8
+ * - Backend: payroll calculation engine (which items to include)
9
+ * - Frontend: payroll estimate UI (which fields to show/hide)
10
+ */
11
+ import { EmploymentType } from "./employment.enums";
12
+ /**
13
+ * Hourly-based types use non-legal denomination for certain concepts
14
+ * to avoid labor law implications (prima → apoyo, aguinaldo → apoyo de fin de año).
15
+ */
16
+ export declare function isHourlyBasedType(type: EmploymentType): boolean;
17
+ /**
18
+ * Salary-based types use legal denomination (prima dominical, aguinaldo, etc.)
19
+ * and are eligible for full tax calculation (ISR).
20
+ */
21
+ export declare function isSalaryBasedType(type: EmploymentType): boolean;
22
+ /**
23
+ * Payroll calculation features that can be toggled per employment type.
24
+ *
25
+ * Note: hasImss and hasCfdi are driven by the employment's config flags
26
+ * (hasImss, payrollStampingMode), NOT hardcoded here.
27
+ */
28
+ export interface PayrollFeatures {
29
+ /** Base pay: salary (halfWeekFixedIncome) or hourly (hours × rate) */
30
+ usesHourlyBasePay: boolean;
31
+ /** Attendance-based deductions (absences, tardiness, unworked hours) */
32
+ hasAttendanceDiscounts: boolean;
33
+ /** Punctuality bonus (salary types only) */
34
+ hasPunctualityBonus: boolean;
35
+ /** Overtime: simple, double, triple (salary types only) */
36
+ hasOvertime: boolean;
37
+ /** Guaranteed income top-up */
38
+ hasGuaranteedIncome: boolean;
39
+ /** ISR tax withholding */
40
+ hasIsr: boolean;
41
+ /** Aguinaldo / apoyo de fin de año */
42
+ hasAguinaldo: boolean;
43
+ /** PTU (profit sharing) */
44
+ hasPtu: boolean;
45
+ /** Legal concept names renamed to non-legal ("apoyo") denomination */
46
+ usesRenamedConcepts: boolean;
47
+ /** Generates a real payroll (as opposed to only nominal) */
48
+ generatesRealPayroll: boolean;
49
+ }
50
+ export declare const PAYROLL_FEATURES: Record<EmploymentType, PayrollFeatures>;
51
+ /**
52
+ * Payroll concept keys that have different labels depending on whether
53
+ * the employment type is salary-based (legal terms) or hourly-based (non-legal).
54
+ *
55
+ * Hourly-based types use "apoyo" denomination to avoid labor law implications.
56
+ */
57
+ export declare const PAYROLL_CONCEPT_LABELS: Record<string, {
58
+ salary: string;
59
+ hourly: string;
60
+ }>;
61
+ /**
62
+ * Get the display label for a payroll concept based on employment type.
63
+ *
64
+ * @param conceptKey - Key from PAYROLL_CONCEPT_LABELS (e.g., "sundayBonus")
65
+ * @param employmentType - The employment type to determine denomination
66
+ * @returns The correct Spanish label for this concept
67
+ */
68
+ export declare function getPayrollConceptLabel(conceptKey: string, employmentType: EmploymentType): string;
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ /**
3
+ * Payroll Features by Employment Type (GH#204)
4
+ *
5
+ * Defines which payroll calculation features each employment type supports
6
+ * and how legal concepts are renamed for hourly-based types.
7
+ *
8
+ * Used by:
9
+ * - Backend: payroll calculation engine (which items to include)
10
+ * - Frontend: payroll estimate UI (which fields to show/hide)
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.PAYROLL_CONCEPT_LABELS = exports.PAYROLL_FEATURES = void 0;
14
+ exports.isHourlyBasedType = isHourlyBasedType;
15
+ exports.isSalaryBasedType = isSalaryBasedType;
16
+ exports.getPayrollConceptLabel = getPayrollConceptLabel;
17
+ const employment_enums_1 = require("./employment.enums");
18
+ // ─── Employment Type Classification Helpers ──────────────────────────────────
19
+ /**
20
+ * Hourly-based types use non-legal denomination for certain concepts
21
+ * to avoid labor law implications (prima → apoyo, aguinaldo → apoyo de fin de año).
22
+ */
23
+ function isHourlyBasedType(type) {
24
+ return (type === employment_enums_1.EmploymentType.hourly_agreed ||
25
+ type === employment_enums_1.EmploymentType.casual ||
26
+ type === employment_enums_1.EmploymentType.intern);
27
+ }
28
+ /**
29
+ * Salary-based types use legal denomination (prima dominical, aguinaldo, etc.)
30
+ * and are eligible for full tax calculation (ISR).
31
+ */
32
+ function isSalaryBasedType(type) {
33
+ return (type === employment_enums_1.EmploymentType.payroll ||
34
+ type === employment_enums_1.EmploymentType.payroll_draft);
35
+ }
36
+ exports.PAYROLL_FEATURES = {
37
+ [employment_enums_1.EmploymentType.payroll]: {
38
+ usesHourlyBasePay: false,
39
+ hasAttendanceDiscounts: true,
40
+ hasPunctualityBonus: true,
41
+ hasOvertime: true,
42
+ hasGuaranteedIncome: true,
43
+ hasIsr: true,
44
+ hasAguinaldo: true,
45
+ hasPtu: true,
46
+ usesRenamedConcepts: false,
47
+ generatesRealPayroll: true,
48
+ },
49
+ [employment_enums_1.EmploymentType.payroll_draft]: {
50
+ usesHourlyBasePay: false,
51
+ hasAttendanceDiscounts: true,
52
+ hasPunctualityBonus: true,
53
+ hasOvertime: true,
54
+ hasGuaranteedIncome: true,
55
+ hasIsr: false,
56
+ hasAguinaldo: true,
57
+ hasPtu: true,
58
+ usesRenamedConcepts: false,
59
+ generatesRealPayroll: true,
60
+ },
61
+ [employment_enums_1.EmploymentType.hourly_agreed]: {
62
+ usesHourlyBasePay: true,
63
+ hasAttendanceDiscounts: true,
64
+ hasPunctualityBonus: false,
65
+ hasOvertime: false,
66
+ hasGuaranteedIncome: false,
67
+ hasIsr: false,
68
+ hasAguinaldo: true,
69
+ hasPtu: true,
70
+ usesRenamedConcepts: true,
71
+ generatesRealPayroll: true,
72
+ },
73
+ [employment_enums_1.EmploymentType.casual]: {
74
+ usesHourlyBasePay: true,
75
+ hasAttendanceDiscounts: false,
76
+ hasPunctualityBonus: false,
77
+ hasOvertime: false,
78
+ hasGuaranteedIncome: false,
79
+ hasIsr: false,
80
+ hasAguinaldo: true,
81
+ hasPtu: true,
82
+ usesRenamedConcepts: true,
83
+ generatesRealPayroll: true,
84
+ },
85
+ [employment_enums_1.EmploymentType.intern]: {
86
+ usesHourlyBasePay: true,
87
+ hasAttendanceDiscounts: false,
88
+ hasPunctualityBonus: false,
89
+ hasOvertime: false,
90
+ hasGuaranteedIncome: false,
91
+ hasIsr: false,
92
+ hasAguinaldo: true,
93
+ hasPtu: true,
94
+ usesRenamedConcepts: true,
95
+ generatesRealPayroll: true,
96
+ },
97
+ [employment_enums_1.EmploymentType.coverage_only]: {
98
+ usesHourlyBasePay: false,
99
+ hasAttendanceDiscounts: false,
100
+ hasPunctualityBonus: false,
101
+ hasOvertime: false,
102
+ hasGuaranteedIncome: false,
103
+ hasIsr: false,
104
+ hasAguinaldo: false,
105
+ hasPtu: false,
106
+ usesRenamedConcepts: false,
107
+ generatesRealPayroll: false,
108
+ },
109
+ [employment_enums_1.EmploymentType.contractor]: {
110
+ usesHourlyBasePay: false,
111
+ hasAttendanceDiscounts: false,
112
+ hasPunctualityBonus: false,
113
+ hasOvertime: false,
114
+ hasGuaranteedIncome: false,
115
+ hasIsr: false,
116
+ hasAguinaldo: false,
117
+ hasPtu: false,
118
+ usesRenamedConcepts: false,
119
+ generatesRealPayroll: false,
120
+ },
121
+ };
122
+ // ─── Payroll Concept Denomination ───────────────────────────────────────────
123
+ /**
124
+ * Payroll concept keys that have different labels depending on whether
125
+ * the employment type is salary-based (legal terms) or hourly-based (non-legal).
126
+ *
127
+ * Hourly-based types use "apoyo" denomination to avoid labor law implications.
128
+ */
129
+ exports.PAYROLL_CONCEPT_LABELS = {
130
+ sundayBonus: {
131
+ salary: "Prima dominical",
132
+ hourly: "Apoyo dominical",
133
+ },
134
+ holidayOrRestExtraPay: {
135
+ salary: "Pago extra por día festivo",
136
+ hourly: "Apoyo por día festivo",
137
+ },
138
+ vacationBonus: {
139
+ salary: "Prima vacacional",
140
+ hourly: "Apoyo vacacional",
141
+ },
142
+ endYearBonus: {
143
+ salary: "Aguinaldo",
144
+ hourly: "Apoyo de fin de año",
145
+ },
146
+ };
147
+ /**
148
+ * Get the display label for a payroll concept based on employment type.
149
+ *
150
+ * @param conceptKey - Key from PAYROLL_CONCEPT_LABELS (e.g., "sundayBonus")
151
+ * @param employmentType - The employment type to determine denomination
152
+ * @returns The correct Spanish label for this concept
153
+ */
154
+ function getPayrollConceptLabel(conceptKey, employmentType) {
155
+ const entry = exports.PAYROLL_CONCEPT_LABELS[conceptKey];
156
+ if (!entry)
157
+ return conceptKey;
158
+ return isHourlyBasedType(employmentType) ? entry.hourly : entry.salary;
159
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hvp-shared",
3
- "version": "6.68.0",
3
+ "version": "6.70.0",
4
4
  "description": "Shared types and utilities for HVP backend and frontend",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",