@accounter/server 0.0.9-alpha-20251211102658-1b334ef361fc6faf8cd44900059dd236994fe9ab → 0.0.9-alpha-20251211102754-3592a860d1348674bd09af63fa304ff1e439a1dd

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 (115) hide show
  1. package/CHANGELOG.md +5 -5
  2. package/GMAIL_LISTENER.md +7 -7
  3. package/dist/green-invoice-graphql/src/mesh-artifacts/index.d.ts +1 -1
  4. package/dist/server/src/__tests__/factories/charge.d.ts +3 -1
  5. package/dist/server/src/__tests__/factories/charge.js +2 -1
  6. package/dist/server/src/__tests__/factories/charge.js.map +1 -1
  7. package/dist/server/src/__tests__/factories/transaction.d.ts +3 -1
  8. package/dist/server/src/__tests__/factories/transaction.js +1 -0
  9. package/dist/server/src/__tests__/factories/transaction.js.map +1 -1
  10. package/dist/server/src/__tests__/helpers/fixture-loader.d.ts +7 -3
  11. package/dist/server/src/__tests__/helpers/fixture-loader.js +28 -20
  12. package/dist/server/src/__tests__/helpers/fixture-loader.js.map +1 -1
  13. package/dist/server/src/__tests__/helpers/fixture-types.d.ts +1 -3
  14. package/dist/server/src/modules/accountant-approval/index.js +1 -1
  15. package/dist/server/src/modules/accountant-approval/index.js.map +1 -1
  16. package/dist/server/src/modules/admin-context/index.js +1 -1
  17. package/dist/server/src/modules/admin-context/index.js.map +1 -1
  18. package/dist/server/src/modules/bank-deposits/index.js +1 -1
  19. package/dist/server/src/modules/bank-deposits/index.js.map +1 -1
  20. package/dist/server/src/modules/bank-deposits/providers/bank-deposit-transactions.provider.d.ts +4 -4
  21. package/dist/server/src/modules/business-trips/index.js +3 -3
  22. package/dist/server/src/modules/business-trips/index.js.map +1 -1
  23. package/dist/server/src/modules/charges/index.js +4 -4
  24. package/dist/server/src/modules/charges/index.js.map +1 -1
  25. package/dist/server/src/modules/charges-matcher/index.js +1 -1
  26. package/dist/server/src/modules/charges-matcher/index.js.map +1 -1
  27. package/dist/server/src/modules/charts/index.js +1 -1
  28. package/dist/server/src/modules/charts/index.js.map +1 -1
  29. package/dist/server/src/modules/common/index.js +4 -4
  30. package/dist/server/src/modules/common/index.js.map +1 -1
  31. package/dist/server/src/modules/common/resolvers/timeless-date.d.ts +1 -1
  32. package/dist/server/src/modules/contracts/index.js +1 -1
  33. package/dist/server/src/modules/contracts/index.js.map +1 -1
  34. package/dist/server/src/modules/corn-jobs/index.js +1 -1
  35. package/dist/server/src/modules/corn-jobs/index.js.map +1 -1
  36. package/dist/server/src/modules/corporate-taxes/index.js +1 -1
  37. package/dist/server/src/modules/corporate-taxes/index.js.map +1 -1
  38. package/dist/server/src/modules/corporate-taxes/providers/corporate-taxes.provider.d.ts +1 -1
  39. package/dist/server/src/modules/countries/index.js +1 -1
  40. package/dist/server/src/modules/countries/index.js.map +1 -1
  41. package/dist/server/src/modules/deel/index.js +1 -1
  42. package/dist/server/src/modules/deel/index.js.map +1 -1
  43. package/dist/server/src/modules/depreciation/index.js +1 -1
  44. package/dist/server/src/modules/depreciation/index.js.map +1 -1
  45. package/dist/server/src/modules/dividends/index.js +1 -1
  46. package/dist/server/src/modules/dividends/index.js.map +1 -1
  47. package/dist/server/src/modules/documents/index.js +3 -3
  48. package/dist/server/src/modules/documents/index.js.map +1 -1
  49. package/dist/server/src/modules/exchange-rates/index.js +1 -1
  50. package/dist/server/src/modules/exchange-rates/index.js.map +1 -1
  51. package/dist/server/src/modules/financial-accounts/index.js +2 -2
  52. package/dist/server/src/modules/financial-accounts/index.js.map +1 -1
  53. package/dist/server/src/modules/financial-entities/index.js +6 -6
  54. package/dist/server/src/modules/financial-entities/index.js.map +1 -1
  55. package/dist/server/src/modules/green-invoice/index.js +1 -1
  56. package/dist/server/src/modules/green-invoice/index.js.map +1 -1
  57. package/dist/server/src/modules/ledger/__tests__/helpers/ledger-assertions.js +5 -5
  58. package/dist/server/src/modules/ledger/__tests__/helpers/ledger-assertions.js.map +1 -1
  59. package/dist/server/src/modules/ledger/index.js +1 -1
  60. package/dist/server/src/modules/ledger/index.js.map +1 -1
  61. package/dist/server/src/modules/misc-expenses/index.js +1 -1
  62. package/dist/server/src/modules/misc-expenses/index.js.map +1 -1
  63. package/dist/server/src/modules/reports/helpers/pcn.helper.d.ts +1 -1
  64. package/dist/server/src/modules/reports/index.js +12 -12
  65. package/dist/server/src/modules/reports/index.js.map +1 -1
  66. package/dist/server/src/modules/salaries/index.js +3 -3
  67. package/dist/server/src/modules/salaries/index.js.map +1 -1
  68. package/dist/server/src/modules/sort-codes/index.js +1 -1
  69. package/dist/server/src/modules/sort-codes/index.js.map +1 -1
  70. package/dist/server/src/modules/tags/index.js +1 -1
  71. package/dist/server/src/modules/tags/index.js.map +1 -1
  72. package/dist/server/src/modules/transactions/helpers/effective-date.helper.d.ts +1 -1
  73. package/dist/server/src/modules/transactions/index.js +3 -3
  74. package/dist/server/src/modules/transactions/index.js.map +1 -1
  75. package/dist/server/src/modules/vat/index.js +1 -1
  76. package/dist/server/src/modules/vat/index.js.map +1 -1
  77. package/dist/server/src/modules/vat/providers/vat.provider.d.ts +1 -1
  78. package/docs/implementation-guide.md +2 -2
  79. package/package.json +1 -1
  80. package/src/__tests__/factories/charge.ts +4 -2
  81. package/src/__tests__/factories/transaction.ts +3 -1
  82. package/src/__tests__/helpers/fixture-loader.ts +30 -22
  83. package/src/__tests__/helpers/fixture-types.ts +1 -3
  84. package/src/modules/accountant-approval/index.ts +1 -1
  85. package/src/modules/admin-context/index.ts +1 -1
  86. package/src/modules/bank-deposits/index.ts +1 -1
  87. package/src/modules/business-trips/index.ts +3 -3
  88. package/src/modules/charges/index.ts +4 -4
  89. package/src/modules/charges-matcher/README.md +12 -12
  90. package/src/modules/charges-matcher/documentation/SPEC.md +103 -103
  91. package/src/modules/charges-matcher/index.ts +1 -1
  92. package/src/modules/charts/index.ts +1 -1
  93. package/src/modules/common/index.ts +4 -4
  94. package/src/modules/contracts/index.ts +1 -1
  95. package/src/modules/corn-jobs/index.ts +1 -1
  96. package/src/modules/corporate-taxes/index.ts +1 -1
  97. package/src/modules/countries/index.ts +1 -1
  98. package/src/modules/deel/index.ts +1 -1
  99. package/src/modules/depreciation/index.ts +1 -1
  100. package/src/modules/dividends/index.ts +1 -1
  101. package/src/modules/documents/index.ts +3 -3
  102. package/src/modules/exchange-rates/index.ts +1 -1
  103. package/src/modules/financial-accounts/index.ts +2 -2
  104. package/src/modules/financial-entities/index.ts +6 -6
  105. package/src/modules/green-invoice/index.ts +1 -1
  106. package/src/modules/ledger/__tests__/helpers/ledger-assertions.ts +5 -6
  107. package/src/modules/ledger/__tests__/ledger-scenario-a.integration.test.ts +2 -2
  108. package/src/modules/ledger/index.ts +1 -1
  109. package/src/modules/misc-expenses/index.ts +1 -1
  110. package/src/modules/reports/index.ts +12 -12
  111. package/src/modules/salaries/index.ts +3 -3
  112. package/src/modules/sort-codes/index.ts +1 -1
  113. package/src/modules/tags/index.ts +1 -1
  114. package/src/modules/transactions/index.ts +3 -3
  115. package/src/modules/vat/index.ts +1 -1
@@ -91,8 +91,8 @@ Avoids full GraphQL application boot:
91
91
  Usage Pattern:
92
92
 
93
93
  ```typescript
94
- const { injector, adminContext } = createLedgerTestContext({ pool, mockExchangeRates })
95
- const result = await ledgerGenerationByCharge({ chargeId }, { injector, adminContext })
94
+ const { injector, adminContext } = createLedgerTestContext({ pool, mockExchangeRates });
95
+ const result = await ledgerGenerationByCharge({ chargeId }, { injector, adminContext });
96
96
  ```
97
97
 
98
98
  ## 8. Environment Isolation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@accounter/server",
3
- "version": "0.0.9-alpha-20251211102658-1b334ef361fc6faf8cd44900059dd236994fe9ab",
3
+ "version": "0.0.9-alpha-20251211102754-3592a860d1348674bd09af63fa304ff1e439a1dd",
4
4
  "type": "module",
5
5
  "description": "",
6
6
  "license": "MIT",
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Based on charges table schema from migrations and charges.provider.ts:
7
7
  * - Required: owner_id
8
- * - Optional: type, accountant_status, user_description, tax_category_id, optional_vat, documents_optional_flag
8
+ * - Optional: type, accountant_status, user_description, tax_category_id, optional_vat, documents_optional_flag, is_property
9
9
  *
10
10
  * @see packages/server/src/modules/charges/providers/charges.provider.ts (generateCharge query)
11
11
  * @see packages/migrations/src/actions/*-charges-*.ts (migrations)
@@ -22,6 +22,7 @@ import { makeUUID, makeUUIDLegacy } from '../../demo-fixtures/helpers/determinis
22
22
  export interface ChargeInsertParams {
23
23
  id?: string;
24
24
  owner_id: string;
25
+ is_property?: boolean | null;
25
26
  type?: string | null;
26
27
  accountant_status?: string | null;
27
28
  user_description?: string | null;
@@ -45,6 +46,7 @@ export interface ChargeInsertParams {
45
46
  * - accountant_status defaults to 'PENDING' (database requires NOT NULL)
46
47
  * - optional_vat defaults to false (database requires NOT NULL)
47
48
  * - documents_optional_flag defaults to false (database requires NOT NULL)
49
+ * - is_property defaults to null (property-related charges can override to true)
48
50
  * - id defaults to deterministic UUID if not provided
49
51
  *
50
52
  * @example
@@ -97,4 +99,4 @@ export function createCharge(
97
99
  documents_optional_flag: false,
98
100
  ...overrides,
99
101
  };
100
- }
102
+ }
@@ -31,9 +31,10 @@ export interface TransactionInsertParams {
31
31
  event_date: string;
32
32
  debit_date?: string | null;
33
33
  amount: string; // PostgreSQL numeric -> string
34
- current_balance: string; // PostgreSQL numeric -> string
34
+ current_balance?: string; // PostgreSQL numeric -> string
35
35
  business_id?: string | null;
36
36
  is_fee?: boolean | null;
37
+ currency_rate?: number | null;
37
38
  }
38
39
 
39
40
  /**
@@ -50,6 +51,7 @@ export interface TransactionInsertParams {
50
51
  * - currency is required (e.g., 'ILS', 'USD', 'EUR')
51
52
  * - event_date is required (transaction date)
52
53
  * - is_fee defaults to false (regular transaction, not a fee)
54
+ * - currency_rate defaults to null (foreign currency transactions can specify exchange rate)
53
55
  * - account_id defaults to deterministic UUID if not provided
54
56
  * - source_id defaults to deterministic UUID (raw transaction reference)
55
57
  * - source_description defaults to null
@@ -8,7 +8,7 @@
8
8
  * @see packages/server/src/__tests__/helpers/fixture-validation.ts for validation logic
9
9
  */
10
10
 
11
- import type { PoolClient } from 'pg';
11
+ import type { Client, PoolClient } from 'pg';
12
12
  import type { Fixture } from './fixture-types.js';
13
13
  import { assertValidFixture } from './fixture-validation.js';
14
14
  import { qualifyTable } from './test-db-config.js';
@@ -85,12 +85,16 @@ export type FixtureIdMapping = Map<string, string>;
85
85
  * - Checks referential integrity (charge IDs, business IDs, etc.)
86
86
  * - Validates required fields
87
87
  *
88
- * @param client - PostgreSQL client within an active transaction
88
+ * @param client - PostgreSQL client (PoolClient or standalone Client) within an active transaction
89
89
  * @param fixture - Complete fixture to insert
90
90
  * @returns Promise resolving to ID mapping (fixture ID → database ID)
91
91
  * @throws {Error} If fixture validation fails (via assertValidFixture)
92
92
  * @throws {FixtureInsertionError} If any insertion section fails
93
93
  *
94
+ * @remarks
95
+ * Type Safety: Accepts both PoolClient and Client to support both test transactions
96
+ * (pool.connect()) and standalone connections (new pg.Client()) used in seed scripts.
97
+ *
94
98
  * @example
95
99
  * ```typescript
96
100
  * import { withTestTransaction } from './test-transaction.js';
@@ -112,7 +116,7 @@ export type FixtureIdMapping = Map<string, string>;
112
116
  * ```
113
117
  */
114
118
  export async function insertFixture(
115
- client: PoolClient,
119
+ client: PoolClient | Client,
116
120
  fixture: Fixture,
117
121
  ): Promise<FixtureIdMapping> {
118
122
  // Validate fixture before insertion
@@ -174,13 +178,13 @@ export async function insertFixture(
174
178
  business.website,
175
179
  business.phoneNumber,
176
180
  business.governmentId, // Maps to vat_number column
177
- business.exemptDealer,
178
- business.suggestions,
179
- business.optionalVat,
180
- business.country,
181
- business.pcn874RecordTypeOverride,
182
- business.isReceiptEnough,
183
- business.isDocumentsOptional,
181
+ business.exemptDealer ?? false,
182
+ business.suggestions ?? null,
183
+ business.optionalVat ?? false,
184
+ business.country ?? 'ISR',
185
+ business.pcn874RecordTypeOverride ?? null,
186
+ business.isReceiptEnough ?? false,
187
+ business.isDocumentsOptional ?? false,
184
188
  ],
185
189
  );
186
190
 
@@ -214,7 +218,7 @@ export async function insertFixture(
214
218
  )
215
219
  VALUES ($1, $2, $3)
216
220
  ON CONFLICT (id) DO NOTHING`,
217
- [taxCategory.id, taxCategory.hashavshevetName, taxCategory.taxExcluded],
221
+ [taxCategory.id, taxCategory.hashavshevetName, taxCategory.taxExcluded ?? false],
218
222
  );
219
223
 
220
224
  if (entityResult.rows.length > 0) {
@@ -293,19 +297,21 @@ export async function insertFixture(
293
297
  await client.query(
294
298
  `INSERT INTO ${qualifyTable('charges')} (
295
299
  id, owner_id, type, accountant_status, user_description,
296
- tax_category_id, optional_vat, documents_optional_flag
300
+ tax_category_id, optional_vat, documents_optional_flag,
301
+ is_property, created_at, updated_at
297
302
  )
298
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
303
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW(), NOW())
299
304
  ON CONFLICT (id) DO NOTHING`,
300
305
  [
301
306
  charge.id,
302
307
  charge.owner_id,
303
- charge.type,
304
- charge.accountant_status,
308
+ charge.type ?? null,
309
+ charge.accountant_status ?? 'PENDING',
305
310
  charge.user_description,
306
- charge.tax_category_id,
307
- charge.optional_vat,
308
- charge.documents_optional_flag,
311
+ charge.tax_category_id ?? null,
312
+ charge.optional_vat ?? false,
313
+ charge.documents_optional_flag ?? false,
314
+ charge.is_property ?? false,
309
315
  ],
310
316
  );
311
317
 
@@ -349,9 +355,10 @@ export async function insertFixture(
349
355
  `INSERT INTO ${qualifyTable('transactions')} (
350
356
  id, account_id, charge_id, source_id, source_description,
351
357
  currency, event_date, debit_date, amount, current_balance,
352
- business_id, is_fee, source_reference, source_origin, origin_key
358
+ business_id, is_fee, source_reference, source_origin, origin_key,
359
+ currency_rate, created_at, updated_at
353
360
  )
354
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
361
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, NOW(), NOW())
355
362
  ON CONFLICT (id) DO NOTHING`,
356
363
  [
357
364
  transaction.id,
@@ -363,12 +370,13 @@ export async function insertFixture(
363
370
  transaction.event_date,
364
371
  transaction.debit_date,
365
372
  transaction.amount,
366
- transaction.current_balance,
373
+ transaction.current_balance ?? '0',
367
374
  transaction.business_id,
368
- transaction.is_fee,
375
+ transaction.is_fee ?? false,
369
376
  'TEST-REF', // source_reference (NOT NULL)
370
377
  'TEST', // source_origin (NOT NULL)
371
378
  dummyEtherscanId, // origin_key (NOT NULL) - must match etherscan_id in raw list
379
+ transaction.currency_rate ?? 1,
372
380
  ],
373
381
  );
374
382
 
@@ -13,9 +13,7 @@
13
13
  * @see packages/server/src/__tests__/helpers/fixture-loader.ts for insertion logic
14
14
  */
15
15
 
16
- import type { ChargeInsertParams } from '../factories/charge.js';
17
- import type { TransactionInsertParams } from '../factories/transaction.js';
18
- import type { DocumentInsertParams } from '../factories/document.js';
16
+ import type { ChargeInsertParams, TransactionInsertParams, DocumentInsertParams } from '../factories/index.js';
19
17
  import type { financial_account_type } from '../../modules/transactions/types.js';
20
18
 
21
19
  /**
@@ -1,6 +1,6 @@
1
+ import accountantApproval from './typeDefs/accountant-approval.graphql.js';
1
2
  import { createModule } from 'graphql-modules';
2
3
  import { accountantApprovalResolvers } from './resolvers/accountant-approval.resolver.js';
3
- import accountantApproval from './typeDefs/accountant-approval.graphql.js';
4
4
 
5
5
  const __dirname = new URL('.', import.meta.url).pathname;
6
6
 
@@ -1,7 +1,7 @@
1
+ import adminContext from './typeDefs/admin-context.graphql.js';
1
2
  import { createModule } from 'graphql-modules';
2
3
  import { AdminContextProvider } from './providers/admin-context.provider.js';
3
4
  import { adminContextResolvers } from './resolvers/admin-context.resolvers.js';
4
- import adminContext from './typeDefs/admin-context.graphql.js';
5
5
 
6
6
  const __dirname = new URL('.', import.meta.url).pathname;
7
7
 
@@ -1,7 +1,7 @@
1
+ import bankDeposits from './typeDefs/bank-deposits.graphql.js';
1
2
  import { createModule } from 'graphql-modules';
2
3
  import { BankDepositTransactionsProvider } from './providers/bank-deposit-transactions.provider.js';
3
4
  import { bankDepositTransactionsResolvers } from './resolvers/bank-deposit-transactions.resolver.js';
4
- import bankDeposits from './typeDefs/bank-deposits.graphql.js';
5
5
 
6
6
  const __dirname = new URL('.', import.meta.url).pathname;
7
7
 
@@ -1,3 +1,6 @@
1
+ import businessTripAttendees from './typeDefs/business-trip-attendees.graphql.js';
2
+ import businessTripExpenses from './typeDefs/business-trip-expenses.graphql.js';
3
+ import businessTrips from './typeDefs/business-trips.graphql.js';
1
4
  import { createModule } from 'graphql-modules';
2
5
  import { BusinessTripAttendeesProvider } from './providers/business-trips-attendees.provider.js';
3
6
  import { BusinessTripEmployeePaymentsProvider } from './providers/business-trips-employee-payments.provider.js';
@@ -13,9 +16,6 @@ import { BusinessTripsProvider } from './providers/business-trips.provider.js';
13
16
  import { businessTripAttendeesResolvers } from './resolvers/business-trip-attendees.resolver.js';
14
17
  import { businessTripExpensesResolvers } from './resolvers/business-trips-expenses.resolver.js';
15
18
  import { businessTripsResolvers } from './resolvers/business-trips.resolver.js';
16
- import businessTripAttendees from './typeDefs/business-trip-attendees.graphql.js';
17
- import businessTripExpenses from './typeDefs/business-trip-expenses.graphql.js';
18
- import businessTrips from './typeDefs/business-trips.graphql.js';
19
19
 
20
20
  const __dirname = new URL('.', import.meta.url).pathname;
21
21
 
@@ -1,13 +1,13 @@
1
+ import chargeSuggestions from './typeDefs/charge-suggestions.graphql.js';
2
+ import chargeValidation from './typeDefs/charge-validation.graphql.js';
3
+ import charges from './typeDefs/charges.graphql.js';
4
+ import financialCharges from './typeDefs/financial-charges.graphql.js';
1
5
  import { createModule } from 'graphql-modules';
2
6
  import { ChargeSpreadProvider } from './providers/charge-spread.provider.js';
3
7
  import { ChargesProvider } from './providers/charges.provider.js';
4
8
  import { chargeSuggestionsResolvers } from './resolvers/charge-suggestions/charge-suggestions.resolver.js';
5
9
  import { chargesResolvers } from './resolvers/charges.resolver.js';
6
10
  import { financialChargesResolvers } from './resolvers/financial-charges.resolver.js';
7
- import chargeSuggestions from './typeDefs/charge-suggestions.graphql.js';
8
- import chargeValidation from './typeDefs/charge-validation.graphql.js';
9
- import charges from './typeDefs/charges.graphql.js';
10
- import financialCharges from './typeDefs/financial-charges.graphql.js';
11
11
 
12
12
  const __dirname = new URL('.', import.meta.url).pathname;
13
13
 
@@ -107,17 +107,17 @@ import {
107
107
  createMockTransaction,
108
108
  createMockDocument,
109
109
  createMockAggregatedTransaction,
110
- createMockAggregatedDocument
111
- } from './__tests__/test-helpers.js'
110
+ createMockAggregatedDocument,
111
+ } from './__tests__/test-helpers.js';
112
112
 
113
113
  // Create a transaction with defaults
114
- const transaction = createMockTransaction()
114
+ const transaction = createMockTransaction();
115
115
 
116
116
  // Create with overrides
117
117
  const customTransaction = createMockTransaction({
118
118
  amount: '250.00',
119
- currency: 'USD'
120
- })
119
+ currency: 'USD',
120
+ });
121
121
  ```
122
122
 
123
123
  ### Helper Functions
@@ -128,19 +128,19 @@ import {
128
128
  roundConfidence,
129
129
  isValidConfidenceScore,
130
130
  daysDifference,
131
- isWithinDays
132
- } from './__tests__/test-helpers.js'
131
+ isWithinDays,
132
+ } from './__tests__/test-helpers.js';
133
133
 
134
134
  // Calculate weighted confidence
135
- const scores = { amount: 0.9, currency: 1.0, business: 0.5, date: 0.8 }
136
- const confidence = calculateExpectedConfidence(scores) // 0.79
135
+ const scores = { amount: 0.9, currency: 1.0, business: 0.5, date: 0.8 };
136
+ const confidence = calculateExpectedConfidence(scores); // 0.79
137
137
 
138
138
  // Round to 2 decimal places
139
- const rounded = roundConfidence(0.956789) // 0.96
139
+ const rounded = roundConfidence(0.956789); // 0.96
140
140
 
141
141
  // Validate score range
142
- isValidConfidenceScore(0.95) // true
143
- isValidConfidenceScore(1.5) // false
142
+ isValidConfidenceScore(0.95); // true
143
+ isValidConfidenceScore(1.5); // false
144
144
  ```
145
145
 
146
146
  ## Key Concepts