@accounter/server 0.0.8-alpha-20251102200443-d7162b8ce1dfc629b8b454df17dcec9ed005a052 → 0.0.8-alpha-20251102213150-c9d936f545d5351df0dc5326c2623266f1ad1f46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/CHANGELOG.md +47 -7
  2. package/dist/green-invoice-graphql/src/mesh-artifacts/index.d.ts +1 -1
  3. package/dist/server/src/__generated__/types.d.ts +77 -0
  4. package/dist/server/src/__generated__/types.js.map +1 -1
  5. package/dist/server/src/modules/charges-matcher/__generated__/types.d.ts +68 -0
  6. package/dist/server/src/modules/charges-matcher/__generated__/types.js +7 -0
  7. package/dist/server/src/modules/charges-matcher/__generated__/types.js.map +1 -0
  8. package/dist/server/src/modules/charges-matcher/__tests__/amount-confidence.test.d.ts +1 -0
  9. package/dist/server/src/modules/charges-matcher/__tests__/amount-confidence.test.js +218 -0
  10. package/dist/server/src/modules/charges-matcher/__tests__/amount-confidence.test.js.map +1 -0
  11. package/dist/server/src/modules/charges-matcher/__tests__/auto-match-integration.test.d.ts +1 -0
  12. package/dist/server/src/modules/charges-matcher/__tests__/auto-match-integration.test.js +645 -0
  13. package/dist/server/src/modules/charges-matcher/__tests__/auto-match-integration.test.js.map +1 -0
  14. package/dist/server/src/modules/charges-matcher/__tests__/auto-match.test.d.ts +1 -0
  15. package/dist/server/src/modules/charges-matcher/__tests__/auto-match.test.js +530 -0
  16. package/dist/server/src/modules/charges-matcher/__tests__/auto-match.test.js.map +1 -0
  17. package/dist/server/src/modules/charges-matcher/__tests__/business-confidence.test.d.ts +1 -0
  18. package/dist/server/src/modules/charges-matcher/__tests__/business-confidence.test.js +143 -0
  19. package/dist/server/src/modules/charges-matcher/__tests__/business-confidence.test.js.map +1 -0
  20. package/dist/server/src/modules/charges-matcher/__tests__/candidate-filter.test.d.ts +1 -0
  21. package/dist/server/src/modules/charges-matcher/__tests__/candidate-filter.test.js +186 -0
  22. package/dist/server/src/modules/charges-matcher/__tests__/candidate-filter.test.js.map +1 -0
  23. package/dist/server/src/modules/charges-matcher/__tests__/charge-validator.test.d.ts +1 -0
  24. package/dist/server/src/modules/charges-matcher/__tests__/charge-validator.test.js +301 -0
  25. package/dist/server/src/modules/charges-matcher/__tests__/charge-validator.test.js.map +1 -0
  26. package/dist/server/src/modules/charges-matcher/__tests__/currency-confidence.test.d.ts +1 -0
  27. package/dist/server/src/modules/charges-matcher/__tests__/currency-confidence.test.js +127 -0
  28. package/dist/server/src/modules/charges-matcher/__tests__/currency-confidence.test.js.map +1 -0
  29. package/dist/server/src/modules/charges-matcher/__tests__/date-confidence.test.d.ts +1 -0
  30. package/dist/server/src/modules/charges-matcher/__tests__/date-confidence.test.js +246 -0
  31. package/dist/server/src/modules/charges-matcher/__tests__/date-confidence.test.js.map +1 -0
  32. package/dist/server/src/modules/charges-matcher/__tests__/document-aggregator.test.d.ts +1 -0
  33. package/dist/server/src/modules/charges-matcher/__tests__/document-aggregator.test.js +475 -0
  34. package/dist/server/src/modules/charges-matcher/__tests__/document-aggregator.test.js.map +1 -0
  35. package/dist/server/src/modules/charges-matcher/__tests__/document-amount.test.d.ts +1 -0
  36. package/dist/server/src/modules/charges-matcher/__tests__/document-amount.test.js +287 -0
  37. package/dist/server/src/modules/charges-matcher/__tests__/document-amount.test.js.map +1 -0
  38. package/dist/server/src/modules/charges-matcher/__tests__/document-business.test.d.ts +1 -0
  39. package/dist/server/src/modules/charges-matcher/__tests__/document-business.test.js +151 -0
  40. package/dist/server/src/modules/charges-matcher/__tests__/document-business.test.js.map +1 -0
  41. package/dist/server/src/modules/charges-matcher/__tests__/match-scorer.test.d.ts +1 -0
  42. package/dist/server/src/modules/charges-matcher/__tests__/match-scorer.test.js +550 -0
  43. package/dist/server/src/modules/charges-matcher/__tests__/match-scorer.test.js.map +1 -0
  44. package/dist/server/src/modules/charges-matcher/__tests__/overall-confidence.test.d.ts +1 -0
  45. package/dist/server/src/modules/charges-matcher/__tests__/overall-confidence.test.js +410 -0
  46. package/dist/server/src/modules/charges-matcher/__tests__/overall-confidence.test.js.map +1 -0
  47. package/dist/server/src/modules/charges-matcher/__tests__/single-match-integration.test.d.ts +1 -0
  48. package/dist/server/src/modules/charges-matcher/__tests__/single-match-integration.test.js +504 -0
  49. package/dist/server/src/modules/charges-matcher/__tests__/single-match-integration.test.js.map +1 -0
  50. package/dist/server/src/modules/charges-matcher/__tests__/single-match.test.d.ts +1 -0
  51. package/dist/server/src/modules/charges-matcher/__tests__/single-match.test.js +483 -0
  52. package/dist/server/src/modules/charges-matcher/__tests__/single-match.test.js.map +1 -0
  53. package/dist/server/src/modules/charges-matcher/__tests__/test-helpers.d.ts +46 -0
  54. package/dist/server/src/modules/charges-matcher/__tests__/test-helpers.js +143 -0
  55. package/dist/server/src/modules/charges-matcher/__tests__/test-helpers.js.map +1 -0
  56. package/dist/server/src/modules/charges-matcher/__tests__/test-infrastructure.spec.d.ts +1 -0
  57. package/dist/server/src/modules/charges-matcher/__tests__/test-infrastructure.spec.js +137 -0
  58. package/dist/server/src/modules/charges-matcher/__tests__/test-infrastructure.spec.js.map +1 -0
  59. package/dist/server/src/modules/charges-matcher/__tests__/transaction-aggregator.test.d.ts +1 -0
  60. package/dist/server/src/modules/charges-matcher/__tests__/transaction-aggregator.test.js +415 -0
  61. package/dist/server/src/modules/charges-matcher/__tests__/transaction-aggregator.test.js.map +1 -0
  62. package/dist/server/src/modules/charges-matcher/helpers/amount-confidence.helper.d.ts +7 -0
  63. package/dist/server/src/modules/charges-matcher/helpers/amount-confidence.helper.js +70 -0
  64. package/dist/server/src/modules/charges-matcher/helpers/amount-confidence.helper.js.map +1 -0
  65. package/dist/server/src/modules/charges-matcher/helpers/business-confidence.helper.d.ts +7 -0
  66. package/dist/server/src/modules/charges-matcher/helpers/business-confidence.helper.js +19 -0
  67. package/dist/server/src/modules/charges-matcher/helpers/business-confidence.helper.js.map +1 -0
  68. package/dist/server/src/modules/charges-matcher/helpers/candidate-filter.helper.d.ts +24 -0
  69. package/dist/server/src/modules/charges-matcher/helpers/candidate-filter.helper.js +45 -0
  70. package/dist/server/src/modules/charges-matcher/helpers/candidate-filter.helper.js.map +1 -0
  71. package/dist/server/src/modules/charges-matcher/helpers/charge-validator.helper.d.ts +33 -0
  72. package/dist/server/src/modules/charges-matcher/helpers/charge-validator.helper.js +65 -0
  73. package/dist/server/src/modules/charges-matcher/helpers/charge-validator.helper.js.map +1 -0
  74. package/dist/server/src/modules/charges-matcher/helpers/currency-confidence.helper.d.ts +7 -0
  75. package/dist/server/src/modules/charges-matcher/helpers/currency-confidence.helper.js +18 -0
  76. package/dist/server/src/modules/charges-matcher/helpers/currency-confidence.helper.js.map +1 -0
  77. package/dist/server/src/modules/charges-matcher/helpers/date-confidence.helper.d.ts +7 -0
  78. package/dist/server/src/modules/charges-matcher/helpers/date-confidence.helper.js +35 -0
  79. package/dist/server/src/modules/charges-matcher/helpers/date-confidence.helper.js.map +1 -0
  80. package/dist/server/src/modules/charges-matcher/helpers/document-amount.helper.d.ts +49 -0
  81. package/dist/server/src/modules/charges-matcher/helpers/document-amount.helper.js +58 -0
  82. package/dist/server/src/modules/charges-matcher/helpers/document-amount.helper.js.map +1 -0
  83. package/dist/server/src/modules/charges-matcher/helpers/document-business.helper.d.ts +13 -0
  84. package/dist/server/src/modules/charges-matcher/helpers/document-business.helper.js +37 -0
  85. package/dist/server/src/modules/charges-matcher/helpers/document-business.helper.js.map +1 -0
  86. package/dist/server/src/modules/charges-matcher/helpers/overall-confidence.helper.d.ts +42 -0
  87. package/dist/server/src/modules/charges-matcher/helpers/overall-confidence.helper.js +77 -0
  88. package/dist/server/src/modules/charges-matcher/helpers/overall-confidence.helper.js.map +1 -0
  89. package/dist/server/src/modules/charges-matcher/index.d.ts +3 -0
  90. package/dist/server/src/modules/charges-matcher/index.js +15 -0
  91. package/dist/server/src/modules/charges-matcher/index.js.map +1 -0
  92. package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.d.ts +48 -0
  93. package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.js +133 -0
  94. package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.js.map +1 -0
  95. package/dist/server/src/modules/charges-matcher/providers/charges-matcher.provider.d.ts +38 -0
  96. package/dist/server/src/modules/charges-matcher/providers/charges-matcher.provider.js +248 -0
  97. package/dist/server/src/modules/charges-matcher/providers/charges-matcher.provider.js.map +1 -0
  98. package/dist/server/src/modules/charges-matcher/providers/document-aggregator.d.ts +61 -0
  99. package/dist/server/src/modules/charges-matcher/providers/document-aggregator.js +153 -0
  100. package/dist/server/src/modules/charges-matcher/providers/document-aggregator.js.map +1 -0
  101. package/dist/server/src/modules/charges-matcher/providers/match-scorer.provider.d.ts +25 -0
  102. package/dist/server/src/modules/charges-matcher/providers/match-scorer.provider.js +114 -0
  103. package/dist/server/src/modules/charges-matcher/providers/match-scorer.provider.js.map +1 -0
  104. package/dist/server/src/modules/charges-matcher/providers/single-match.provider.d.ts +39 -0
  105. package/dist/server/src/modules/charges-matcher/providers/single-match.provider.js +189 -0
  106. package/dist/server/src/modules/charges-matcher/providers/single-match.provider.js.map +1 -0
  107. package/dist/server/src/modules/charges-matcher/providers/transaction-aggregator.d.ts +54 -0
  108. package/dist/server/src/modules/charges-matcher/providers/transaction-aggregator.js +93 -0
  109. package/dist/server/src/modules/charges-matcher/providers/transaction-aggregator.js.map +1 -0
  110. package/dist/server/src/modules/charges-matcher/resolvers/auto-match-charges.resolver.d.ts +2 -0
  111. package/dist/server/src/modules/charges-matcher/resolvers/auto-match-charges.resolver.js +22 -0
  112. package/dist/server/src/modules/charges-matcher/resolvers/auto-match-charges.resolver.js.map +1 -0
  113. package/dist/server/src/modules/charges-matcher/resolvers/find-charge-matches.resolver.d.ts +2 -0
  114. package/dist/server/src/modules/charges-matcher/resolvers/find-charge-matches.resolver.js +24 -0
  115. package/dist/server/src/modules/charges-matcher/resolvers/find-charge-matches.resolver.js.map +1 -0
  116. package/dist/server/src/modules/charges-matcher/resolvers/index.d.ts +2 -0
  117. package/dist/server/src/modules/charges-matcher/resolvers/index.js +11 -0
  118. package/dist/server/src/modules/charges-matcher/resolvers/index.js.map +1 -0
  119. package/dist/server/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.d.ts +2 -0
  120. package/dist/server/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.js +47 -0
  121. package/dist/server/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.js.map +1 -0
  122. package/dist/server/src/modules/charges-matcher/types.d.ts +179 -0
  123. package/dist/server/src/modules/charges-matcher/types.js +14 -0
  124. package/dist/server/src/modules/charges-matcher/types.js.map +1 -0
  125. package/dist/server/src/modules/documents/resolvers/document-suggestions.resolver.js +2 -2
  126. package/dist/server/src/modules/documents/resolvers/document-suggestions.resolver.js.map +1 -1
  127. package/dist/server/src/modules-app.js +2 -0
  128. package/dist/server/src/modules-app.js.map +1 -1
  129. package/dist/server/src/shared/types/index.d.ts +1 -1
  130. package/package.json +4 -4
  131. package/src/__generated__/types.ts +87 -0
  132. package/src/modules/charges-matcher/README.md +279 -0
  133. package/src/modules/charges-matcher/__generated__/types.ts +71 -0
  134. package/src/modules/charges-matcher/__tests__/amount-confidence.test.ts +260 -0
  135. package/src/modules/charges-matcher/__tests__/auto-match-integration.test.ts +714 -0
  136. package/src/modules/charges-matcher/__tests__/auto-match.test.ts +621 -0
  137. package/src/modules/charges-matcher/__tests__/business-confidence.test.ts +177 -0
  138. package/src/modules/charges-matcher/__tests__/candidate-filter.test.ts +238 -0
  139. package/src/modules/charges-matcher/__tests__/charge-validator.test.ts +374 -0
  140. package/src/modules/charges-matcher/__tests__/currency-confidence.test.ts +164 -0
  141. package/src/modules/charges-matcher/__tests__/date-confidence.test.ts +291 -0
  142. package/src/modules/charges-matcher/__tests__/document-aggregator.test.ts +614 -0
  143. package/src/modules/charges-matcher/__tests__/document-amount.test.ts +352 -0
  144. package/src/modules/charges-matcher/__tests__/document-business.test.ts +192 -0
  145. package/src/modules/charges-matcher/__tests__/match-scorer.test.ts +659 -0
  146. package/src/modules/charges-matcher/__tests__/overall-confidence.test.ts +502 -0
  147. package/src/modules/charges-matcher/__tests__/single-match-integration.test.ts +556 -0
  148. package/src/modules/charges-matcher/__tests__/single-match.test.ts +608 -0
  149. package/src/modules/charges-matcher/__tests__/test-helpers.ts +174 -0
  150. package/src/modules/charges-matcher/__tests__/test-infrastructure.spec.ts +177 -0
  151. package/src/modules/charges-matcher/__tests__/transaction-aggregator.test.ts +547 -0
  152. package/src/modules/charges-matcher/documentation/README.md +331 -0
  153. package/src/modules/charges-matcher/documentation/SPEC.md +1503 -0
  154. package/src/modules/charges-matcher/documentation/TODO.md +799 -0
  155. package/src/modules/charges-matcher/helpers/amount-confidence.helper.ts +88 -0
  156. package/src/modules/charges-matcher/helpers/business-confidence.helper.ts +23 -0
  157. package/src/modules/charges-matcher/helpers/candidate-filter.helper.ts +56 -0
  158. package/src/modules/charges-matcher/helpers/charge-validator.helper.ts +100 -0
  159. package/src/modules/charges-matcher/helpers/currency-confidence.helper.ts +22 -0
  160. package/src/modules/charges-matcher/helpers/date-confidence.helper.ts +41 -0
  161. package/src/modules/charges-matcher/helpers/document-amount.helper.ts +77 -0
  162. package/src/modules/charges-matcher/helpers/document-business.helper.ts +54 -0
  163. package/src/modules/charges-matcher/helpers/overall-confidence.helper.ts +90 -0
  164. package/src/modules/charges-matcher/index.ts +17 -0
  165. package/src/modules/charges-matcher/providers/auto-match.provider.ts +176 -0
  166. package/src/modules/charges-matcher/providers/charges-matcher.provider.ts +322 -0
  167. package/src/modules/charges-matcher/providers/document-aggregator.ts +211 -0
  168. package/src/modules/charges-matcher/providers/match-scorer.provider.ts +154 -0
  169. package/src/modules/charges-matcher/providers/single-match.provider.ts +252 -0
  170. package/src/modules/charges-matcher/providers/transaction-aggregator.ts +131 -0
  171. package/src/modules/charges-matcher/resolvers/auto-match-charges.resolver.ts +23 -0
  172. package/src/modules/charges-matcher/resolvers/find-charge-matches.resolver.ts +25 -0
  173. package/src/modules/charges-matcher/resolvers/index.ts +12 -0
  174. package/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.ts +47 -0
  175. package/src/modules/charges-matcher/types.ts +200 -0
  176. package/src/modules/documents/resolvers/document-suggestions.resolver.ts +2 -2
  177. package/src/modules-app.ts +2 -0
  178. package/src/shared/types/index.ts +1 -1
@@ -0,0 +1,352 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ normalizeDocumentAmount,
4
+ type DocumentType,
5
+ } from '../helpers/document-amount.helper.js';
6
+
7
+ describe('Document Amount Normalization', () => {
8
+ describe('INVOICE - Business is Debtor (User is Creditor)', () => {
9
+ it('should return positive amount when business is debtor', () => {
10
+ // Business owes user (user is creditor, business is debtor)
11
+ const result = normalizeDocumentAmount(100, false, 'INVOICE');
12
+ expect(result).toBe(100);
13
+ });
14
+
15
+ it('should handle decimal amounts', () => {
16
+ const result = normalizeDocumentAmount(150.75, false, 'INVOICE');
17
+ expect(result).toBe(150.75);
18
+ });
19
+
20
+ it('should handle large amounts', () => {
21
+ const result = normalizeDocumentAmount(1000000, false, 'INVOICE');
22
+ expect(result).toBe(1000000);
23
+ });
24
+ });
25
+
26
+ describe('INVOICE - Business is Creditor (User is Debtor)', () => {
27
+ it('should return negative amount when business is creditor', () => {
28
+ // User owes business (user is debtor, business is creditor)
29
+ const result = normalizeDocumentAmount(100, true, 'INVOICE');
30
+ expect(result).toBe(-100);
31
+ });
32
+
33
+ it('should handle decimal amounts', () => {
34
+ const result = normalizeDocumentAmount(250.50, true, 'INVOICE');
35
+ expect(result).toBe(-250.50);
36
+ });
37
+
38
+ it('should handle large amounts', () => {
39
+ const result = normalizeDocumentAmount(5000000, true, 'INVOICE');
40
+ expect(result).toBe(-5000000);
41
+ });
42
+ });
43
+
44
+ describe('CREDIT_INVOICE - Business is Debtor (User is Creditor)', () => {
45
+ it('should return negative amount for credit invoice with business debtor', () => {
46
+ // Credit invoice: user owes refund to business (opposite of regular invoice)
47
+ const result = normalizeDocumentAmount(100, false, 'CREDIT_INVOICE');
48
+ expect(result).toBe(-100);
49
+ });
50
+
51
+ it('should handle decimal amounts', () => {
52
+ const result = normalizeDocumentAmount(75.25, false, 'CREDIT_INVOICE');
53
+ expect(result).toBe(-75.25);
54
+ });
55
+
56
+ it('should handle large amounts', () => {
57
+ const result = normalizeDocumentAmount(2000000, false, 'CREDIT_INVOICE');
58
+ expect(result).toBe(-2000000);
59
+ });
60
+ });
61
+
62
+ describe('CREDIT_INVOICE - Business is Creditor (User is Debtor)', () => {
63
+ it('should return positive amount for credit invoice with business creditor (double negation)', () => {
64
+ // Credit invoice: business owes refund to user (double negation: -1 * -1 = 1)
65
+ const result = normalizeDocumentAmount(100, true, 'CREDIT_INVOICE');
66
+ expect(result).toBe(100);
67
+ });
68
+
69
+ it('should handle decimal amounts', () => {
70
+ const result = normalizeDocumentAmount(125.99, true, 'CREDIT_INVOICE');
71
+ expect(result).toBe(125.99);
72
+ });
73
+
74
+ it('should handle large amounts', () => {
75
+ const result = normalizeDocumentAmount(3000000, true, 'CREDIT_INVOICE');
76
+ expect(result).toBe(3000000);
77
+ });
78
+ });
79
+
80
+ describe('RECEIPT - Business is Debtor (User is Creditor)', () => {
81
+ it('should return positive amount when business is debtor', () => {
82
+ const result = normalizeDocumentAmount(100, false, 'RECEIPT');
83
+ expect(result).toBe(100);
84
+ });
85
+
86
+ it('should handle typical receipt amounts', () => {
87
+ const result = normalizeDocumentAmount(49.99, false, 'RECEIPT');
88
+ expect(result).toBe(49.99);
89
+ });
90
+ });
91
+
92
+ describe('RECEIPT - Business is Creditor (User is Debtor)', () => {
93
+ it('should return negative amount when business is creditor', () => {
94
+ const result = normalizeDocumentAmount(100, true, 'RECEIPT');
95
+ expect(result).toBe(-100);
96
+ });
97
+
98
+ it('should handle typical receipt amounts', () => {
99
+ const result = normalizeDocumentAmount(89.50, true, 'RECEIPT');
100
+ expect(result).toBe(-89.50);
101
+ });
102
+ });
103
+
104
+ describe('INVOICE_RECEIPT - Business is Debtor (User is Creditor)', () => {
105
+ it('should return positive amount when business is debtor', () => {
106
+ const result = normalizeDocumentAmount(100, false, 'INVOICE_RECEIPT');
107
+ expect(result).toBe(100);
108
+ });
109
+
110
+ it('should handle combined invoice-receipt amounts', () => {
111
+ const result = normalizeDocumentAmount(350.00, false, 'INVOICE_RECEIPT');
112
+ expect(result).toBe(350.00);
113
+ });
114
+ });
115
+
116
+ describe('INVOICE_RECEIPT - Business is Creditor (User is Debtor)', () => {
117
+ it('should return negative amount when business is creditor', () => {
118
+ const result = normalizeDocumentAmount(100, true, 'INVOICE_RECEIPT');
119
+ expect(result).toBe(-100);
120
+ });
121
+
122
+ it('should handle combined invoice-receipt amounts', () => {
123
+ const result = normalizeDocumentAmount(425.75, true, 'INVOICE_RECEIPT');
124
+ expect(result).toBe(-425.75);
125
+ });
126
+ });
127
+
128
+ describe('OTHER Document Type', () => {
129
+ it('should handle OTHER type with business debtor', () => {
130
+ const result = normalizeDocumentAmount(100, false, 'OTHER');
131
+ expect(result).toBe(100);
132
+ });
133
+
134
+ it('should handle OTHER type with business creditor', () => {
135
+ const result = normalizeDocumentAmount(100, true, 'OTHER');
136
+ expect(result).toBe(-100);
137
+ });
138
+ });
139
+
140
+ describe('PROFORMA Document Type', () => {
141
+ it('should handle PROFORMA type with business debtor', () => {
142
+ const result = normalizeDocumentAmount(100, false, 'PROFORMA');
143
+ expect(result).toBe(100);
144
+ });
145
+
146
+ it('should handle PROFORMA type with business creditor', () => {
147
+ const result = normalizeDocumentAmount(100, true, 'PROFORMA');
148
+ expect(result).toBe(-100);
149
+ });
150
+ });
151
+
152
+ describe('UNPROCESSED Document Type', () => {
153
+ it('should handle UNPROCESSED type with business debtor', () => {
154
+ const result = normalizeDocumentAmount(100, false, 'UNPROCESSED');
155
+ expect(result).toBe(100);
156
+ });
157
+
158
+ it('should handle UNPROCESSED type with business creditor', () => {
159
+ const result = normalizeDocumentAmount(100, true, 'UNPROCESSED');
160
+ expect(result).toBe(-100);
161
+ });
162
+ });
163
+
164
+ describe('Negative Input Amounts (Absolute Value First)', () => {
165
+ it('should convert negative input to positive for INVOICE with business debtor', () => {
166
+ // Input is -100, should become |−100| = 100
167
+ const result = normalizeDocumentAmount(-100, false, 'INVOICE');
168
+ expect(result).toBe(100);
169
+ });
170
+
171
+ it('should convert negative input to negative for INVOICE with business creditor', () => {
172
+ // Input is -100, should become |−100| * -1 = -100
173
+ const result = normalizeDocumentAmount(-100, true, 'INVOICE');
174
+ expect(result).toBe(-100);
175
+ });
176
+
177
+ it('should convert negative input for CREDIT_INVOICE with business debtor', () => {
178
+ // Input is -100, should become |−100| * -1 = -100
179
+ const result = normalizeDocumentAmount(-100, false, 'CREDIT_INVOICE');
180
+ expect(result).toBe(-100);
181
+ });
182
+
183
+ it('should convert negative input for CREDIT_INVOICE with business creditor', () => {
184
+ // Input is -100, should become |−100| * -1 * -1 = 100
185
+ const result = normalizeDocumentAmount(-100, true, 'CREDIT_INVOICE');
186
+ expect(result).toBe(100);
187
+ });
188
+
189
+ it('should handle negative decimal amounts', () => {
190
+ const result = normalizeDocumentAmount(-250.50, false, 'INVOICE');
191
+ expect(result).toBe(250.50);
192
+ });
193
+
194
+ it('should handle large negative amounts', () => {
195
+ const result = normalizeDocumentAmount(-1000000, true, 'RECEIPT');
196
+ expect(result).toBe(-1000000);
197
+ });
198
+ });
199
+
200
+ describe('Zero Amounts', () => {
201
+ it('should handle zero amount with business debtor', () => {
202
+ const result = normalizeDocumentAmount(0, false, 'INVOICE');
203
+ expect(result).toBe(0);
204
+ });
205
+
206
+ it('should handle zero amount with business creditor', () => {
207
+ const result = normalizeDocumentAmount(0, true, 'INVOICE');
208
+ expect(result).toBe(-0); // JavaScript has -0, but 0 === -0 is true
209
+ });
210
+
211
+ it('should handle zero for CREDIT_INVOICE', () => {
212
+ const result = normalizeDocumentAmount(0, false, 'CREDIT_INVOICE');
213
+ expect(result).toBe(-0);
214
+ });
215
+
216
+ it('should verify zero equality regardless of sign', () => {
217
+ const result1 = normalizeDocumentAmount(0, false, 'INVOICE');
218
+ const result2 = normalizeDocumentAmount(0, true, 'INVOICE');
219
+ expect(result1).toBe(0);
220
+ // In JavaScript, -0 and 0 are loosely equal but Object.is distinguishes them
221
+ // For our purposes, they are functionally equivalent
222
+ expect(result2 == 0).toBe(true); // Using == instead of === for -0 case
223
+ });
224
+ });
225
+
226
+ describe('Very Large Amounts', () => {
227
+ it('should handle very large positive amounts', () => {
228
+ const largeAmount = 999999999.99;
229
+ const result = normalizeDocumentAmount(largeAmount, false, 'INVOICE');
230
+ expect(result).toBe(largeAmount);
231
+ });
232
+
233
+ it('should handle very large amounts with creditor negation', () => {
234
+ const largeAmount = 999999999.99;
235
+ const result = normalizeDocumentAmount(largeAmount, true, 'INVOICE');
236
+ expect(result).toBe(-largeAmount);
237
+ });
238
+
239
+ it('should handle very large amounts with CREDIT_INVOICE', () => {
240
+ const largeAmount = 888888888.88;
241
+ const result = normalizeDocumentAmount(largeAmount, true, 'CREDIT_INVOICE');
242
+ expect(result).toBe(largeAmount);
243
+ });
244
+
245
+ it('should handle maximum safe integer', () => {
246
+ const maxSafe = Number.MAX_SAFE_INTEGER;
247
+ const result = normalizeDocumentAmount(maxSafe, false, 'INVOICE');
248
+ expect(result).toBe(maxSafe);
249
+ });
250
+ });
251
+
252
+ describe('Small Decimal Amounts', () => {
253
+ it('should handle very small decimal amounts', () => {
254
+ const result = normalizeDocumentAmount(0.01, false, 'INVOICE');
255
+ expect(result).toBe(0.01);
256
+ });
257
+
258
+ it('should handle fractional cents', () => {
259
+ const result = normalizeDocumentAmount(0.001, true, 'RECEIPT');
260
+ expect(result).toBe(-0.001);
261
+ });
262
+
263
+ it('should preserve precision for small decimals', () => {
264
+ const result = normalizeDocumentAmount(1.23456789, false, 'INVOICE');
265
+ expect(result).toBe(1.23456789);
266
+ });
267
+ });
268
+
269
+ describe('All Document Types Summary Table', () => {
270
+ // This test documents the expected behavior for all combinations
271
+ it('should correctly normalize all document type and business role combinations', () => {
272
+ const testCases: Array<{
273
+ type: DocumentType;
274
+ isCreditor: boolean;
275
+ input: number;
276
+ expected: number;
277
+ description: string;
278
+ }> = [
279
+ // INVOICE
280
+ { type: 'INVOICE', isCreditor: false, input: 100, expected: 100, description: 'INVOICE, business debtor' },
281
+ { type: 'INVOICE', isCreditor: true, input: 100, expected: -100, description: 'INVOICE, business creditor' },
282
+
283
+ // CREDIT_INVOICE
284
+ { type: 'CREDIT_INVOICE', isCreditor: false, input: 100, expected: -100, description: 'CREDIT_INVOICE, business debtor' },
285
+ { type: 'CREDIT_INVOICE', isCreditor: true, input: 100, expected: 100, description: 'CREDIT_INVOICE, business creditor (double neg)' },
286
+
287
+ // RECEIPT
288
+ { type: 'RECEIPT', isCreditor: false, input: 100, expected: 100, description: 'RECEIPT, business debtor' },
289
+ { type: 'RECEIPT', isCreditor: true, input: 100, expected: -100, description: 'RECEIPT, business creditor' },
290
+
291
+ // INVOICE_RECEIPT
292
+ { type: 'INVOICE_RECEIPT', isCreditor: false, input: 100, expected: 100, description: 'INVOICE_RECEIPT, business debtor' },
293
+ { type: 'INVOICE_RECEIPT', isCreditor: true, input: 100, expected: -100, description: 'INVOICE_RECEIPT, business creditor' },
294
+
295
+ // OTHER
296
+ { type: 'OTHER', isCreditor: false, input: 100, expected: 100, description: 'OTHER, business debtor' },
297
+ { type: 'OTHER', isCreditor: true, input: 100, expected: -100, description: 'OTHER, business creditor' },
298
+
299
+ // PROFORMA
300
+ { type: 'PROFORMA', isCreditor: false, input: 100, expected: 100, description: 'PROFORMA, business debtor' },
301
+ { type: 'PROFORMA', isCreditor: true, input: 100, expected: -100, description: 'PROFORMA, business creditor' },
302
+
303
+ // UNPROCESSED
304
+ { type: 'UNPROCESSED', isCreditor: false, input: 100, expected: 100, description: 'UNPROCESSED, business debtor' },
305
+ { type: 'UNPROCESSED', isCreditor: true, input: 100, expected: -100, description: 'UNPROCESSED, business creditor' },
306
+ ];
307
+
308
+ testCases.forEach(({ type, isCreditor, input, expected, description }) => {
309
+ const result = normalizeDocumentAmount(input, isCreditor, type);
310
+ expect(result).toBe(expected);
311
+ });
312
+ });
313
+ });
314
+
315
+ describe('Real-World Scenarios', () => {
316
+ it('should normalize customer invoice (business sends invoice to customer)', () => {
317
+ // Business is creditor (customer owes business)
318
+ const result = normalizeDocumentAmount(1500, true, 'INVOICE');
319
+ expect(result).toBe(-1500); // Negative because user owes
320
+ });
321
+
322
+ it('should normalize vendor invoice (vendor sends invoice to business)', () => {
323
+ // Business is debtor (business owes vendor)
324
+ const result = normalizeDocumentAmount(750, false, 'INVOICE');
325
+ expect(result).toBe(750); // Positive because vendor owes user
326
+ });
327
+
328
+ it('should normalize customer refund (credit invoice to customer)', () => {
329
+ // Business owes customer a refund
330
+ const result = normalizeDocumentAmount(200, true, 'CREDIT_INVOICE');
331
+ expect(result).toBe(200); // Positive because business owes refund
332
+ });
333
+
334
+ it('should normalize vendor credit note (credit invoice from vendor)', () => {
335
+ // Vendor gives credit note to business
336
+ const result = normalizeDocumentAmount(150, false, 'CREDIT_INVOICE');
337
+ expect(result).toBe(-150); // Negative because business owes vendor less now
338
+ });
339
+
340
+ it('should normalize payment receipt from customer', () => {
341
+ // Customer pays business
342
+ const result = normalizeDocumentAmount(500, true, 'RECEIPT');
343
+ expect(result).toBe(-500); // Negative because customer paid
344
+ });
345
+
346
+ it('should normalize payment receipt to vendor', () => {
347
+ // Business pays vendor
348
+ const result = normalizeDocumentAmount(300, false, 'RECEIPT');
349
+ expect(result).toBe(300); // Positive because business paid
350
+ });
351
+ });
352
+ });
@@ -0,0 +1,192 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ extractDocumentBusiness,
4
+ } from '../helpers/document-business.helper.js';
5
+
6
+ describe('extractDocumentBusiness', () => {
7
+ const adminBusinessId = 'user-123';
8
+ const businessId1 = 'business-456';
9
+ const businessId2 = 'business-789';
10
+
11
+ describe('valid scenarios - user is debtor', () => {
12
+ it('should return creditor as business when user is debtor', () => {
13
+ const result = extractDocumentBusiness(businessId1, adminBusinessId, adminBusinessId);
14
+
15
+ expect(result).toEqual({
16
+ businessId: businessId1,
17
+ isBusinessCreditor: true,
18
+ });
19
+ });
20
+
21
+ it('should handle null creditor when user is debtor', () => {
22
+ const result = extractDocumentBusiness(null, adminBusinessId, adminBusinessId);
23
+
24
+ expect(result).toEqual({
25
+ businessId: null,
26
+ isBusinessCreditor: true,
27
+ });
28
+ });
29
+
30
+ it('should work with different business IDs when user is debtor', () => {
31
+ const result = extractDocumentBusiness(businessId2, adminBusinessId, adminBusinessId);
32
+
33
+ expect(result).toEqual({
34
+ businessId: businessId2,
35
+ isBusinessCreditor: true,
36
+ });
37
+ });
38
+ });
39
+
40
+ describe('valid scenarios - user is creditor', () => {
41
+ it('should return debtor as business when user is creditor', () => {
42
+ const result = extractDocumentBusiness(adminBusinessId, businessId1, adminBusinessId);
43
+
44
+ expect(result).toEqual({
45
+ businessId: businessId1,
46
+ isBusinessCreditor: false,
47
+ });
48
+ });
49
+
50
+ it('should handle null debtor when user is creditor', () => {
51
+ const result = extractDocumentBusiness(adminBusinessId, null, adminBusinessId);
52
+
53
+ expect(result).toEqual({
54
+ businessId: null,
55
+ isBusinessCreditor: false,
56
+ });
57
+ });
58
+
59
+ it('should work with different business IDs when user is creditor', () => {
60
+ const result = extractDocumentBusiness(adminBusinessId, businessId2, adminBusinessId);
61
+
62
+ expect(result).toEqual({
63
+ businessId: businessId2,
64
+ isBusinessCreditor: false,
65
+ });
66
+ });
67
+ });
68
+
69
+ describe('error scenarios - both are user', () => {
70
+ it('should throw error when both creditor and debtor are user', () => {
71
+ expect(() => extractDocumentBusiness(adminBusinessId, adminBusinessId, adminBusinessId)).toThrow(
72
+ 'Document has both creditor_id and debtor_id equal to user ID - invalid document state',
73
+ );
74
+ });
75
+
76
+ it('should include clear error message for both-user scenario', () => {
77
+ try {
78
+ extractDocumentBusiness(adminBusinessId, adminBusinessId, adminBusinessId);
79
+ expect.fail('Should have thrown an error');
80
+ } catch (error) {
81
+ expect(error).toBeInstanceOf(Error);
82
+ expect((error as Error).message).toContain('both creditor_id and debtor_id equal to user');
83
+ }
84
+ });
85
+ });
86
+
87
+ describe('error scenarios - neither are user', () => {
88
+ it('should throw error when neither creditor nor debtor are user', () => {
89
+ expect(() => extractDocumentBusiness(businessId1, businessId2, adminBusinessId)).toThrow(
90
+ 'Document has neither creditor_id nor debtor_id equal to user ID - document does not belong to user',
91
+ );
92
+ });
93
+
94
+ it('should throw error when creditor is business and debtor is null (neither is user)', () => {
95
+ expect(() => extractDocumentBusiness(businessId1, null, adminBusinessId)).toThrow(
96
+ 'Document has neither creditor_id nor debtor_id equal to user ID - document does not belong to user',
97
+ );
98
+ });
99
+
100
+ it('should throw error when debtor is business and creditor is null (neither is user)', () => {
101
+ expect(() => extractDocumentBusiness(null, businessId1, adminBusinessId)).toThrow(
102
+ 'Document has neither creditor_id nor debtor_id equal to user ID - document does not belong to user',
103
+ );
104
+ });
105
+
106
+ it('should include clear error message for neither-user scenario', () => {
107
+ try {
108
+ extractDocumentBusiness(businessId1, businessId2, adminBusinessId);
109
+ expect.fail('Should have thrown an error');
110
+ } catch (error) {
111
+ expect(error).toBeInstanceOf(Error);
112
+ expect((error as Error).message).toContain('neither creditor_id nor debtor_id equal to user');
113
+ }
114
+ });
115
+ });
116
+
117
+ describe('error scenarios - both are null', () => {
118
+ it('should throw error when both creditor and debtor are null', () => {
119
+ expect(() => extractDocumentBusiness(null, null, adminBusinessId)).toThrow(
120
+ 'Document has both creditor_id and debtor_id as null - invalid document state',
121
+ );
122
+ });
123
+
124
+ it('should include clear error message for both-null scenario', () => {
125
+ try {
126
+ extractDocumentBusiness(null, null, adminBusinessId);
127
+ expect.fail('Should have thrown an error');
128
+ } catch (error) {
129
+ expect(error).toBeInstanceOf(Error);
130
+ expect((error as Error).message).toContain('both creditor_id and debtor_id as null');
131
+ }
132
+ });
133
+ });
134
+
135
+ describe('return value structure', () => {
136
+ it('should return object with correct properties', () => {
137
+ const result = extractDocumentBusiness(businessId1, adminBusinessId, adminBusinessId);
138
+
139
+ expect(result).toHaveProperty('businessId');
140
+ expect(result).toHaveProperty('isBusinessCreditor');
141
+ expect(typeof result.isBusinessCreditor).toBe('boolean');
142
+ });
143
+
144
+ it('should return consistent structure for null business', () => {
145
+ const result = extractDocumentBusiness(null, adminBusinessId, adminBusinessId);
146
+
147
+ expect(result).toHaveProperty('businessId');
148
+ expect(result).toHaveProperty('isBusinessCreditor');
149
+ expect(result.businessId).toBe(null);
150
+ expect(typeof result.isBusinessCreditor).toBe('boolean');
151
+ });
152
+ });
153
+
154
+ describe('business creditor flag correctness', () => {
155
+ it('should set isBusinessCreditor to true when user is debtor (business is creditor)', () => {
156
+ const result = extractDocumentBusiness(businessId1, adminBusinessId, adminBusinessId);
157
+ expect(result.isBusinessCreditor).toBe(true);
158
+ });
159
+
160
+ it('should set isBusinessCreditor to false when user is creditor (business is debtor)', () => {
161
+ const result = extractDocumentBusiness(adminBusinessId, businessId1, adminBusinessId);
162
+ expect(result.isBusinessCreditor).toBe(false);
163
+ });
164
+
165
+ it('should set isBusinessCreditor correctly even when business is null', () => {
166
+ const resultDebtorUser = extractDocumentBusiness(null, adminBusinessId, adminBusinessId);
167
+ expect(resultDebtorUser.isBusinessCreditor).toBe(true);
168
+
169
+ const resultCreditorUser = extractDocumentBusiness(adminBusinessId, null, adminBusinessId);
170
+ expect(resultCreditorUser.isBusinessCreditor).toBe(false);
171
+ });
172
+ });
173
+
174
+ describe('edge cases with different user IDs', () => {
175
+ it('should work correctly with different user ID format', () => {
176
+ const differentUser = 'abc-def-ghi';
177
+ const result = extractDocumentBusiness(businessId1, differentUser, differentUser);
178
+
179
+ expect(result.businessId).toBe(businessId1);
180
+ expect(result.isBusinessCreditor).toBe(true);
181
+ });
182
+
183
+ it('should correctly identify when user is creditor with UUID-like IDs', () => {
184
+ const uuidUser = '550e8400-e29b-41d4-a716-446655440000';
185
+ const uuidBusiness = '660e8400-e29b-41d4-a716-446655440000';
186
+ const result = extractDocumentBusiness(uuidUser, uuidBusiness, uuidUser);
187
+
188
+ expect(result.businessId).toBe(uuidBusiness);
189
+ expect(result.isBusinessCreditor).toBe(false);
190
+ });
191
+ });
192
+ });