@tomei/finance 0.6.98 → 0.6.100

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 (110) hide show
  1. package/.commitlintrc.json +22 -22
  2. package/.eslintrc.js +72 -72
  3. package/.gitlab-ci.yml +16 -16
  4. package/.husky/commit-msg +4 -4
  5. package/.husky/pre-commit +4 -4
  6. package/.prettierrc +4 -4
  7. package/CONTRIBUTING.md +30 -30
  8. package/LICENSE +21 -21
  9. package/README.md +13 -13
  10. package/configs/config.js +348 -348
  11. package/dist/document/document.d.ts +3 -1
  12. package/dist/document/document.js +31 -3
  13. package/dist/document/document.js.map +1 -1
  14. package/dist/finance-company/finance-company.js +8 -2
  15. package/dist/finance-company/finance-company.js.map +1 -1
  16. package/dist/interfaces/account-system.interface.d.ts +4 -1
  17. package/dist/payment-item/payment-item.js +4 -4
  18. package/dist/tsconfig.tsbuildinfo +1 -1
  19. package/invoice-template/assets/css/style.css.map +12 -12
  20. package/invoice-template/assets/css/style.min.css +1 -1
  21. package/invoice-template/assets/img/arrow_bg.svg +11 -11
  22. package/invoice-template/assets/img/coffy_shop_img.svg +18 -18
  23. package/invoice-template/assets/img/logo_accent.svg +3 -3
  24. package/invoice-template/assets/img/logo_white.svg +4 -4
  25. package/invoice-template/assets/img/sign.svg +12 -12
  26. package/invoice-template/assets/img/tomei-logo.svg +9 -9
  27. package/invoice-template/assets/js/html2canvas.min.js +10379 -10379
  28. package/invoice-template/assets/js/jquery.min.js +1 -1
  29. package/invoice-template/assets/sass/common/_color_variable.scss +12 -12
  30. package/invoice-template/assets/sass/common/_typography.scss +178 -178
  31. package/invoice-template/assets/sass/style.scss +12 -12
  32. package/migrations/finance-account-migration.js +97 -97
  33. package/migrations/finance-company-migration.js +29 -29
  34. package/migrations/finance-customer-migration.js +51 -51
  35. package/migrations/finance-document-item-migration.js +111 -111
  36. package/migrations/finance-document-migration.js +122 -122
  37. package/migrations/finance-journal-entry-migration.js +59 -59
  38. package/migrations/finance-ledger-transaction-migration.js +89 -89
  39. package/migrations/finance-payment-item-migration.js +52 -52
  40. package/migrations/finance-payment-method-migration.js +31 -31
  41. package/migrations/finance-payment-method-type-migration.js +55 -55
  42. package/migrations/finance-payment-migration.js +96 -96
  43. package/migrations/finance-post-history-migration.js +45 -45
  44. package/migrations/finance-tax-migration.js +52 -52
  45. package/migrations/refactor-finance-document-migration.js +71 -71
  46. package/migrations/update-document-item.migration.js +87 -87
  47. package/nest-cli.json +19 -19
  48. package/package.json +82 -82
  49. package/sonar-project.properties +12 -12
  50. package/src/account/account.repository.ts +11 -11
  51. package/src/account/account.ts +305 -305
  52. package/src/account/interfaces/account-attr.interface.ts +31 -31
  53. package/src/account-system-entity/account-system-entity.ts +66 -66
  54. package/src/account-system-entity/post-history.repository.ts +11 -11
  55. package/src/config.ts +382 -382
  56. package/src/customer/customer.ts +310 -310
  57. package/src/customer/finance-customer.repository.ts +13 -13
  58. package/src/customer/interfaces/customer.repository.interface.ts +3 -3
  59. package/src/customer/interfaces/finance-customer-attr.interface.ts +10 -10
  60. package/src/customer/interfaces/finance-customer.repository.interface.ts +4 -4
  61. package/src/database.ts +48 -48
  62. package/src/document/document-item.repository.ts +11 -11
  63. package/src/document/document.repository.ts +11 -11
  64. package/src/document/document.ts +64 -12
  65. package/src/document/interfaces/document-attr.interface.ts +28 -28
  66. package/src/document/interfaces/document-item-attr.interface.ts +26 -26
  67. package/src/document/interfaces/document-item.repository.interface.ts +4 -4
  68. package/src/enum/collect-payment-type.ts +3 -3
  69. package/src/enum/doc-type.enum.ts +8 -8
  70. package/src/enum/index.ts +19 -19
  71. package/src/enum/payment-method.enum.ts +3 -3
  72. package/src/enum/payment-type.enum.ts +4 -4
  73. package/src/enum/quick-book-client-scopes.enum.ts +14 -14
  74. package/src/finance-company/finance-company.repository.ts +11 -11
  75. package/src/finance-company/finance-company.ts +2436 -2424
  76. package/src/helpers/login-user.ts +38 -38
  77. package/src/helpers/typeof.ts +35 -35
  78. package/src/index.ts +32 -32
  79. package/src/interfaces/account-system.interface.ts +51 -36
  80. package/src/interfaces/index.ts +3 -3
  81. package/src/models/account.entity.ts +206 -206
  82. package/src/models/customer.entity.ts +93 -93
  83. package/src/models/document-item.entity.ts +167 -167
  84. package/src/models/document.entity.ts +211 -211
  85. package/src/models/finance-company.entity.ts +69 -69
  86. package/src/models/journal-entry.entity.ts +110 -110
  87. package/src/models/ledger-transaction.entity.ts +148 -148
  88. package/src/models/payment-item.entity.ts +60 -60
  89. package/src/models/payment-method-type.entity.ts +70 -70
  90. package/src/models/payment-method.entity.ts +51 -51
  91. package/src/models/payment.entity.ts +165 -165
  92. package/src/models/post-history.entity.ts +41 -41
  93. package/src/models/tax.entity.ts +75 -75
  94. package/src/payment/interfaces/payment-attr.interface.ts +23 -23
  95. package/src/payment/interfaces/payment-params.interface.ts +8 -8
  96. package/src/payment/payment.repository.ts +11 -11
  97. package/src/payment/payment.ts +236 -236
  98. package/src/payment-item/interfaces/payment-item-attr.interface.ts +10 -10
  99. package/src/payment-item/payment-item.repository.ts +11 -11
  100. package/src/payment-item/payment-item.ts +134 -134
  101. package/src/payment-method/payment-method.repository.ts +11 -11
  102. package/src/payment-method-type/payment-method-type.repository.ts +11 -11
  103. package/src/tax/interfaces/tax-attr.interface.ts +10 -10
  104. package/src/tax/tax.repository.ts +11 -11
  105. package/src/tax/tax.ts +69 -69
  106. package/src/test-document.ts +25 -25
  107. package/src/test.ts +3 -3
  108. package/tsconfig.build.json +4 -4
  109. package/tsconfig.json +22 -22
  110. package/tslint.json +18 -18
@@ -1,2424 +1,2436 @@
1
- import axios from 'axios';
2
- import {
3
- ClassError,
4
- HashTable,
5
- LoginUserBase,
6
- ObjectBase,
7
- RecordNotFoundError,
8
- } from '@tomei/general';
9
- import Account from '../account/account';
10
- import JournalEntry from '../journal-entry/journal-entry';
11
- import FinanceCustomerBase from '../customer/customer';
12
- import Document from '../document/document';
13
- import { IAccountSystem } from '../interfaces';
14
- import { FinanceCompanyRepository } from './finance-company.repository';
15
- import { FinanceCustomerRepository } from '../customer/finance-customer.repository';
16
- import { LedgerTransactionRepository } from '../ledger-transaction/ledger-transaction.repository';
17
- import {
18
- DocType,
19
- DocumentStatus,
20
- PaymentStatus,
21
- PaymentType,
22
- TransactionTypeOptions,
23
- } from '../enum';
24
- import PaymentMethodType from '../payment-method-type/payment-method-type';
25
- import { PaymentRepository } from '../payment/payment.repository';
26
- import { PaymentItemRepository } from '../payment-item/payment-item.repository';
27
- import Payment from '../payment/payment';
28
- import { DocumentRepository } from '../document/document.repository';
29
- import { DocumentItemRepository } from '../document/document-item.repository';
30
- import { PaymentMethodRepository } from '../payment-method/payment-method.repository';
31
- import { PaymentMethodTypeRepository } from '../payment-method-type/payment-method-type.repository';
32
- import PaymentMethod from '../payment-method/payment-method';
33
- import { AccountRepository } from '../account/account.repository';
34
- import { PaymentPaidWithRepository } from '../payment-paid-with/payment-paid-with.repository';
35
- import { MediasModel } from '@tomei/media';
36
- import DocumentItem from '../document/document-item';
37
- import { type } from '../helpers/typeof';
38
- import { LoginUser } from '@tomei/sso';
39
- import { ActionEnum, Activity } from '@tomei/activity-history';
40
- import { ApplicationConfig, ComponentConfig } from '@tomei/config';
41
- import { TaxRepository } from '../tax/tax.repository';
42
- import { Tax } from '../tax/tax';
43
- import { CollectPaymentType } from '../enum/collect-payment-type';
44
- import FinanceCustomerModel from 'src/models/customer.entity';
45
-
46
- export default class FinanceCompany extends ObjectBase {
47
- private _CompanyId = 'New';
48
- private _CompSystemCode = '';
49
- private _CompSystemRefId = '';
50
- private _AccSystemCode = '';
51
- private _AccSystemRefId = 'REF';
52
- private _PostedToAccSystemYN = 'N';
53
- private _PostedById = null;
54
- private _PostedDateTime = null;
55
- private _ObjectType = 'FinanceCompany';
56
-
57
- private static _htFinanceCompanyIds = new HashTable();
58
- private static _htFinanceCompanies = new HashTable();
59
- private static _financeCompanyRepository = new FinanceCompanyRepository();
60
- private static _PaymentRepository = new PaymentRepository();
61
- private static _PaymentItemRepository = new PaymentItemRepository();
62
- private static _PaymentPaidWithRepository = new PaymentPaidWithRepository();
63
- private static _PaymentMethodRepository = new PaymentMethodRepository();
64
- private static _PaymentMethodTypeRepository =
65
- new PaymentMethodTypeRepository();
66
- private static _DocumentRepository = new DocumentRepository();
67
- private static _DocumentItemRepository = new DocumentItemRepository();
68
- private static _FinanceCustomerRepository = new FinanceCustomerRepository();
69
- private static _LedgerTransactionRepository =
70
- new LedgerTransactionRepository();
71
-
72
- private static _AccountRepository = new AccountRepository();
73
- private static _TaxRepository = new TaxRepository();
74
-
75
- private _AccountingSystem: IAccountSystem;
76
-
77
- private _DbTransaction: any;
78
- private _PaymentMethods = [];
79
- private _Taxes: Tax[] = [];
80
-
81
- get ObjectType() {
82
- return this._ObjectType;
83
- }
84
-
85
- get CompSystemCode(): string {
86
- return this._CompSystemCode;
87
- }
88
-
89
- private set CompSystemCode(code: string) {
90
- this._CompSystemCode = code;
91
- }
92
-
93
- get CompSystemRefId() {
94
- return this._CompSystemRefId;
95
- }
96
-
97
- set CompSystemRefId(id: string) {
98
- this._CompSystemRefId = id;
99
- }
100
-
101
- get AccSystemCode() {
102
- return this._AccSystemCode;
103
- }
104
-
105
- get AccSystemRefId() {
106
- return this._AccSystemRefId;
107
- }
108
-
109
- set AccSystemRefId(id: string) {
110
- this._AccSystemRefId = id;
111
- }
112
-
113
- get PostedToAccSystemYN() {
114
- return this._PostedToAccSystemYN;
115
- }
116
-
117
- set PostedToAccSystemYN(value: string) {
118
- this._PostedToAccSystemYN = value;
119
- }
120
-
121
- get PostedById() {
122
- return this._PostedById;
123
- }
124
-
125
- set PostedById(id: string) {
126
- this._PostedById = id;
127
- }
128
-
129
- get PostedDateTime() {
130
- return this._PostedDateTime;
131
- }
132
-
133
- set PostedDateTime(date: any) {
134
- this._PostedDateTime = date;
135
- }
136
-
137
- private set AccSystemCode(code: string) {
138
- this._AccSystemCode = code;
139
- }
140
-
141
- get CompanyId() {
142
- return this._CompanyId;
143
- }
144
-
145
- get ObjectId() {
146
- return this._CompanyId;
147
- }
148
-
149
- private set ObjectId(id: string) {
150
- this._CompanyId = id;
151
- }
152
-
153
- get ObjectName() {
154
- return `${this.CompSystemCode}-${this.CompSystemRefId}-${this.AccSystemCode}`;
155
- }
156
-
157
- get TableName() {
158
- return 'finance_Company';
159
- }
160
-
161
- get AccountingSystem(): IAccountSystem {
162
- return this._AccountingSystem;
163
- }
164
-
165
- set AccountingSystem(system: IAccountSystem) {
166
- this._AccountingSystem = system;
167
- }
168
-
169
- constructor(
170
- compSystemCode: string,
171
- compSystemRefId: string,
172
- accSystemCode: string,
173
- ) {
174
- super();
175
- this.CompSystemCode = compSystemCode;
176
- this.CompSystemRefId = compSystemRefId;
177
- this.AccSystemCode = accSystemCode;
178
-
179
- this.AccSystemRefId = 'REF';
180
- this.PostedToAccSystemYN = 'N';
181
- }
182
-
183
- static async getFinanceCompanyId(
184
- compSystemCode: string,
185
- compSystemRefId: string,
186
- accSystemCode: string,
187
- ): Promise<string> {
188
- let sCompanyId = '';
189
- /*Assemble the hashtable key*/
190
- const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
191
- /*Check if the FinanceCompany has previously being loaded*/
192
- if (!FinanceCompany._htFinanceCompanyIds.get(sKey)) {
193
- /*Instantiate a new FinanceCompany*/
194
- const financeCompany = new FinanceCompany(
195
- compSystemCode,
196
- compSystemRefId,
197
- accSystemCode,
198
- );
199
-
200
- /*Retrieve the finance company from finance_Company table using compSystemCode,
201
- * CompSystemRefId and accSystemCode */
202
- const company = await FinanceCompany._financeCompanyRepository.findOne({
203
- where: {
204
- CompSystemCode: compSystemCode,
205
- CompSystemRefId: compSystemRefId,
206
- AccSystemCode: accSystemCode,
207
- },
208
- });
209
-
210
- /*Retrieve and store the companyId from the result*/
211
- financeCompany.ObjectId = company.CompanyId;
212
- sCompanyId = financeCompany.ObjectId;
213
-
214
- /*Add the details into the hashtable*/
215
- FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
216
- FinanceCompany._htFinanceCompanies.add(sCompanyId, financeCompany);
217
- }
218
-
219
- if (typeof FinanceCompany._htFinanceCompanyIds.get(sKey) === 'string') {
220
- sCompanyId = FinanceCompany._htFinanceCompanyIds.get(sKey);
221
- }
222
-
223
- return sCompanyId;
224
- }
225
-
226
- static async getFinanceCompany(companyId: string): Promise<FinanceCompany> {
227
- /*Check if the finance company is previously be loaded*/
228
- if (!FinanceCompany._htFinanceCompanies.get(companyId)) {
229
- /*Retrieve the finance company from finance_Company table using compSystemCode,
230
- * CompSystemRefId and accSystemCode */
231
- const company = await FinanceCompany._financeCompanyRepository.findOne({
232
- where: { CompanyId: companyId },
233
- });
234
-
235
- if (!company) {
236
- throw Error('No finance company found. Please create first.');
237
- }
238
-
239
- /*Using the result returned, instantiate a new FinanceCompany*/
240
- const compSystemCode = company.CompSystemCode;
241
- const compSystemRefId = company.CompSystemRefId;
242
- const accSystemCode = company.AccSystemCode;
243
-
244
- const financeCompany = new FinanceCompany(
245
- compSystemCode,
246
- compSystemRefId,
247
- accSystemCode,
248
- );
249
-
250
- /*Retrieve and store the CompanyId from the result*/
251
- financeCompany.ObjectId = company.CompanyId;
252
- financeCompany._CompanyId = company.CompanyId;
253
-
254
- /*Add the details into the hashtable*/
255
- const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
256
- FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
257
- FinanceCompany._htFinanceCompanies.add(
258
- financeCompany.ObjectId,
259
- financeCompany,
260
- );
261
- }
262
- // tslint:disable-next-line:no-console
263
- console.log('return from hash table');
264
- return FinanceCompany._htFinanceCompanies.get(companyId);
265
- }
266
-
267
- static async createFinanceCompany(
268
- dbTransaction: any,
269
- loginUser: LoginUserBase,
270
- companyId: string,
271
- compSystemCode: string,
272
- compSystemRefId: string,
273
- accSystemCode: string,
274
- ) {
275
- /*Instantiate a new FinanceCompany*/
276
- const financeCompany = new FinanceCompany(
277
- compSystemCode,
278
- compSystemRefId,
279
- accSystemCode,
280
- );
281
-
282
- /*Validating if the finance company already exists in finance_Company*/
283
- const company = await FinanceCompany._financeCompanyRepository.findOne({
284
- where: {
285
- CompSystemCode: compSystemCode,
286
- CompSystemRefId: compSystemRefId,
287
- AccSystemCode: accSystemCode,
288
- },
289
- });
290
-
291
- if (company) {
292
- throw Error(
293
- 'There is already another Finance Company with the compSystemCode, CompSystemRefId and accSystemCode specified.',
294
- );
295
- }
296
-
297
- /*Generating the companyId*/
298
- financeCompany.ObjectId = companyId;
299
- const accSystemRefId = ComponentConfig.getComponentConfigValue(
300
- 'finance-config.json',
301
- 'accountSystemRefId',
302
- );
303
-
304
- const postedToAccSystemYN = ComponentConfig.getComponentConfigValue(
305
- 'finance-config.json',
306
- 'postedToAccSystemYN',
307
- );
308
-
309
- financeCompany.PostedById = loginUser.ObjectId || null;
310
- financeCompany.PostedDateTime = new Date();
311
-
312
- /*Save the FinanceCompany to the finance_Company table*/
313
- await FinanceCompany._financeCompanyRepository.create(
314
- {
315
- CompanyId: financeCompany.CompanyId,
316
- CompSystemCode: financeCompany.CompSystemCode,
317
- CompSystemRefId: financeCompany.CompSystemRefId,
318
- AccSystemCode: financeCompany.AccSystemCode,
319
- AccSystemRefId:
320
- postedToAccSystemYN === 'Y'
321
- ? accSystemRefId
322
- : financeCompany.AccSystemRefId,
323
- PostedToAccSystemYN:
324
- postedToAccSystemYN || financeCompany.PostedToAccSystemYN,
325
- PostedById: financeCompany.PostedById,
326
- PostedDateTime: financeCompany.PostedDateTime,
327
- },
328
- {
329
- transaction: dbTransaction,
330
- },
331
- );
332
-
333
- /*Add the details to hashtable*/
334
- const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
335
- FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
336
- FinanceCompany._htFinanceCompanies.add(
337
- financeCompany.ObjectId,
338
- financeCompany,
339
- );
340
-
341
- // tslint:disable-next-line:no-console
342
- console.log('return from hash table');
343
- return FinanceCompany._htFinanceCompanies.get(companyId);
344
- }
345
-
346
- static async findAccount(
347
- loginUser: LoginUserBase,
348
- dbTransaction: any,
349
- accountNo: string,
350
- ): Promise<Account> {
351
- try {
352
- //Part 1: Privilege Check
353
- //Check if the user has the privilege to view the account
354
- const systemCode =
355
- ApplicationConfig.getComponentConfigValue('system-code');
356
- const isPrivileged = await loginUser.checkPrivileges(
357
- systemCode,
358
- 'FINANCECOMPANY_VIEW_ACCOUNT',
359
- );
360
-
361
- //If the user is not privileged, throw an error
362
- if (!isPrivileged) {
363
- throw new ClassError(
364
- 'FinanceCompany',
365
- 'findAccountErrMsg0X',
366
- 'User not privileged to view finance company account',
367
- );
368
- }
369
-
370
- //Part 2: Find Account
371
- //Find the account using the accountNo
372
- const record = await FinanceCompany._AccountRepository.findOne({
373
- where: {
374
- AccountNo: accountNo,
375
- },
376
- transaction: dbTransaction,
377
- });
378
-
379
- if (record) {
380
- const account = new Account(dbTransaction);
381
- account.init(record.get({ plain: true }));
382
- return account;
383
- }
384
- return undefined;
385
- } catch (error) {
386
- throw error;
387
- }
388
- }
389
-
390
- async createCustomer<T extends FinanceCustomerBase>(
391
- dbTransaction: any,
392
- custSystemCode: string,
393
- custSystemRefId: string,
394
- customer: T,
395
- loginUser: LoginUser,
396
- ) {
397
- try {
398
- if (!custSystemCode || !custSystemRefId) {
399
- throw new Error(
400
- 'CustSystemCode and CustomerRefId are required fields.',
401
- );
402
- }
403
-
404
- const financeCustomerData =
405
- await FinanceCompany._FinanceCustomerRepository.findOne({
406
- where: {
407
- CompanyId: this._CompanyId,
408
- CustSystemCode: custSystemCode,
409
- CustSystemRefId: custSystemRefId,
410
- },
411
- });
412
-
413
- if (financeCustomerData) {
414
- throw new Error('Customer already created previously.');
415
- }
416
-
417
- let AccCustomerRefId = 'REF';
418
- if (this.AccountingSystem) {
419
- AccCustomerRefId = await this.AccountingSystem.createCustomer(
420
- customer,
421
- dbTransaction,
422
- );
423
- }
424
-
425
- customer.CompanyId = this._CompanyId;
426
-
427
- const newCustomer = await customer.save(
428
- AccCustomerRefId,
429
- custSystemCode,
430
- custSystemRefId,
431
- dbTransaction,
432
- );
433
-
434
- const activity = new Activity();
435
- activity.ActivityId = this._createId().toUpperCase();
436
- activity.Action = ActionEnum.ADD;
437
- activity.Description = 'Add Finance Customer';
438
- activity.EntityType = 'FinanceCustomer';
439
- activity.EntityId = newCustomer.CustomerId;
440
- activity.EntityValueBefore = JSON.stringify({});
441
- activity.EntityValueAfter = JSON.stringify(newCustomer);
442
-
443
- await activity.create(loginUser.ObjectId, dbTransaction);
444
-
445
- return customer;
446
- } catch (error) {
447
- throw error;
448
- }
449
- }
450
-
451
- async postJournal(
452
- dbTransaction: any,
453
- journalEntry: JournalEntry,
454
- loginUser: LoginUserBase,
455
- ) {
456
- const debitTransactions = await journalEntry.DebitTransactions;
457
- const creditTransactions = await journalEntry.CreditTransactions;
458
- try {
459
- if (creditTransactions.length < 1 || debitTransactions?.length < 1) {
460
- throw new Error(
461
- 'There should be at least 1 debit ledger transaction and 1 credit ledger transaction in the journal entry',
462
- );
463
- }
464
-
465
- let totalCreditAmount = creditTransactions.reduce(
466
- (accumulator, currentValue) => accumulator + currentValue.CreditAmount,
467
- 0,
468
- );
469
- let totalDebitAmount = debitTransactions.reduce(
470
- (accumulator, currentValue) => accumulator + currentValue.DebitAmount,
471
- 0,
472
- );
473
- console.log('totalCreditAmount: ', totalCreditAmount);
474
- console.log('totalDebitAmount: ', totalDebitAmount);
475
-
476
- if (typeof totalCreditAmount === 'string') {
477
- totalCreditAmount = parseFloat(totalCreditAmount);
478
- }
479
-
480
- if (typeof totalDebitAmount === 'string') {
481
- totalDebitAmount = parseFloat(totalDebitAmount);
482
- }
483
- if (totalCreditAmount.toFixed(2) !== totalDebitAmount.toFixed(2)) {
484
- throw new Error(
485
- 'Credit ledger transaction and debit ledger transaction should the same amount',
486
- );
487
- }
488
-
489
- const newJournalEntry = await journalEntry.save(
490
- loginUser.ObjectId,
491
- dbTransaction,
492
- );
493
-
494
- for (const ledgerTransaction of debitTransactions) {
495
- ledgerTransaction.JournalEntryId = newJournalEntry.JournalEntryId;
496
-
497
- await ledgerTransaction.save(dbTransaction);
498
- // const dt = await ledgerTransaction.newLedgerTransaction(
499
- // TransactionTypeOptions.DEBIT,
500
- // newJournalEntry.JournalEntryId,
501
- // );
502
- // await dt.save();
503
- }
504
-
505
- for (const ledgerTransaction of creditTransactions) {
506
- ledgerTransaction.JournalEntryId = newJournalEntry.JournalEntryId;
507
-
508
- await ledgerTransaction.save(dbTransaction);
509
- // const ct = await ledgerTransaction.newLedgerTransaction(
510
- // TransactionTypeOptions.CREDIT,
511
- // newJournalEntry.JournalEntryId,
512
- // );
513
- // await ct.save();
514
- }
515
-
516
- // await this.AccountingSystem.postJournalEntry(newJournalEntry);
517
-
518
- const payload = {
519
- Action: 'Create',
520
- Activity: 'Post Journal Entry',
521
- Description: `Journal Entry (ID: ${newJournalEntry.JournalEntryId}) has been created`,
522
- EntityType: 'JournalEntry',
523
- EntityValueBefore: JSON.stringify({}),
524
- EntityValueAfter: JSON.stringify(newJournalEntry),
525
- PerformedById: loginUser.ObjectId,
526
- PerformedAt: new Date(),
527
- EntityId: newJournalEntry.JournalEntryId,
528
- };
529
-
530
- await axios.post(
531
- `${process.env.COMMON_API_URL}/activity-histories`,
532
- payload,
533
- );
534
- } catch (error) {
535
- throw error;
536
- }
537
- }
538
-
539
- async createAccount(
540
- dbTransaction: any,
541
- account: Account,
542
- loginUser: LoginUserBase,
543
- ) {
544
- try {
545
- let accSystemRefId: string;
546
-
547
- if (!account.AccountType) {
548
- throw new Error('AccountType is required.');
549
- }
550
-
551
- // Retrieve & validate accountNoLength from finance config file
552
- const accountNoLength = ComponentConfig.getComponentConfigValue(
553
- '@tomei/finance',
554
- 'accountNoLength',
555
- );
556
-
557
- if (accountNoLength && account.AccountNo.length > accountNoLength) {
558
- throw new Error(
559
- `Account No length should not exceed ${accountNoLength} characters.`,
560
- );
561
- }
562
-
563
- let createAccountPayload: any = {
564
- Name: account.Name,
565
- AcctNum: account.AccountNo,
566
- AccountType: account.AccountType,
567
- AccountSubType: account.AccountSubtype,
568
- };
569
-
570
- if (this.AccountingSystem) {
571
- accSystemRefId = await this.AccountingSystem.createAccount(
572
- account,
573
- dbTransaction,
574
- );
575
- }
576
-
577
- if (account.isParentAccountExists()) {
578
- createAccountPayload = {
579
- ...createAccountPayload,
580
- CurrencyRef: 'MYR',
581
- ParentRef: account.ParentAccountNo,
582
- SubAccount: true,
583
- };
584
- }
585
-
586
- console.log(
587
- 'Finance Company Create Account: Before accSystemAccountId Create',
588
- );
589
-
590
- console.log(
591
- 'Finance Company Create Account: After accSystemAccountId Create',
592
- );
593
-
594
- console.log('Finance Company Create Account: Before new Account Create');
595
-
596
- if (accSystemRefId) {
597
- account.PostedToAccSystemYN = 'Y';
598
- account.PostedById = loginUser.ObjectId;
599
- account.PostedDateTime = new Date();
600
- }
601
-
602
- const newAccount = await account.save(
603
- this.CompanyId,
604
- accSystemRefId || 'REF',
605
- loginUser.ObjectId,
606
- dbTransaction,
607
- );
608
-
609
- console.log('Finance Company Create Account: After new Account Create');
610
-
611
- const payload = {
612
- Action: 'Create',
613
- Activity: 'Account Created',
614
- Description: `Account (ID: ${newAccount.AccountNo}) has been created`,
615
- EntityType: 'Account',
616
- EntityValueBefore: JSON.stringify({}),
617
- EntityValueAfter: JSON.stringify(newAccount),
618
- PerformedById: loginUser.ObjectId,
619
- PerformedAt: new Date(),
620
- EntityId: newAccount.AccountNo,
621
- };
622
-
623
- await axios.post(
624
- `${process.env.COMMON_API_URL}/activity-histories`,
625
- payload,
626
- );
627
-
628
- return account;
629
- } catch (error) {
630
- throw error;
631
- }
632
- }
633
-
634
- /**
635
- * Issue an invoice
636
- *
637
- * @param dbTransaction - The database transaction to be used
638
- * @param loginUser - The user issuing the invoice
639
- * @param invoice - The document containing the invoice details
640
- * @param customer - The customer to be issued the invoice
641
- * @param dtAccountNo - The account number of the Account Receivable (AR) account to debit. If not provided, will debit the customer's default AR account
642
- *
643
- * @returns {Document} - Document object representing the full details of the invoice issued
644
- */
645
- async issueInvoice<C extends FinanceCustomerBase>(
646
- /*todo: loginUser & customer is NOT supposed to be optional */
647
- dbTransaction: any,
648
- invoice: Document,
649
- loginUser?: LoginUserBase,
650
- customer?: C,
651
- dtAccountNo?: string,
652
- ): Promise<Document> {
653
- try {
654
- /*Check if the invoice number already exists (should be unique)*/
655
- const duplicateInvoice = await FinanceCompany._DocumentRepository.findOne(
656
- {
657
- where: {
658
- DocNo: invoice.DocNo,
659
- },
660
- transaction: dbTransaction,
661
- },
662
- );
663
-
664
- if (duplicateInvoice) {
665
- throw new Error('Invoice number already exists');
666
- }
667
-
668
- const documentItems = await invoice.getDocumentItems(dbTransaction);
669
-
670
- /*Check if the document has at least 1 document item*/
671
- if (!documentItems.length) {
672
- throw new Error('Document must have at least 1 document item');
673
- }
674
-
675
- /*Check if each document item has CtAccountNo provided*/
676
- for (const invoiceItem of documentItems) {
677
- if (!invoiceItem.CtAccountNo) {
678
- throw new Error(
679
- 'Each document item should have CtAccountNo provided',
680
- );
681
- }
682
- }
683
-
684
- /*Set up the document type*/
685
- invoice.DocType = DocType.INVOICE;
686
-
687
- /*Generating the invoice*/
688
- let invoiceMedia: {
689
- HTMLMedia: MediasModel;
690
- PDFMedia: MediasModel;
691
- };
692
-
693
- if (invoice.UseAccSystemDocYN === 'Y') {
694
- /*todo: Posting to accounting system to generate invoice*/
695
- const accInvoiceRefIdDetail = await this.AccountingSystem.createInvoice({
696
- customer,
697
- invoice,
698
- paymentMode: 'credit',
699
- }, dbTransaction);
700
- invoice.AccSystemRefId = accInvoiceRefIdDetail.invoiceRefId;
701
- invoice.PostedToAccSystemYN = 'Y';
702
- invoice.PostedById = loginUser.ObjectId;
703
- invoice.PostedDateTime = new Date();
704
-
705
- //for each document item, insert the document item accounting reference id that have matching name
706
- for (const documentItem of documentItems) {
707
- const accItem = accInvoiceRefIdDetail.invoiceItems.find(
708
- (item) => item.name === documentItem.Name
709
- );
710
- if (accItem) {
711
- documentItem.AccSystemRefId = accItem.itemRefId;
712
- documentItem.PostedToAccSystemYN = 'Y';
713
- documentItem.PostedById = loginUser.ObjectId;
714
- documentItem.PostedDateTime = new Date();
715
- }
716
- }
717
- } else {
718
- /*todo: check config file to see which invoice template is to be used for specific project*/
719
-
720
- /*Generating invoice based on template*/
721
- invoiceMedia = await invoice.generateInvoice(
722
- invoice.IssuedById,
723
- customer,
724
- dbTransaction,
725
- );
726
- }
727
-
728
- /*Saving the document and document items to the database*/
729
- await FinanceCompany._DocumentRepository.create(
730
- {
731
- DocNo: invoice.DocNo,
732
- DocType: invoice.DocType,
733
- DocDate: invoice.DocDate,
734
- CompanyId: invoice.CompanyId,
735
- Currency: invoice.Currency,
736
- Amount: invoice.Amount,
737
- Description: invoice.Description,
738
- Status: invoice.Status,
739
- IssuedById: invoice.IssuedById,
740
- IssuedToId: invoice.IssuedToId,
741
- IssuedToType: invoice.IssuedToType,
742
- RelatedObjectId: invoice.RelatedObjectId,
743
- RelatedObjectType: invoice.RelatedObjectType,
744
- CreatedById: invoice.CreatedById,
745
- CreatedAt: new Date(),
746
- UpdatedById: invoice.UpdatedById,
747
- UpdatedAt: new Date(),
748
- DocPDFFileMediaId: invoice.UseAccSystemDocYN == 'N' ? invoiceMedia.PDFMedia.MediaId : null,
749
- DocHTMLFileMediaId: invoice.UseAccSystemDocYN == 'N' ? invoiceMedia.HTMLMedia.MediaId : null,
750
- AccSystemRefId: invoice.AccSystemRefId,
751
- PostedToAccSystemYN: invoice.PostedToAccSystemYN,
752
- PostedById:
753
- invoice.UseAccSystemDocYN == 'Y' ? invoice.PostedById : null,
754
- PostedDateTime: new Date(),
755
- UseAccSystemDocYN: invoice.UseAccSystemDocYN,
756
- },
757
- {
758
- transaction: dbTransaction,
759
- },
760
- );
761
-
762
- for (const documentItem of documentItems) {
763
- await FinanceCompany._DocumentItemRepository.create(
764
- {
765
- DocumentItemId: this._createId().toUpperCase(),
766
- DocNo: invoice.DocNo,
767
- Name: documentItem.Name,
768
- NameBM: documentItem.NameBM,
769
- Description: documentItem.Description,
770
- ItemId: documentItem.ItemId,
771
- ItemType: documentItem.ItemType,
772
- ItemSKU: documentItem.ItemSKU,
773
- ItemSerialNo: documentItem.ItemSerialNo,
774
- Currency: documentItem.Currency,
775
- UnitPrice: documentItem.UnitPrice,
776
- Quantity: documentItem.Quantity,
777
- QuantityUOM: documentItem.QuantityUOM,
778
- Amount: documentItem.Amount,
779
- TaxCode: documentItem.TaxCode,
780
- TaxAmount: documentItem.TaxAmount,
781
- TaxRate: documentItem.TaxRate,
782
- TaxInclusiveYN: documentItem.TaxInclusiveYN,
783
- DtAccountNo: documentItem.DtAccountNo
784
- ? documentItem.DtAccountNo
785
- : null,
786
- CtAccountNo: documentItem.CtAccountNo
787
- ? documentItem.CtAccountNo
788
- : null,
789
- AccSystemRefId: documentItem.AccSystemRefId,
790
- PostedToAccSystemYN: documentItem.PostedToAccSystemYN,
791
- PostedById: documentItem.PostedById,
792
- PostedDateTime: documentItem.PostedDateTime,
793
- },
794
- {
795
- transaction: dbTransaction,
796
- },
797
- );
798
- }
799
-
800
- const transactionDate = new Date();
801
- const htCreditAccountAmount = new HashTable();
802
- const htCreditAccountCurrency = new HashTable();
803
- const htCreditAccountPurpose = new HashTable();
804
-
805
- documentItems.forEach((invoiceItem) => {
806
- if (!htCreditAccountAmount.exists(invoiceItem.CtAccountNo)) {
807
- //add the credit account to the hash table
808
- htCreditAccountAmount.add(
809
- invoiceItem.CtAccountNo,
810
- invoiceItem.Amount,
811
- );
812
-
813
- htCreditAccountCurrency.add(
814
- invoiceItem.CtAccountNo,
815
- invoiceItem.Currency,
816
- );
817
-
818
- htCreditAccountPurpose.add(invoiceItem.CtAccountNo, invoiceItem.Name);
819
- } else {
820
- //update the credit account amount
821
- const d = htCreditAccountAmount.get(invoiceItem.CtAccountNo);
822
- htCreditAccountAmount.add(
823
- invoiceItem.CtAccountNo,
824
- d + invoiceItem.Amount,
825
- );
826
- }
827
- });
828
-
829
- const savedItems = htCreditAccountAmount.list();
830
-
831
- for (const item of savedItems) {
832
- const journalEntry = new JournalEntry(dbTransaction);
833
- //Temporary fix to successfully save journal entry in PostJournal
834
- journalEntry.init({
835
- CompanyId: this.CompanyId,
836
- Name: 'Issue Invoice ' + invoice.DocNo,
837
- });
838
- // const account = await Account.initAccount(dbTransaction, item.value[0]);
839
- const creditAmount = item.value[1];
840
- const currency = htCreditAccountCurrency.get(item.value[0]);
841
- const purpose = htCreditAccountPurpose.get(item.value[0]);
842
-
843
- const dt = await journalEntry.newLedgerTransaction(
844
- TransactionTypeOptions.DEBIT,
845
- );
846
-
847
- if (dtAccountNo) {
848
- /*Transacting using AR account provided*/
849
- dt.AccountNo = dtAccountNo;
850
- } else {
851
- /*Transacting based on default customer AR account*/
852
- const arAccount = await customer.getAccountReceivable();
853
- dt.AccountNo = arAccount.AccountNo;
854
- }
855
-
856
- dt.Currency = currency ? currency : 'MYR';
857
- dt.DebitAmount = creditAmount ? creditAmount : 0.0;
858
- dt.Date = transactionDate;
859
- dt.Description = `${purpose}`;
860
- dt.Name = `${purpose}`;
861
- dt.RelatedDocNo = invoice.DocNo;
862
- dt.RelatedObjectId = invoice.RelatedObjectId;
863
- dt.RelatedObjectType = invoice.RelatedObjectType;
864
-
865
- const ct = await journalEntry.newLedgerTransaction(
866
- TransactionTypeOptions.CREDIT,
867
- );
868
- ct.AccountNo = item.value[0];
869
- ct.Currency = currency ? currency : 'MYR';
870
- ct.CreditAmount = creditAmount ? creditAmount : 0.0;
871
- ct.Date = transactionDate;
872
- ct.Description = customer.FullName;
873
- ct.Name = customer.FullName;
874
- ct.RelatedDocNo = invoice.DocNo;
875
- ct.RelatedObjectId = invoice.RelatedObjectId;
876
- ct.RelatedObjectType = invoice.RelatedObjectType;
877
-
878
- await this.postJournal(dbTransaction, journalEntry, loginUser);
879
- }
880
-
881
- return invoice;
882
- } catch (err) {
883
- // tslint:disable-next-line:no-console
884
- console.log('Issue invoice err: ', err);
885
- throw err;
886
- }
887
- }
888
-
889
- static async findCustomer(
890
- custSystemRefId: string,
891
- dbTransaction?: any,
892
- ): Promise<FinanceCustomerModel> {
893
- const data = await FinanceCompany._FinanceCustomerRepository.findOne({
894
- where: {
895
- CustSystemRefId: custSystemRefId,
896
- },
897
- transaction: dbTransaction,
898
- });
899
-
900
- return data;
901
- }
902
-
903
- /**
904
- * Issue a debit note
905
- *
906
- * @param dbTransaction - The database transaction to be used
907
- * @param loginUser - The user issuing the invoice
908
- * @param invoice - The document containing the invoice details
909
- * @param customer - The customer to be issued the invoice
910
- * @param dtAccountNo - The account number of the Account Receivable (AR) account to debit. If not provided, will debit the customer's default AR account
911
- *
912
- * @returns {Document} - Document object representing the full details of the invoice issued
913
- */
914
- async issueDebitNote(
915
- dbTransaction: any,
916
- loginUser: LoginUserBase,
917
- invoice: Document,
918
- customer: FinanceCustomerBase,
919
- dtAccountNo?: string,
920
- ): Promise<Document> {
921
- try {
922
- /*Check if the invoice number already exists (should be unique)*/
923
- const duplicateInvoice = await FinanceCompany._DocumentRepository.findOne(
924
- {
925
- where: {
926
- DocNo: invoice.DocNo,
927
- },
928
- transaction: dbTransaction,
929
- },
930
- );
931
-
932
- if (duplicateInvoice) {
933
- throw new Error('Invoice number already exists');
934
- }
935
-
936
- const documentItems = await invoice.getDocumentItems(dbTransaction);
937
-
938
- /*Check if the document has at least 1 document item*/
939
- if (!documentItems.length) {
940
- throw new Error('Document must have at least 1 document item');
941
- }
942
-
943
- /*Check if each document item has CtAccountNo provided*/
944
- for (const invoiceItem of documentItems) {
945
- if (!invoiceItem.CtAccountNo) {
946
- throw new Error(
947
- 'Each document item should have CtAccountNo provided',
948
- );
949
- }
950
- }
951
-
952
- /*Set up the document type*/
953
- invoice.DocType = DocType.DEBIT_NOTE;
954
- // invoice.DocNo = this._createId().toUpperCase();
955
-
956
- /*Generating the invoice*/
957
- if (invoice.UseAccSystemDocYN === 'Y') {
958
- /*todo: Posting to accounting system to generate invoice*/
959
- const accInvoiceRefIDetail = await this.AccountingSystem.createInvoice({
960
- customer,
961
- invoice,
962
- paymentMode: 'credit',
963
- },
964
- dbTransaction
965
- );
966
- invoice.AccSystemRefId = accInvoiceRefIDetail.invoiceRefId;
967
- invoice.PostedToAccSystemYN = 'Y';
968
- invoice.PostedById = loginUser.ObjectId;
969
- invoice.PostedDateTime = new Date();
970
-
971
- //for each document item, insert the document item accounting reference id that have matching name
972
- for (const documentItem of documentItems) {
973
- const accItem = accInvoiceRefIDetail.invoiceItems.find(
974
- (item) => item.name === documentItem.Name
975
- );
976
- if (accItem) {
977
- documentItem.AccSystemRefId = accItem.itemRefId;
978
- documentItem.PostedToAccSystemYN = 'Y';
979
- documentItem.PostedById = loginUser.ObjectId;
980
- documentItem.PostedDateTime = new Date();
981
- }
982
- }
983
- } else {
984
- /*todo: check config file to see which invoice template is to be used for specific project*/
985
-
986
- /*Generating invoice based on template*/
987
- invoice.generateInvoice(loginUser.IDNo, customer, dbTransaction);
988
- }
989
-
990
- /*Saving the document and document items to the database*/
991
- await FinanceCompany._DocumentRepository.create(
992
- {
993
- DocNo: invoice.DocNo,
994
- DocType: invoice.DocType,
995
- DocDate: invoice.DocDate,
996
- CompanyId: invoice.CompanyId,
997
- Currency: invoice.Currency,
998
- Amount: invoice.Amount,
999
- Description: invoice.Description,
1000
- Status: invoice.Status,
1001
- IssuedById: loginUser.ObjectId,
1002
- IssuedToId: customer.ObjectId,
1003
- IssuedToType: invoice.IssuedToType,
1004
- RelatedObjectId: invoice.RelatedObjectId,
1005
- RelatedObjectType: invoice.RelatedObjectType,
1006
- CreatedById: loginUser.ObjectId,
1007
- CreatedAt: new Date(),
1008
- UpdatedById: loginUser.ObjectId,
1009
- UpdatedAt: new Date(),
1010
- DocPDFFileMediaId: invoice.DocPDFFileMediaId,
1011
- DocHTMLFileMediaId: invoice.DocHTMLFileMediaId,
1012
- AccSystemRefId: invoice.AccSystemRefId,
1013
- PostedToAccSystemYN: invoice.PostedToAccSystemYN,
1014
- PostedById:
1015
- invoice.PostedToAccSystemYN == 'Y' ? invoice.PostedById : null,
1016
- PostedDateTime: new Date(),
1017
- UseAccSystemDocYN: invoice.UseAccSystemDocYN,
1018
- },
1019
- {
1020
- transaction: dbTransaction,
1021
- },
1022
- );
1023
-
1024
- documentItems.forEach(async (documentItem) => {
1025
- await FinanceCompany._DocumentItemRepository.create(
1026
- {
1027
- DocumentItemId: this._createId().toUpperCase(),
1028
- DocNo: documentItem.DocNo,
1029
- Name: documentItem.Name,
1030
- NameBM: documentItem.NameBM,
1031
- Description: documentItem.Description,
1032
- ItemId: documentItem.ItemId,
1033
- ItemType: documentItem.ItemType,
1034
- ItemSKU: documentItem.ItemSKU,
1035
- ItemSerialNo: documentItem.ItemSerialNo,
1036
- Currency: documentItem.Currency,
1037
- UnitPrice: documentItem.UnitPrice,
1038
- Quantity: documentItem.Quantity,
1039
- QuantityUOM: documentItem.QuantityUOM,
1040
- Amount: documentItem.Amount,
1041
- TaxCode: documentItem.TaxCode,
1042
- TaxAmount: documentItem.TaxAmount,
1043
- TaxRate: documentItem.TaxRate,
1044
- TaxInclusiveYN: documentItem.TaxInclusiveYN,
1045
- DtAccountNo: documentItem.DtAccountNo,
1046
- CtAccountNo: documentItem.CtAccountNo,
1047
- AccSystemRefId: documentItem.AccSystemRefId,
1048
- PostedToAccSystemYN: documentItem.PostedToAccSystemYN,
1049
- PostedById: documentItem.PostedById,
1050
- PostedDateTime: documentItem.PostedDateTime,
1051
- },
1052
- {
1053
- transaction: dbTransaction,
1054
- },
1055
- );
1056
- });
1057
-
1058
- const transactionDate = new Date();
1059
- const htCreditAccountAmount = new HashTable();
1060
- const htCreditAccountCurrency = new HashTable();
1061
- const htCreditAccountPurpose = new HashTable();
1062
-
1063
- documentItems.forEach((invoiceItem) => {
1064
- if (!htCreditAccountAmount.exists(invoiceItem.CtAccountNo)) {
1065
- //add the credit account to the hash table
1066
- htCreditAccountAmount.add(
1067
- invoiceItem.CtAccountNo,
1068
- invoiceItem.Amount,
1069
- );
1070
-
1071
- htCreditAccountCurrency.add(
1072
- invoiceItem.CtAccountNo,
1073
- invoiceItem.Currency,
1074
- );
1075
-
1076
- htCreditAccountPurpose.add(invoiceItem.CtAccountNo, invoiceItem.Name);
1077
- } else {
1078
- //update the credit account amount
1079
- const d = htCreditAccountAmount.get(invoiceItem.CtAccountNo);
1080
- htCreditAccountAmount.add(
1081
- invoiceItem.CtAccountNo,
1082
- d + invoiceItem.Amount,
1083
- );
1084
- }
1085
- });
1086
-
1087
- const savedItems = htCreditAccountAmount.list();
1088
-
1089
- for (const item of savedItems) {
1090
- const journalEntry = new JournalEntry(dbTransaction);
1091
- //Temporary fix to successfully save journal entry in PostJournal
1092
- journalEntry.init({
1093
- CompanyId: this.CompanyId,
1094
- Name: 'issue Invoice ' + invoice.DocNo,
1095
- });
1096
- // const account = await Account.initAccount(dbTransaction, item.value[0]);
1097
- const creditAmount = item.value[1];
1098
- const currency = htCreditAccountCurrency.get(item.value[0]);
1099
- const purpose = htCreditAccountPurpose.get(item.value[0]);
1100
-
1101
- const dt = await journalEntry.newLedgerTransaction(
1102
- TransactionTypeOptions.DEBIT,
1103
- );
1104
-
1105
- if (dtAccountNo) {
1106
- /*Transacting using AR account provided*/
1107
- dt.AccountNo = dtAccountNo;
1108
- } else {
1109
- /*Transacting based on default customer AR account*/
1110
- const arAccount = await customer.getAccountReceivable();
1111
- dt.AccountNo = arAccount.AccountNo;
1112
- }
1113
-
1114
- dt.Currency = currency ? currency : 'MYR';
1115
- dt.DebitAmount = creditAmount ? creditAmount : 0.0;
1116
- dt.Date = transactionDate;
1117
- dt.Description = `${purpose}`;
1118
- dt.Name = `${purpose}`;
1119
- dt.RelatedDocNo = invoice.DocNo;
1120
- dt.RelatedObjectId = invoice.RelatedObjectId;
1121
- dt.RelatedObjectType = invoice.RelatedObjectType;
1122
-
1123
- const ct = await journalEntry.newLedgerTransaction(
1124
- TransactionTypeOptions.CREDIT,
1125
- );
1126
- ct.AccountNo = item.value[0];
1127
- ct.Currency = currency ? currency : 'MYR';
1128
- ct.CreditAmount = creditAmount ? creditAmount : 0.0;
1129
- ct.Date = transactionDate;
1130
- ct.Description = customer.FullName;
1131
- ct.Name = customer.FullName;
1132
- ct.RelatedDocNo = invoice.DocNo;
1133
- ct.RelatedObjectId = invoice.RelatedObjectId;
1134
- ct.RelatedObjectType = invoice.RelatedObjectType;
1135
-
1136
- await this.postJournal(dbTransaction, journalEntry, loginUser);
1137
- }
1138
-
1139
- return invoice;
1140
- } catch (err) {
1141
- // tslint:disable-next-line:no-console
1142
- console.log('Issue debit note err: ', err);
1143
- throw err;
1144
- }
1145
- }
1146
-
1147
- /**
1148
- * Issue a credit note
1149
- *
1150
- * @param dbTransaction - The database transaction to be used
1151
- * @param loginUser - The user issuing the credit note
1152
- * @param creditNote - The document containing the credit note details
1153
- * @param customer - The customer to be issued the credit note
1154
- * @param ctAccountNo - The account number of the Account Payable (AP) account to debit. If not provided, will debit the customer's default AR account
1155
- *
1156
- * @returns {Document} - Document object representing the full details of the invoice issued
1157
- */
1158
- async issueCreditNote(
1159
- dbTransaction: any,
1160
- loginUser: LoginUserBase,
1161
- creditNote: Document,
1162
- customer: FinanceCustomerBase,
1163
- ctAccountNo?: string,
1164
- ): Promise<Document> {
1165
- try {
1166
- /*Check if the invoice number already exists (should be unique)*/
1167
- const duplicateCreditNote =
1168
- await FinanceCompany._DocumentRepository.findOne({
1169
- where: {
1170
- DocNo: creditNote.DocNo,
1171
- },
1172
- transaction: dbTransaction,
1173
- });
1174
-
1175
- if (duplicateCreditNote) {
1176
- throw new Error('Invoice number already exists');
1177
- }
1178
-
1179
- const documentItems = await creditNote.getDocumentItems(dbTransaction);
1180
-
1181
- /*Check if the document has at least 1 document item*/
1182
- if (!documentItems.length) {
1183
- throw new Error('Document must have at least 1 document item');
1184
- }
1185
-
1186
- //Map each creditNote.DocumentItems and do checking below:
1187
- //1. Set actualDocument to the instantiation of existing Document. (to check valid Document)
1188
- //2. Check for each actualDocument to make sure IssuedToId all the same. If not, throw ClassError.
1189
- //3. Check if each document item has DtAccountNo provided
1190
- for (const invoiceItem of documentItems) {
1191
- if (!invoiceItem.DtAccountNo) {
1192
- throw new Error(
1193
- 'Each document item should have DtAccountNo provided',
1194
- );
1195
- }
1196
-
1197
- const actualDocument = await Document.initDocument(
1198
- dbTransaction,
1199
- invoiceItem.ItemId,
1200
- );
1201
-
1202
- if (actualDocument.IssuedToId !== creditNote.IssuedToId) {
1203
- throw new ClassError(
1204
- 'FinanceCompany',
1205
- 'FinanceCompanyErrMsgOX',
1206
- 'To issue credit note, all invoices must belong to same customer.',
1207
- );
1208
- }
1209
- }
1210
-
1211
- /*Set up the document type*/
1212
- creditNote.DocType = DocType.CREDIT_NOTE;
1213
- creditNote.Status = DocumentStatus.SETTLED;
1214
-
1215
- /*Saving the document and document items to the database*/
1216
- await FinanceCompany._DocumentRepository.create(
1217
- {
1218
- DocNo: creditNote.DocNo,
1219
- DocType: creditNote.DocType,
1220
- DocDate: creditNote.DocDate,
1221
- CompanyId: creditNote.CompanyId,
1222
- Currency: creditNote.Currency,
1223
- Amount: creditNote.Amount,
1224
- Description: creditNote.Description,
1225
- Status: creditNote.Status,
1226
- IssuedById: loginUser.ObjectId,
1227
- IssuedToId: customer.ObjectId,
1228
- IssuedToType: creditNote.IssuedToType,
1229
- RelatedObjectId: creditNote.RelatedObjectId,
1230
- RelatedObjectType: creditNote.RelatedObjectType,
1231
- CreatedById: loginUser.ObjectId,
1232
- CreatedAt: new Date(),
1233
- UpdatedById: loginUser.ObjectId,
1234
- UpdatedAt: new Date(),
1235
- DocPDFFileMediaId: creditNote.DocPDFFileMediaId,
1236
- DocHTMLFileMediaId: creditNote.DocHTMLFileMediaId,
1237
- AccSystemRefId: creditNote.AccSystemRefId,
1238
- PostedToAccSystemYN: creditNote.PostedToAccSystemYN,
1239
- PostedById:
1240
- creditNote.PostedToAccSystemYN == 'Y'
1241
- ? creditNote.PostedById
1242
- : null,
1243
- PostedDateTime: new Date(),
1244
- UseAccSystemDocYN: creditNote.UseAccSystemDocYN,
1245
- },
1246
- {
1247
- transaction: dbTransaction,
1248
- },
1249
- );
1250
-
1251
- for (const docItem of documentItems) {
1252
- await FinanceCompany._DocumentItemRepository.create(
1253
- {
1254
- DocumentItemId: this._createId().toUpperCase(),
1255
- DocNo: docItem.DocNo,
1256
- Name: docItem.Name,
1257
- NameBM: docItem.NameBM,
1258
- Description: docItem.Description,
1259
- ItemId: docItem.ItemId,
1260
- ItemType: docItem.ItemType,
1261
- ItemSKU: docItem.ItemSKU,
1262
- ItemSerialNo: docItem.ItemSerialNo,
1263
- Currency: docItem.Currency,
1264
- UnitPrice: docItem.UnitPrice,
1265
- Quantity: docItem.Quantity,
1266
- QuantityUOM: docItem.QuantityUOM,
1267
- Amount: docItem.Amount,
1268
- TaxCode: docItem.TaxCode,
1269
- TaxAmount: docItem.TaxAmount,
1270
- TaxRate: docItem.TaxRate,
1271
- TaxInclusiveYN: docItem.TaxInclusiveYN,
1272
- DtAccountNo: docItem.DtAccountNo ? docItem.DtAccountNo : null,
1273
- CtAccountNo: docItem.CtAccountNo ? docItem.CtAccountNo : null,
1274
- },
1275
- {
1276
- transaction: dbTransaction,
1277
- },
1278
- );
1279
-
1280
- //Call Document.settleByCreditNote to settle the invoice
1281
- await Document.settleByCreditNote(
1282
- loginUser,
1283
- dbTransaction,
1284
- docItem.ItemId,
1285
- docItem.Amount,
1286
- );
1287
- }
1288
-
1289
- /*Generating the credit note*/
1290
- if (creditNote.UseAccSystemDocYN === 'Y') {
1291
- /*todo: Posting to accounting system to generate creditNote*/
1292
- // await this.AccountingSystem.createCreditNote(creditNote);
1293
- } else {
1294
- /*todo: check config file to see which invoice template is to be used for specific project*/
1295
-
1296
- /*Generating credit note based on template*/
1297
- creditNote.generateCreditNote(loginUser.IDNo, customer);
1298
- }
1299
-
1300
- const journalEntry = new JournalEntry(dbTransaction);
1301
- //Temporary fix to successfully save journal entry in PostJournal
1302
- journalEntry.init({
1303
- CompanyId: this.CompanyId,
1304
- Name: 'Issue Credit Note ' + creditNote.DocNo,
1305
- });
1306
-
1307
- const transactionDate = new Date();
1308
-
1309
- const creditTransaction = await journalEntry.newLedgerTransaction(
1310
- TransactionTypeOptions.CREDIT,
1311
- );
1312
-
1313
- if (ctAccountNo) {
1314
- /*Transacting using AR account provided*/
1315
- creditTransaction.AccountNo = ctAccountNo;
1316
- } else {
1317
- /*Transacting based on default customer AR account*/
1318
- // creditTransaction.AccountNo = customer.CustSystemCode + '-AP';
1319
- // getAccountPayable
1320
- const arAccount = await customer.getAccountPayable();
1321
- creditTransaction.AccountNo = arAccount.AccountNo;
1322
- }
1323
-
1324
- creditTransaction.Currency = creditNote.Currency;
1325
- creditTransaction.CreditAmount = creditNote.Amount;
1326
- creditTransaction.Date = transactionDate;
1327
- creditTransaction.Description = creditNote.DocNo;
1328
- creditTransaction.Name = creditNote.DocNo;
1329
- creditTransaction.RelatedDocNo = creditNote.DocNo;
1330
- creditTransaction.RelatedObjectId = creditNote.RelatedObjectId;
1331
- creditTransaction.RelatedObjectType = creditNote.RelatedObjectType;
1332
-
1333
- for (const invoiceItem of documentItems) {
1334
- const itemLedger = await journalEntry.newLedgerTransaction(
1335
- TransactionTypeOptions.DEBIT,
1336
- );
1337
- itemLedger.AccountNo = invoiceItem.DtAccountNo;
1338
- itemLedger.Currency = invoiceItem.Currency;
1339
- itemLedger.DebitAmount = invoiceItem.Amount;
1340
- itemLedger.Date = transactionDate;
1341
- itemLedger.Description = invoiceItem.Name;
1342
- // itemLedger.Name = invoiceItem.Name;
1343
- itemLedger.RelatedDocNo = creditNote.DocNo;
1344
- itemLedger.RelatedObjectId = creditNote.RelatedObjectId;
1345
- itemLedger.RelatedObjectType = creditNote.RelatedObjectType;
1346
- }
1347
-
1348
- await this.postJournal(dbTransaction, journalEntry, loginUser);
1349
-
1350
- const entityValueAfter = {
1351
- LedgerNo: creditTransaction.LedgerNo,
1352
- TransactionType: creditTransaction.TransactionType,
1353
- JournalEntryId: creditTransaction.JournalEntryId,
1354
- AccountNo: creditTransaction.AccountNo,
1355
- Date: creditTransaction.Date,
1356
- Name: creditTransaction.Name,
1357
- Description: creditTransaction.Description,
1358
- Currency: creditTransaction.Currency,
1359
- DebitAmount: creditTransaction.DebitAmount,
1360
- CreditAmount: creditTransaction.CreditAmount,
1361
- RelatedObjectId: creditTransaction.RelatedObjectId,
1362
- RelatedObjectType: creditTransaction.RelatedObjectType,
1363
- RelatedDocNo: creditTransaction.RelatedDocNo,
1364
- RelatedPaymentId: creditTransaction.RelatedPaymentId,
1365
- };
1366
- const payload = {
1367
- Action: 'Create',
1368
- Activity: 'Issuing a Credit Note Transaction',
1369
- Description: `Credit Transaction (ID: ${creditTransaction.LedgerNo}) has been created`,
1370
- EntityType: 'CreditTransaction',
1371
- EntityValueBefore: JSON.stringify({}),
1372
- EntityValueAfter: JSON.stringify(entityValueAfter),
1373
- PerformedById: loginUser.ObjectId,
1374
- PerformedAt: transactionDate,
1375
- EntityId: creditTransaction.LedgerNo,
1376
- };
1377
-
1378
- await axios.post(
1379
- `${process.env.COMMON_API_URL}/activity-histories`,
1380
- payload,
1381
- );
1382
-
1383
- return creditNote;
1384
- } catch (err) {
1385
- // tslint:disable-next-line:no-console
1386
- console.log('Issue credit note err: ', err);
1387
- throw err;
1388
- }
1389
- }
1390
-
1391
- /**
1392
- * Register a payment collected
1393
- *
1394
- * @param dbTransaction - The database transaction to be used
1395
- * @param loginUser - The user collecting the payment
1396
- * @param payment - The payment object containing payment details
1397
- * @param customer - The customer making the payment
1398
- * @param ctAccountNo - The account number of the customer's Account Receivable account to transact, else the default customer account payable receivable will be used
1399
- * @param receiptNo - The document receipt number
1400
- *
1401
- * @returns {Payment} - Payment object representing the full detals of the payment collection recorded
1402
- */
1403
- async collectPayment(
1404
- dbTransaction: any,
1405
- loginUser: LoginUserBase,
1406
- payment: Payment,
1407
- collectPaymentType: CollectPaymentType = CollectPaymentType.AUTOMATIC,
1408
- ): Promise<Payment> {
1409
- try {
1410
- /*validation 1: Make sure that the payment has at least 1 payment item*/
1411
- const paymentItems = await payment.getPaymentItems();
1412
- if (paymentItems.length < 1) {
1413
- throw new Error(
1414
- 'Atleast one payment item is required to identify what payment is being paid for.',
1415
- );
1416
- }
1417
-
1418
- /*validation 2: Make sure that the payment has at least 1 payment method*/
1419
- const paymentPaidWithItems = await payment.getPaymentPaidWith();
1420
- if (paymentPaidWithItems.length < 1) {
1421
- throw new Error(
1422
- 'Atleast one payment paid with item is required to identify how the payment was made.',
1423
- );
1424
- }
1425
-
1426
- /*Saving payment, payment items, and payment paid with details to the database*/
1427
- payment.PaymentId = this._createId().toUpperCase();
1428
- payment.PaymentType = PaymentType.PAYMENT_RECEIVED;
1429
- payment.ReceivedBy = loginUser.ObjectId;
1430
- payment.IssuedBy = loginUser.ObjectId;
1431
-
1432
- await FinanceCompany._PaymentRepository.create(
1433
- {
1434
- PaymentId: payment.PaymentId,
1435
- PaymentType: payment.PaymentType,
1436
- PaymentDate: payment.PaymentDate,
1437
- Description: payment.Description,
1438
- Currency: payment.Currency,
1439
- Amount: payment.Amount,
1440
- Status:
1441
- collectPaymentType === CollectPaymentType.AUTOMATIC
1442
- ? PaymentStatus.CONFIRMED
1443
- : PaymentStatus.PENDING,
1444
- PostedToAccSystemYN: 'N',
1445
- ReceivedBy: payment.ReceivedBy,
1446
- IssuedBy: payment.IssuedBy,
1447
- Remarks: payment.Remarks,
1448
- RelatedObjectId: payment.RelatedObjectId,
1449
- RelatedObjectType: payment.RelatedObjectType,
1450
- ReceiptDocNo: payment.ReceiptDocNo,
1451
- UpdatedAt: new Date(),
1452
- UpdatedBy: loginUser.ObjectId,
1453
- CreatedAt: new Date(),
1454
- CreatedBy: loginUser.ObjectId,
1455
- },
1456
- { transaction: dbTransaction },
1457
- );
1458
-
1459
- for (const paymentItem of paymentItems) {
1460
- await FinanceCompany._PaymentItemRepository.create(
1461
- {
1462
- PaymentId: payment.PaymentId,
1463
- PayForObjectId: paymentItem.PayForObjectId,
1464
- PayForObjectType: paymentItem.PayForObjectType,
1465
- Currency: paymentItem.Currency,
1466
- Amount: paymentItem.Amount,
1467
- Name: paymentItem.Name,
1468
- Description: paymentItem.Description,
1469
- },
1470
- { transaction: dbTransaction },
1471
- );
1472
-
1473
- // await paymentItem.paid(dbTransaction);
1474
- }
1475
-
1476
- for (const paymentPaidWithItem of paymentPaidWithItems) {
1477
- // Validate payment method type used
1478
- await PaymentMethodType.initMethodType(
1479
- dbTransaction,
1480
- paymentPaidWithItem.MethodTypeId,
1481
- );
1482
-
1483
- await FinanceCompany._PaymentPaidWithRepository.create(
1484
- {
1485
- PaymentId: payment.PaymentId,
1486
- MethodTypeId: paymentPaidWithItem.MethodTypeId,
1487
- Currency: paymentPaidWithItem.Currency,
1488
- Amount: paymentPaidWithItem.Amount,
1489
- Status: paymentPaidWithItem.Status,
1490
- TransactionId: paymentPaidWithItem.TransactionId,
1491
- RefBank: paymentPaidWithItem.RefBank,
1492
- RefName: paymentPaidWithItem.RefName,
1493
- RefNo: paymentPaidWithItem.RefNo,
1494
- RefOther1: paymentPaidWithItem.RefOther1,
1495
- RefOther2: paymentPaidWithItem.RefOther2,
1496
- RefOther3: paymentPaidWithItem.RefOther3,
1497
- RefOther4: paymentPaidWithItem.RefOther4,
1498
- RefOther5: paymentPaidWithItem.RefOther5,
1499
- Remarks: paymentPaidWithItem.Remarks,
1500
- PaymentMediaId: paymentPaidWithItem.PaymentMediaId,
1501
- },
1502
- { transaction: dbTransaction },
1503
- );
1504
- }
1505
-
1506
- /*todo: saving a record into the activity history table*/
1507
-
1508
- return payment;
1509
- } catch (error) {
1510
- throw error;
1511
- }
1512
- }
1513
-
1514
- /**
1515
- * Method to make payment to a customer.
1516
- *
1517
- * @param dbTransaction - The database transaction to be used
1518
- * @param loginUser - The user logging in and the user who collected the payment.
1519
- * @param payment - The payment object containing payment details
1520
- * @param customer - The customer who is receiving the payment.
1521
- * @param ctAccountNo - The account number of the customer's Account Payable account to transact, else the default customer account payable will be used.
1522
- *
1523
- * @returns {Payment} - Payment object representing the full details of the payment collection recorded
1524
- */
1525
- async makePayment(
1526
- dbTransaction: any,
1527
- loginUser: LoginUserBase,
1528
- payment: Payment,
1529
- customer: FinanceCustomerBase,
1530
- dtAccountNo?: string,
1531
- ): Promise<Payment> {
1532
- let paymentMethodType: PaymentMethodType;
1533
- try {
1534
- const paymentPaidWith = await payment.getPaymentPaidWith();
1535
- if (paymentPaidWith.length < 1) {
1536
- throw new Error(
1537
- 'Atleast one payment paid with item is required to identify how the payment was made.',
1538
- );
1539
- }
1540
- paymentMethodType = await PaymentMethodType.initMethodType(
1541
- dbTransaction,
1542
- paymentPaidWith[0].MethodTypeId,
1543
- );
1544
-
1545
- const paymentItems = await payment.getPaymentItems();
1546
- if (paymentItems.length < 1) {
1547
- throw new Error(
1548
- 'Atleast one payment item is required to identify what payment is being paid for',
1549
- );
1550
- }
1551
-
1552
- payment.PaymentId = this._createId().toUpperCase();
1553
- payment.PaymentType = PaymentType.PAYOUT;
1554
- payment.ReceivedBy = loginUser.ObjectId;
1555
- payment.IssuedBy = loginUser.ObjectId;
1556
-
1557
- await FinanceCompany._PaymentRepository.create(
1558
- {
1559
- PaymentId: payment.PaymentId,
1560
- PaymentType: payment.PaymentType,
1561
- PaymentDate: payment.PaymentDate,
1562
- Description: payment.Description,
1563
- Currency: payment.Currency,
1564
- ReceivedBy: payment.ReceivedBy,
1565
- IssuedBy: payment.IssuedBy,
1566
- Remarks: payment.Remarks,
1567
- RelatedObjectId: payment.RelatedObjectId,
1568
- RelatedObjectType: payment.RelatedObjectType,
1569
- Amount: payment.Amount,
1570
- Status: PaymentStatus.CONFIRMED,
1571
- PostedToAccSystemYN: 'N',
1572
- UpdatedAt: new Date(),
1573
- UpdatedBy: loginUser.ObjectId,
1574
- CreatedAt: new Date(),
1575
- CreatedBy: loginUser.ObjectId,
1576
- },
1577
- { transaction: dbTransaction },
1578
- );
1579
-
1580
- paymentItems.forEach(async (paymentItem) => {
1581
- await FinanceCompany._PaymentItemRepository.create(
1582
- {
1583
- PaymentId: payment.PaymentId,
1584
- PayForObjectId: paymentItem.PayForObjectId,
1585
- PayForObjectType: paymentItem.PayForObjectType,
1586
- Currency: paymentItem.Currency,
1587
- Amount: paymentItem.Amount,
1588
- Name: paymentItem.Name,
1589
- Description: paymentItem.Description,
1590
- },
1591
- { transaction: dbTransaction },
1592
- );
1593
-
1594
- await paymentItem.paid(dbTransaction);
1595
- });
1596
-
1597
- for (const paymentPaidWithItem of paymentPaidWith) {
1598
- await FinanceCompany._PaymentPaidWithRepository.create(
1599
- {
1600
- PaymentId: payment.PaymentId,
1601
- MethodTypeId: paymentPaidWithItem.MethodTypeId,
1602
- Currency: paymentPaidWithItem.Currency,
1603
- Amount: paymentPaidWithItem.Amount,
1604
- Status: paymentPaidWithItem.Status,
1605
- TransactionId: paymentPaidWithItem.TransactionId,
1606
- RefBank: paymentPaidWithItem.RefBank,
1607
- RefName: paymentPaidWithItem.RefName,
1608
- RefNo: paymentPaidWithItem.RefNo,
1609
- RefOther1: paymentPaidWithItem.RefOther1,
1610
- RefOther2: paymentPaidWithItem.RefOther2,
1611
- RefOther3: paymentPaidWithItem.RefOther3,
1612
- RefOther4: paymentPaidWithItem.RefOther4,
1613
- RefOther5: paymentPaidWithItem.RefOther5,
1614
- Remarks: paymentPaidWithItem.Remarks,
1615
- PaymentMediaId: paymentPaidWithItem.PaymentMediaId,
1616
- },
1617
- { transaction: dbTransaction },
1618
- );
1619
- }
1620
- const transactionDate = new Date();
1621
- for (const paymentPaidWithItem of paymentPaidWith) {
1622
- try {
1623
- paymentMethodType = await PaymentMethodType.initMethodType(
1624
- dbTransaction,
1625
- paymentPaidWithItem.MethodTypeId,
1626
- );
1627
-
1628
- const journalEntry = new JournalEntry(dbTransaction);
1629
- //Temporary fix to successfully save journal entry in PostJournal
1630
- journalEntry.init({
1631
- CompanyId: this.CompanyId,
1632
- Name: 'Make Payment for ' + payment.PaymentId,
1633
- });
1634
-
1635
- const creditLT = await journalEntry.newLedgerTransaction(
1636
- TransactionTypeOptions.CREDIT,
1637
- );
1638
- creditLT.AccountNo = paymentMethodType.AccountNo;
1639
- creditLT.Currency = paymentPaidWithItem.Currency;
1640
- creditLT.CreditAmount = paymentPaidWithItem.Amount;
1641
- creditLT.Date = transactionDate;
1642
- creditLT.Description = customer.FullName;
1643
- creditLT.Name = customer.FullName;
1644
- creditLT.RelatedObjectId = payment.RelatedObjectId;
1645
- creditLT.RelatedObjectType = payment.RelatedObjectType;
1646
-
1647
- const debitLT = await journalEntry.newLedgerTransaction(
1648
- TransactionTypeOptions.DEBIT,
1649
- );
1650
- if (dtAccountNo) {
1651
- debitLT.AccountNo = dtAccountNo;
1652
- } else {
1653
- const apAccount = await customer.getAccountPayable();
1654
- debitLT.AccountNo = apAccount.AccountNo;
1655
- }
1656
- debitLT.Currency = paymentPaidWithItem.Currency;
1657
- debitLT.DebitAmount = paymentPaidWithItem.Amount;
1658
- debitLT.Date = transactionDate;
1659
- debitLT.Description = paymentMethodType.Name;
1660
- debitLT.Name = paymentMethodType.Name;
1661
- debitLT.RelatedObjectId = payment.RelatedObjectId;
1662
- debitLT.RelatedObjectType = payment.RelatedObjectType;
1663
-
1664
- await this.postJournal(dbTransaction, journalEntry, loginUser);
1665
- } catch (error) {
1666
- if (error instanceof RecordNotFoundError) {
1667
- throw new Error('Invalid Payment Method Type Id');
1668
- } else {
1669
- throw error;
1670
- }
1671
- }
1672
- }
1673
- /*todo: saving a record into the activity history table*/
1674
-
1675
- return payment;
1676
- } catch (error) {
1677
- if (error instanceof RecordNotFoundError) {
1678
- throw new Error('Invalid PaymentMethodType id');
1679
- } else {
1680
- throw error;
1681
- }
1682
- }
1683
- }
1684
-
1685
- get PaymentMethods(): Promise<PaymentMethod[]> {
1686
- return new Promise((resolve, reject) => {
1687
- if (this.CompanyId !== 'New') {
1688
- FinanceCompany._PaymentMethodRepository
1689
- .findAll({
1690
- where: {
1691
- CompanyId: this.CompanyId,
1692
- },
1693
- // transaction: this._DbTransaction,
1694
- })
1695
- .then((paymentMethod) => {
1696
- const paymentMethodObjects = paymentMethod.map(
1697
- (paymentMethodData) => {
1698
- return new Promise((resolve, reject) => {
1699
- FinanceCompany._PaymentMethodTypeRepository
1700
- .findAll({
1701
- where: {
1702
- MethodId: paymentMethodData.MethodId,
1703
- },
1704
- // transaction: this._DbTransaction,
1705
- raw: true,
1706
- })
1707
- .then((paymentMethodTypes) => {
1708
- const paymentMethodObjects = {
1709
- ...paymentMethodData.get({ plain: true }),
1710
- Types: paymentMethodTypes,
1711
- };
1712
- resolve(paymentMethodObjects);
1713
- })
1714
- .catch((err) => {
1715
- reject(err);
1716
- });
1717
- }).then((paymentMethods) => paymentMethods);
1718
- },
1719
- );
1720
- return Promise.all(paymentMethodObjects);
1721
- })
1722
- .then((paymentMethodObjects) => {
1723
- this._PaymentMethods = paymentMethodObjects;
1724
- resolve(this._PaymentMethods);
1725
- })
1726
- .catch((err) => {
1727
- reject(err);
1728
- });
1729
- } else {
1730
- resolve(this._PaymentMethods);
1731
- }
1732
- });
1733
- }
1734
-
1735
- get TaxCodes(): Promise<Tax[]> {
1736
- return new Promise((resolve, reject) => {
1737
- if (this.CompanyId !== 'New') {
1738
- FinanceCompany._TaxRepository
1739
- .findAll({
1740
- where: {
1741
- CompanyId: this.CompanyId,
1742
- },
1743
- transaction: this._DbTransaction,
1744
- })
1745
- .then((taxes) => {
1746
- const taxList = [];
1747
- taxes.forEach((tax) => {
1748
- taxList.push(
1749
- new Tax({
1750
- TaxCode: tax.TaxCode,
1751
- TaxRate:
1752
- typeof tax.TaxRate === 'number'
1753
- ? tax.TaxRate
1754
- : parseFloat(tax.TaxRate),
1755
- Description: tax.Description,
1756
- CompanyId: tax.CompanyId,
1757
- CreatedAt: tax.CreatedAt,
1758
- CreatedById: tax.CreatedById,
1759
- UpdatedAt: tax.UpdatedAt,
1760
- UpdatedById: tax.UpdatedById,
1761
- }),
1762
- );
1763
- });
1764
- this._Taxes = taxList;
1765
- resolve(this._Taxes);
1766
- })
1767
- .catch((err) => {
1768
- reject(err);
1769
- });
1770
- } else {
1771
- resolve(this._Taxes);
1772
- }
1773
- });
1774
- }
1775
-
1776
- /**
1777
- * Method to load / reload the payment methods based on the configuration file
1778
- */
1779
- async LoadPaymentMethods(
1780
- companyId: string,
1781
- paymentMethods: any,
1782
- transaction?: any,
1783
- ): Promise<void> {
1784
- const paymentMethod = new PaymentMethod();
1785
- for (const method in paymentMethods) {
1786
- const paymentMethodData =
1787
- await FinanceCompany._PaymentMethodRepository.findOne({
1788
- where: {
1789
- MethodId: paymentMethods[method].id,
1790
- Name: paymentMethods[method].name,
1791
- },
1792
- transaction: transaction,
1793
- });
1794
-
1795
- if (!paymentMethodData) {
1796
- const newPaymentMethod =
1797
- await FinanceCompany._PaymentMethodRepository.create(
1798
- {
1799
- MethodId: paymentMethods[method].id,
1800
- Name: paymentMethods[method].name,
1801
- CompanyId: companyId,
1802
- },
1803
- {
1804
- transaction: transaction,
1805
- },
1806
- );
1807
-
1808
- this._PaymentMethods.push(newPaymentMethod);
1809
- }
1810
- this._PaymentMethods.push(paymentMethodData);
1811
- }
1812
-
1813
- this._PaymentMethods.forEach(async (item) => {
1814
- const p = item?.get({ plain: true });
1815
-
1816
- for (const method in paymentMethods) {
1817
- if (!p) {
1818
- continue;
1819
- }
1820
-
1821
- if (p.MethodId === paymentMethods[method]?.id) {
1822
- const paymentMethodTypeData =
1823
- await FinanceCompany._PaymentMethodTypeRepository.findOne({
1824
- where: {
1825
- MethodId: p.MethodId,
1826
- },
1827
- transaction: transaction,
1828
- });
1829
-
1830
- if (!paymentMethodTypeData) {
1831
- const configPaymentMethodTypes = paymentMethods[method]?.types;
1832
-
1833
- for (const methodType in configPaymentMethodTypes) {
1834
- // TODO: Create a seeder for payment method account
1835
- /*validate whether account data already exists*/
1836
- const accountData =
1837
- await FinanceCompany._AccountRepository.findOne({
1838
- where: {
1839
- AccountNo: configPaymentMethodTypes[methodType].accountNo,
1840
- },
1841
- transaction: transaction,
1842
- });
1843
-
1844
- /*generating account data if not exist */
1845
- if (!accountData) {
1846
- const accountPayload = {
1847
- CompanyId: companyId,
1848
- Name: configPaymentMethodTypes[methodType].name,
1849
- AccountType: 'PaymentMethod',
1850
- CreatedAt: new Date(),
1851
- CreatedById: 'System',
1852
- AccSystemRefId: 'REF',
1853
- PostedToAccSystemYN: 'N',
1854
- };
1855
-
1856
- try {
1857
- await FinanceCompany._AccountRepository.create(
1858
- {
1859
- AccountNo: configPaymentMethodTypes[methodType].accountNo,
1860
- ...accountPayload,
1861
- },
1862
- {
1863
- transaction: transaction,
1864
- },
1865
- );
1866
-
1867
- await FinanceCompany._AccountRepository.create(
1868
- {
1869
- AccountNo:
1870
- configPaymentMethodTypes[methodType]
1871
- .processingFeeAccountNo,
1872
- ...accountPayload,
1873
- },
1874
- {
1875
- transaction: transaction,
1876
- },
1877
- );
1878
- } catch (err) {
1879
- throw err;
1880
- }
1881
- }
1882
-
1883
- const paymentMethodTypePayload = {
1884
- MethodId: p.MethodId,
1885
- MethodTypeId: configPaymentMethodTypes[methodType].id,
1886
- Name: configPaymentMethodTypes[methodType].name,
1887
- AccountNo: configPaymentMethodTypes[methodType].accountNo,
1888
- ProcessingFeeRate:
1889
- configPaymentMethodTypes[methodType].processingFeeRate,
1890
- ProcessingFeeAccountNo:
1891
- configPaymentMethodTypes[methodType].processingFeeAccountNo,
1892
- };
1893
-
1894
- try {
1895
- await paymentMethod.newPaymentMethodType(
1896
- paymentMethodTypePayload,
1897
- transaction,
1898
- );
1899
- } catch (err) { }
1900
- }
1901
- }
1902
- }
1903
- }
1904
- });
1905
- }
1906
-
1907
- /**
1908
- * Method to load / reload the payment methods based on the configuration file
1909
- */
1910
- async LoadTaxCodes(
1911
- companyId: string,
1912
- companyTaxes: any,
1913
- transaction?: any,
1914
- ): Promise<void> {
1915
- for (const tax in companyTaxes) {
1916
- let tx = await FinanceCompany._TaxRepository.findOne({
1917
- where: {
1918
- TaxCode: companyTaxes[tax].taxCode,
1919
- },
1920
- transaction: transaction,
1921
- });
1922
-
1923
- if (!tx) {
1924
- const newTx = await FinanceCompany._TaxRepository.create(
1925
- {
1926
- TaxCode: companyTaxes[tax].taxCode,
1927
- TaxRate: companyTaxes[tax].taxRate,
1928
- Description: companyTaxes[tax].description,
1929
- CompanyId: companyId,
1930
- UpdatedAt: new Date(),
1931
- CreatedById: 'System',
1932
- CreatedAt: new Date(),
1933
- UpdatedById: 'System',
1934
- },
1935
- {
1936
- transaction: transaction,
1937
- },
1938
- );
1939
-
1940
- tx = newTx;
1941
- }
1942
- this._Taxes.push(new Tax(tx.get({ plain: true })));
1943
- }
1944
- }
1945
-
1946
- async collectPaymentForMultipleCustomers(
1947
- dbTransaction: any,
1948
- loginUser: LoginUserBase,
1949
- payment: Payment,
1950
- creditTransaction: {
1951
- AccountNo: string;
1952
- Currency: string;
1953
- Amount: number;
1954
- }[],
1955
- receiptNo?: string,
1956
- collectPaymentType: CollectPaymentType = CollectPaymentType.AUTOMATIC,
1957
- ): Promise<Payment> {
1958
- //Method to collect payment for multiple customer.
1959
- try {
1960
- //Part 1: Privilege Checking\
1961
- const systemCode = await ApplicationConfig.getComponentConfigValue(
1962
- 'system-code',
1963
- );
1964
- const isPrivileged = await loginUser.checkPrivileges(
1965
- systemCode,
1966
- 'Collect Payment For Multiple Customers',
1967
- );
1968
-
1969
- if (!isPrivileged) {
1970
- throw new Error('User is not authorized to perform this action');
1971
- }
1972
-
1973
- //Part 2: Validation
1974
- //Make sure that the payment has at least 1 payment item
1975
- const paymentItems = await payment.getPaymentItems();
1976
- if (paymentItems.length < 1) {
1977
- throw new Error(
1978
- 'Atleast one payment item is required to identify what payment is being paid for.',
1979
- );
1980
- }
1981
-
1982
- //Make sure that the payment has at least 1 payment method
1983
- const paymentPaidWithItems = await payment.getPaymentPaidWith();
1984
- if (paymentPaidWithItems.length < 1) {
1985
- throw new Error(
1986
- 'Atleast one payment paid with item is required to identify how the payment was made.',
1987
- );
1988
- }
1989
-
1990
- //Make sure the payment items length is equal to creditTransactions length records.
1991
- if (paymentItems.length !== creditTransaction.length) {
1992
- throw new Error(
1993
- 'Payment items length is not equal to creditTransaction length',
1994
- );
1995
- }
1996
-
1997
- //Make sure the payment items total Amount is equal to credit transactions total Amount.
1998
- let totalAmount = 0;
1999
- paymentItems.forEach((paymentItem) => {
2000
- totalAmount += paymentItem.Amount;
2001
- });
2002
-
2003
- const totalCreditTransactionAmount = creditTransaction.reduce(
2004
- (accumulator, currentValue) => {
2005
- return accumulator + currentValue.Amount;
2006
- },
2007
- 0,
2008
- );
2009
-
2010
- if (totalAmount !== totalCreditTransactionAmount) {
2011
- throw new Error(
2012
- 'Payment items total Amount is dnot equal to credit transactions total Amount',
2013
- );
2014
- }
2015
-
2016
- //Part 3:Generate Receipt (KIV)
2017
-
2018
- //Part 4: Saving Payment, Payment Items and Payment Paid with Details to the Database
2019
-
2020
- //Set below Payment attributes:
2021
- payment.PaymentId = this._createId().toUpperCase();
2022
- payment.PaymentType = PaymentType.PAYMENT_RECEIVED;
2023
- payment.ReceivedBy = loginUser.ObjectId;
2024
- payment.IssuedBy = loginUser.ObjectId;
2025
-
2026
- //Call Payment Repository create() method by passing:
2027
- await FinanceCompany._PaymentRepository.create(
2028
- {
2029
- PaymentId: payment.PaymentId,
2030
- PaymentType: payment.PaymentType,
2031
- PaymentDate: payment.PaymentDate,
2032
- Description: payment.Description,
2033
- Currency: payment.Currency,
2034
- Amount: payment.Amount,
2035
- Status:
2036
- collectPaymentType === CollectPaymentType.MANUAL
2037
- ? PaymentStatus.PENDING
2038
- : PaymentStatus.CONFIRMED,
2039
- PostedToAccSystemYN: 'N',
2040
- ReceivedBy: payment.ReceivedBy,
2041
- IssuedBy: payment.IssuedBy,
2042
- Remarks: payment.Remarks,
2043
- RelatedObjectId: payment.RelatedObjectId,
2044
- RelatedObjectType: payment.RelatedObjectType,
2045
- ReceiptDocNo: payment.ReceiptDocNo,
2046
- UpdatedAt: new Date(),
2047
- UpdatedBy: loginUser.ObjectId,
2048
- CreatedAt: new Date(),
2049
- CreatedBy: loginUser.ObjectId,
2050
- },
2051
- { transaction: dbTransaction },
2052
- );
2053
-
2054
- //Map Payment.PaymentItems and insert record by passing dbTransaction
2055
- paymentItems.forEach(async (paymentItem) => {
2056
- await FinanceCompany._PaymentItemRepository.create(
2057
- {
2058
- PaymentId: payment.PaymentId,
2059
- PayForObjectId: paymentItem.PayForObjectId,
2060
- PayForObjectType: paymentItem.PayForObjectType,
2061
- Currency: paymentItem.Currency,
2062
- Amount: paymentItem.Amount,
2063
- Name: paymentItem.Name,
2064
- Description: paymentItem.Description,
2065
- },
2066
- { transaction: dbTransaction },
2067
- );
2068
- });
2069
-
2070
- // Map Payment.PaymentPaidWith and insert record by passing dbTransaction.
2071
- paymentPaidWithItems.forEach(async (paymentPaidWithItem) => {
2072
- await FinanceCompany._PaymentPaidWithRepository.create(
2073
- {
2074
- PaymentId: payment.PaymentId,
2075
- MethodTypeId: paymentPaidWithItem.MethodTypeId,
2076
- Currency: paymentPaidWithItem.Currency,
2077
- Amount: paymentPaidWithItem.Amount,
2078
- Status: paymentPaidWithItem.Status,
2079
- LedgerNo: paymentPaidWithItem.TransactionId,
2080
- RefBank: paymentPaidWithItem.RefBank,
2081
- RefName: paymentPaidWithItem.RefName,
2082
- RefNo: paymentPaidWithItem.RefNo,
2083
- RefOther1: paymentPaidWithItem.RefOther1,
2084
- RefOther2: paymentPaidWithItem.RefOther2,
2085
- RefOther3: paymentPaidWithItem.RefOther3,
2086
- RefOther4: paymentPaidWithItem.RefOther4,
2087
- RefOther5: paymentPaidWithItem.RefOther5,
2088
- Remarks: paymentPaidWithItem.Remarks,
2089
- PaymentMediaId: paymentPaidWithItem.PaymentMediaId,
2090
- },
2091
- { transaction: dbTransaction },
2092
- );
2093
- });
2094
-
2095
- //Part 5: Registering the Journal Entries for the transaction
2096
- //Initialise TransactionDate
2097
- // const transactionDate = new Date();
2098
- // //Initialise new JournalEntry
2099
- // const journalEntry = new JournalEntry(dbTransaction);
2100
- // journalEntry.init({
2101
- // CompanyId: this.CompanyId,
2102
- // Name: 'Collect-payments for ' + payment.PaymentId,
2103
- // });
2104
-
2105
- // // For each Payment.PaymentPaidWith:
2106
- // for (const paymentPaidWithItem of paymentPaidWithItems) {
2107
- // const paymentMethodType = await PaymentMethodType.initMethodType(
2108
- // dbTransaction,
2109
- // paymentPaidWithItem.MethodTypeId,
2110
- // );
2111
-
2112
- // //Initialise new Debit LedgerTransaction by calling JournalEntry.newLedgerTransaction() method and set below attributes:
2113
- // const debitLT = await journalEntry.newLedgerTransaction(
2114
- // TransactionTypeOptions.DEBIT,
2115
- // );
2116
- // debitLT.AccountNo = paymentMethodType.AccountNo;
2117
- // debitLT.Currency = paymentPaidWithItem.Currency;
2118
- // debitLT.DebitAmount = paymentPaidWithItem.Amount;
2119
- // debitLT.Date = transactionDate;
2120
- // debitLT.Description = 'Payment Received';
2121
- // debitLT.RelatedPaymentId = payment.PaymentId;
2122
- // }
2123
-
2124
- // //For each creditTransactions, Initialise new Credit LedgerTransaction by calling JournalEntry.newLedgerTransaction() method
2125
- // for (const ct of creditTransaction) {
2126
- // const creditLT = await journalEntry.newLedgerTransaction(
2127
- // TransactionTypeOptions.CREDIT,
2128
- // );
2129
- // creditLT.AccountNo = ct.AccountNo;
2130
- // creditLT.Currency = ct.Currency;
2131
- // creditLT.CreditAmount = ct.Amount;
2132
- // creditLT.Date = transactionDate;
2133
- // creditLT.Description = 'Payment Received';
2134
- // creditLT.RelatedPaymentId = payment.PaymentId;
2135
- // }
2136
-
2137
- // // Call this.postJournal()
2138
- // await this.postJournal(dbTransaction, journalEntry, loginUser);
2139
-
2140
- return payment;
2141
- } catch (error) {
2142
- throw error;
2143
- }
2144
- }
2145
-
2146
- /**
2147
- * Confirms a payment and performs necessary actions such as creating a payment receipt and registering journal entries.
2148
- * @param dbTransaction - The database transaction object.
2149
- * @param loginUser - The logged-in user object.
2150
- * @param customer - The customer object.
2151
- * @param payment - The payment object.
2152
- * @param status - The status of the payment (CONFIRMED, REJECTED, or FAILED).
2153
- * @param remarks - Additional remarks for the payment.
2154
- * @param ctAccountNo - Optional. The account number for the credit transaction.
2155
- * @param receiptNo - Optional. The receipt number for the payment.
2156
- * @returns A promise that resolves when the payment is confirmed.
2157
- * @throws Throws an error if the user does not have the necessary privileges, if the payment is not found, if the payment status is not 'Pending', or if an invalid status is provided.
2158
- */
2159
- async confirmPayment(
2160
- dbTransaction: any,
2161
- loginUser: LoginUserBase,
2162
- customer: FinanceCustomerBase,
2163
- payment: Payment,
2164
- status:
2165
- | PaymentStatus.CONFIRMED
2166
- | PaymentStatus.REJECTED
2167
- | PaymentStatus.FAILED,
2168
- remarks: string,
2169
- ctAccountNo?: string,
2170
- receiptNo?: string,
2171
- ): Promise<void> {
2172
- try {
2173
- this._DbTransaction = dbTransaction;
2174
- const systemCode =
2175
- ApplicationConfig.getComponentConfigValue('system-code');
2176
-
2177
- const isPrivileged = await loginUser.checkPrivileges(
2178
- systemCode,
2179
- 'FinanceCompany - Confirm Payment',
2180
- );
2181
- if (!isPrivileged) {
2182
- throw new ClassError(
2183
- 'FinanceCompany',
2184
- 'FinanceCompanyConfirmPaymentErrMsg00',
2185
- `You do not have 'Payment - Confirm' privilege.`,
2186
- );
2187
- }
2188
-
2189
- if (payment.PaymentId === 'New') {
2190
- throw new ClassError(
2191
- 'FinanceCompany',
2192
- 'FinanceCompanyConfirmPaymentErrMsg01',
2193
- `Payment not found.`,
2194
- );
2195
- }
2196
-
2197
- if (payment.Status !== PaymentStatus.PENDING) {
2198
- throw new ClassError(
2199
- 'Payment',
2200
- 'PaymentConfirmPaymentErrMsg02',
2201
- `Payment status is not 'Pending'.`,
2202
- );
2203
- }
2204
-
2205
- payment.Remarks = remarks;
2206
- let receiptDocNo = null;
2207
- switch (status) {
2208
- case PaymentStatus.REJECTED:
2209
- payment.Status = PaymentStatus.REJECTED;
2210
- break;
2211
- case PaymentStatus.FAILED:
2212
- payment.Status = PaymentStatus.FAILED;
2213
- break;
2214
- case PaymentStatus.CONFIRMED:
2215
- payment.Status = PaymentStatus.CONFIRMED;
2216
- //Creating the payment receipt
2217
- /*Generating the receipt*/
2218
- const receiptDocuments: any =
2219
- await FinanceCompany._DocumentRepository.findAll({
2220
- where: {
2221
- DocType: DocType.RECEIPT,
2222
- CompanyId: this.ObjectId,
2223
- },
2224
- });
2225
-
2226
- const receipt = new Document(dbTransaction);
2227
-
2228
- if (receiptNo) {
2229
- receipt.DocNo = receiptNo;
2230
- } else {
2231
- receipt.DocNo = `EZC-RCT-${receiptDocuments.length + 1}`;
2232
- }
2233
- receipt.DocType = DocType.RECEIPT;
2234
- receipt.DocDate = new Date();
2235
- receipt.CompanyId = this.ObjectId;
2236
- receipt.Currency = payment.Currency;
2237
- receipt.Description = 'Payment Received';
2238
- receipt.IssuedById = loginUser.ObjectId;
2239
- receipt.IssuedToId = customer.CustomerId;
2240
- receipt.IssuedToType = type(customer);
2241
- receipt.RelatedObjectId = payment.RelatedObjectId;
2242
- receipt.RelatedObjectType = payment.RelatedObjectType;
2243
- receipt.UseAccSystemDocYN = 'N';
2244
- receipt.CreatedById = loginUser.ObjectId;
2245
- receipt.UpdatedById = loginUser.ObjectId;
2246
-
2247
- const paymentItems = await payment.getPaymentItems(dbTransaction);
2248
- for (const paymentItem of paymentItems) {
2249
- const receiptItem = new DocumentItem(dbTransaction, receipt);
2250
- receiptItem.Name = `Payment for ${paymentItem.PayForObjectType} No. ${paymentItem.PayForObjectId}`;
2251
- receiptItem.NameBM = `Bayaran untuk ${paymentItem.PayForObjectType} No. ${paymentItem.PayForObjectId}`;
2252
- receiptItem.Description = '-';
2253
- receiptItem.Currency = paymentItem.Currency;
2254
- receiptItem.UnitPrice = paymentItem.Amount;
2255
- receiptItem.Quantity = 1;
2256
- receiptItem.Amount = receiptItem.UnitPrice * receiptItem.Quantity;
2257
- receiptItem.ItemId = receipt.DocNo;
2258
- receiptItem.ItemType = type(payment);
2259
- receiptItem.CtAccountNo = ctAccountNo;
2260
-
2261
- await paymentItem.paid(dbTransaction);
2262
- await receipt.newDocumentItem(receiptItem);
2263
- }
2264
-
2265
- const receiptMedia = await receipt.generateReceipt(
2266
- receipt.IssuedById,
2267
- customer,
2268
- dbTransaction,
2269
- );
2270
-
2271
- /*Saving the receipt document and document items to the database*/
2272
- await FinanceCompany._DocumentRepository.create(
2273
- {
2274
- DocNo: receipt.DocNo,
2275
- DocType: receipt.DocType,
2276
- DocDate: receipt.DocDate,
2277
- CompanyId: receipt.CompanyId,
2278
- Currency: receipt.Currency,
2279
- Amount: receipt.Amount,
2280
- Description: receipt.Description,
2281
- Status: receipt.Status,
2282
- IssuedById: receipt.IssuedById,
2283
- IssuedToId: receipt.IssuedToId,
2284
- IssuedToType: receipt.IssuedToType,
2285
- RelatedObjectId: receipt.RelatedObjectId,
2286
- RelatedObjectType: receipt.RelatedObjectType,
2287
- CreatedById: receipt.CreatedById,
2288
- CreatedAt: new Date(),
2289
- UpdatedById: receipt.UpdatedById,
2290
- UpdatedAt: new Date(),
2291
- DocPDFFileMediaId: receiptMedia.PDFMedia.MediaId,
2292
- DocHTMLFileMediaId: receiptMedia.HTMLMedia.MediaId,
2293
- AccSystemRefId: receipt.AccSystemRefId,
2294
- PostedToAccSystemYN: receipt.PostedToAccSystemYN,
2295
- PostedById:
2296
- receipt.PostedToAccSystemYN == 'Y' ? receipt.PostedById : null,
2297
- PostedDateTime: new Date(),
2298
- UseAccSystemDocYN: receipt.UseAccSystemDocYN,
2299
- },
2300
- {
2301
- transaction: dbTransaction,
2302
- },
2303
- );
2304
-
2305
- const receiptItems = await receipt.getDocumentItems(dbTransaction);
2306
-
2307
- for (const receiptItem of receiptItems) {
2308
- await FinanceCompany._DocumentItemRepository.create(
2309
- {
2310
- DocumentItemId: this._createId().toUpperCase(),
2311
- DocNo: receipt.DocNo,
2312
- Name: receiptItem.Name,
2313
- NameBM: receiptItem.NameBM,
2314
- Description: receiptItem.Description,
2315
- ItemId: receiptItem.ItemId,
2316
- ItemType: receiptItem.ItemType,
2317
- ItemSKU: receiptItem.ItemSKU,
2318
- ItemSerialNo: receiptItem.ItemSerialNo,
2319
- Currency: receiptItem.Currency,
2320
- UnitPrice: receiptItem.UnitPrice,
2321
- Quantity: receiptItem.Quantity,
2322
- QuantityUOM: receiptItem.QuantityUOM,
2323
- Amount: receiptItem.Amount,
2324
- TaxCode: receiptItem.TaxCode,
2325
- TaxAmount: receiptItem.TaxAmount,
2326
- TaxRate: receiptItem.TaxRate,
2327
- TaxInclusiveYN: receiptItem.TaxInclusiveYN,
2328
- DtAccountNo: receiptItem.DtAccountNo
2329
- ? receiptItem.DtAccountNo
2330
- : null,
2331
- CtAccountNo: receiptItem.CtAccountNo
2332
- ? receiptItem.CtAccountNo
2333
- : null,
2334
- },
2335
- {
2336
- transaction: dbTransaction,
2337
- },
2338
- );
2339
- }
2340
- //Set the receipt doc no to the payment
2341
- payment.ReceiptDocNo = receipt.DocNo;
2342
- receiptDocNo = receipt.DocNo;
2343
-
2344
- /*Registering the Journal Entries for the transaction*/
2345
- const transactionDate = new Date();
2346
- const paymentPaidWithItems = await payment.getPaymentPaidWith(
2347
- dbTransaction,
2348
- );
2349
-
2350
- for (const paymentPaidWith of paymentPaidWithItems) {
2351
- let paymentMethodType = await PaymentMethodType.initMethodType(
2352
- dbTransaction,
2353
- paymentPaidWith.MethodTypeId,
2354
- );
2355
-
2356
- const journalEntry = new JournalEntry(dbTransaction);
2357
-
2358
- journalEntry.init({
2359
- CompanyId: this.CompanyId,
2360
- Name: 'Collect-payments for ' + payment.PaymentId,
2361
- });
2362
-
2363
- const debitLT = await journalEntry.newLedgerTransaction(
2364
- TransactionTypeOptions.DEBIT,
2365
- );
2366
- debitLT.AccountNo = paymentMethodType.AccountNo;
2367
- debitLT.Currency = paymentPaidWith.Currency;
2368
- debitLT.DebitAmount = paymentPaidWith.Amount;
2369
- debitLT.Date = transactionDate;
2370
- debitLT.Description = 'Payment Received'; //customer.FullName;
2371
- debitLT.Name = customer.FullName;
2372
- debitLT.RelatedObjectId = payment.PaymentId;
2373
- debitLT.RelatedObjectType = 'Payment';
2374
- debitLT.RelatedPaymentId = payment.PaymentId;
2375
-
2376
- const creditLT = await journalEntry.newLedgerTransaction(
2377
- TransactionTypeOptions.CREDIT,
2378
- );
2379
-
2380
- if (ctAccountNo) {
2381
- creditLT.AccountNo = ctAccountNo;
2382
- } else {
2383
- const arAccount = await customer.getAccountReceivable();
2384
- creditLT.AccountNo = arAccount.AccountNo;
2385
- }
2386
- creditLT.Currency = paymentPaidWith.Currency;
2387
- creditLT.CreditAmount = paymentPaidWith.Amount;
2388
- creditLT.Date = transactionDate;
2389
- creditLT.Description = 'Payment Received'; //paymentMethodType.Name;
2390
- creditLT.Name = paymentMethodType.Name;
2391
- creditLT.RelatedObjectId = payment.PaymentId;
2392
- creditLT.RelatedObjectType = type(payment);
2393
- creditLT.RelatedPaymentId = payment.PaymentId;
2394
-
2395
- await this.postJournal(dbTransaction, journalEntry, loginUser);
2396
- }
2397
- break;
2398
- default:
2399
- throw new ClassError(
2400
- 'FinanceCompany',
2401
- 'FinanceCompanyConfirmPaymentErrMsg03',
2402
- `Invalid status.`,
2403
- );
2404
- }
2405
-
2406
- await FinanceCompany._PaymentRepository.update(
2407
- {
2408
- ReceiptDocNo: receiptDocNo,
2409
- Status: payment.Status,
2410
- UpdatedAt: new Date(),
2411
- UpdatedBy: loginUser.ObjectId,
2412
- },
2413
- {
2414
- where: {
2415
- PaymentId: payment.PaymentId,
2416
- },
2417
- transaction: dbTransaction,
2418
- },
2419
- );
2420
- } catch (error) {
2421
- throw error;
2422
- }
2423
- }
2424
- }
1
+ import axios from 'axios';
2
+ import {
3
+ ClassError,
4
+ HashTable,
5
+ LoginUserBase,
6
+ ObjectBase,
7
+ RecordNotFoundError,
8
+ } from '@tomei/general';
9
+ import Account from '../account/account';
10
+ import JournalEntry from '../journal-entry/journal-entry';
11
+ import FinanceCustomerBase from '../customer/customer';
12
+ import Document from '../document/document';
13
+ import { IAccountSystem } from '../interfaces';
14
+ import { FinanceCompanyRepository } from './finance-company.repository';
15
+ import { FinanceCustomerRepository } from '../customer/finance-customer.repository';
16
+ import { LedgerTransactionRepository } from '../ledger-transaction/ledger-transaction.repository';
17
+ import {
18
+ DocType,
19
+ DocumentStatus,
20
+ PaymentStatus,
21
+ PaymentType,
22
+ TransactionTypeOptions,
23
+ } from '../enum';
24
+ import PaymentMethodType from '../payment-method-type/payment-method-type';
25
+ import { PaymentRepository } from '../payment/payment.repository';
26
+ import { PaymentItemRepository } from '../payment-item/payment-item.repository';
27
+ import Payment from '../payment/payment';
28
+ import { DocumentRepository } from '../document/document.repository';
29
+ import { DocumentItemRepository } from '../document/document-item.repository';
30
+ import { PaymentMethodRepository } from '../payment-method/payment-method.repository';
31
+ import { PaymentMethodTypeRepository } from '../payment-method-type/payment-method-type.repository';
32
+ import PaymentMethod from '../payment-method/payment-method';
33
+ import { AccountRepository } from '../account/account.repository';
34
+ import { PaymentPaidWithRepository } from '../payment-paid-with/payment-paid-with.repository';
35
+ import { MediasModel } from '@tomei/media';
36
+ import DocumentItem from '../document/document-item';
37
+ import { type } from '../helpers/typeof';
38
+ import { LoginUser } from '@tomei/sso';
39
+ import { ActionEnum, Activity } from '@tomei/activity-history';
40
+ import { ApplicationConfig, ComponentConfig } from '@tomei/config';
41
+ import { TaxRepository } from '../tax/tax.repository';
42
+ import { Tax } from '../tax/tax';
43
+ import { CollectPaymentType } from '../enum/collect-payment-type';
44
+ import FinanceCustomerModel from 'src/models/customer.entity';
45
+
46
+ export default class FinanceCompany extends ObjectBase {
47
+ private _CompanyId = 'New';
48
+ private _CompSystemCode = '';
49
+ private _CompSystemRefId = '';
50
+ private _AccSystemCode = '';
51
+ private _AccSystemRefId = 'REF';
52
+ private _PostedToAccSystemYN = 'N';
53
+ private _PostedById = null;
54
+ private _PostedDateTime = null;
55
+ private _ObjectType = 'FinanceCompany';
56
+
57
+ private static _htFinanceCompanyIds = new HashTable();
58
+ private static _htFinanceCompanies = new HashTable();
59
+ private static _financeCompanyRepository = new FinanceCompanyRepository();
60
+ private static _PaymentRepository = new PaymentRepository();
61
+ private static _PaymentItemRepository = new PaymentItemRepository();
62
+ private static _PaymentPaidWithRepository = new PaymentPaidWithRepository();
63
+ private static _PaymentMethodRepository = new PaymentMethodRepository();
64
+ private static _PaymentMethodTypeRepository =
65
+ new PaymentMethodTypeRepository();
66
+ private static _DocumentRepository = new DocumentRepository();
67
+ private static _DocumentItemRepository = new DocumentItemRepository();
68
+ private static _FinanceCustomerRepository = new FinanceCustomerRepository();
69
+ private static _LedgerTransactionRepository =
70
+ new LedgerTransactionRepository();
71
+
72
+ private static _AccountRepository = new AccountRepository();
73
+ private static _TaxRepository = new TaxRepository();
74
+
75
+ private _AccountingSystem: IAccountSystem;
76
+
77
+ private _DbTransaction: any;
78
+ private _PaymentMethods = [];
79
+ private _Taxes: Tax[] = [];
80
+
81
+ get ObjectType() {
82
+ return this._ObjectType;
83
+ }
84
+
85
+ get CompSystemCode(): string {
86
+ return this._CompSystemCode;
87
+ }
88
+
89
+ private set CompSystemCode(code: string) {
90
+ this._CompSystemCode = code;
91
+ }
92
+
93
+ get CompSystemRefId() {
94
+ return this._CompSystemRefId;
95
+ }
96
+
97
+ set CompSystemRefId(id: string) {
98
+ this._CompSystemRefId = id;
99
+ }
100
+
101
+ get AccSystemCode() {
102
+ return this._AccSystemCode;
103
+ }
104
+
105
+ get AccSystemRefId() {
106
+ return this._AccSystemRefId;
107
+ }
108
+
109
+ set AccSystemRefId(id: string) {
110
+ this._AccSystemRefId = id;
111
+ }
112
+
113
+ get PostedToAccSystemYN() {
114
+ return this._PostedToAccSystemYN;
115
+ }
116
+
117
+ set PostedToAccSystemYN(value: string) {
118
+ this._PostedToAccSystemYN = value;
119
+ }
120
+
121
+ get PostedById() {
122
+ return this._PostedById;
123
+ }
124
+
125
+ set PostedById(id: string) {
126
+ this._PostedById = id;
127
+ }
128
+
129
+ get PostedDateTime() {
130
+ return this._PostedDateTime;
131
+ }
132
+
133
+ set PostedDateTime(date: any) {
134
+ this._PostedDateTime = date;
135
+ }
136
+
137
+ private set AccSystemCode(code: string) {
138
+ this._AccSystemCode = code;
139
+ }
140
+
141
+ get CompanyId() {
142
+ return this._CompanyId;
143
+ }
144
+
145
+ get ObjectId() {
146
+ return this._CompanyId;
147
+ }
148
+
149
+ private set ObjectId(id: string) {
150
+ this._CompanyId = id;
151
+ }
152
+
153
+ get ObjectName() {
154
+ return `${this.CompSystemCode}-${this.CompSystemRefId}-${this.AccSystemCode}`;
155
+ }
156
+
157
+ get TableName() {
158
+ return 'finance_Company';
159
+ }
160
+
161
+ get AccountingSystem(): IAccountSystem {
162
+ return this._AccountingSystem;
163
+ }
164
+
165
+ set AccountingSystem(system: IAccountSystem) {
166
+ this._AccountingSystem = system;
167
+ }
168
+
169
+ constructor(
170
+ compSystemCode: string,
171
+ compSystemRefId: string,
172
+ accSystemCode: string,
173
+ ) {
174
+ super();
175
+ this.CompSystemCode = compSystemCode;
176
+ this.CompSystemRefId = compSystemRefId;
177
+ this.AccSystemCode = accSystemCode;
178
+
179
+ this.AccSystemRefId = 'REF';
180
+ this.PostedToAccSystemYN = 'N';
181
+ }
182
+
183
+ static async getFinanceCompanyId(
184
+ compSystemCode: string,
185
+ compSystemRefId: string,
186
+ accSystemCode: string,
187
+ ): Promise<string> {
188
+ let sCompanyId = '';
189
+ /*Assemble the hashtable key*/
190
+ const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
191
+ /*Check if the FinanceCompany has previously being loaded*/
192
+ if (!FinanceCompany._htFinanceCompanyIds.get(sKey)) {
193
+ /*Instantiate a new FinanceCompany*/
194
+ const financeCompany = new FinanceCompany(
195
+ compSystemCode,
196
+ compSystemRefId,
197
+ accSystemCode,
198
+ );
199
+
200
+ /*Retrieve the finance company from finance_Company table using compSystemCode,
201
+ * CompSystemRefId and accSystemCode */
202
+ const company = await FinanceCompany._financeCompanyRepository.findOne({
203
+ where: {
204
+ CompSystemCode: compSystemCode,
205
+ CompSystemRefId: compSystemRefId,
206
+ AccSystemCode: accSystemCode,
207
+ },
208
+ });
209
+
210
+ /*Retrieve and store the companyId from the result*/
211
+ financeCompany.ObjectId = company.CompanyId;
212
+ sCompanyId = financeCompany.ObjectId;
213
+
214
+ /*Add the details into the hashtable*/
215
+ FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
216
+ FinanceCompany._htFinanceCompanies.add(sCompanyId, financeCompany);
217
+ }
218
+
219
+ if (typeof FinanceCompany._htFinanceCompanyIds.get(sKey) === 'string') {
220
+ sCompanyId = FinanceCompany._htFinanceCompanyIds.get(sKey);
221
+ }
222
+
223
+ return sCompanyId;
224
+ }
225
+
226
+ static async getFinanceCompany(companyId: string): Promise<FinanceCompany> {
227
+ /*Check if the finance company is previously be loaded*/
228
+ if (!FinanceCompany._htFinanceCompanies.get(companyId)) {
229
+ /*Retrieve the finance company from finance_Company table using compSystemCode,
230
+ * CompSystemRefId and accSystemCode */
231
+ const company = await FinanceCompany._financeCompanyRepository.findOne({
232
+ where: { CompanyId: companyId },
233
+ });
234
+
235
+ if (!company) {
236
+ throw Error('No finance company found. Please create first.');
237
+ }
238
+
239
+ /*Using the result returned, instantiate a new FinanceCompany*/
240
+ const compSystemCode = company.CompSystemCode;
241
+ const compSystemRefId = company.CompSystemRefId;
242
+ const accSystemCode = company.AccSystemCode;
243
+
244
+ const financeCompany = new FinanceCompany(
245
+ compSystemCode,
246
+ compSystemRefId,
247
+ accSystemCode,
248
+ );
249
+
250
+ /*Retrieve and store the CompanyId from the result*/
251
+ financeCompany.ObjectId = company.CompanyId;
252
+ financeCompany._CompanyId = company.CompanyId;
253
+
254
+ /*Add the details into the hashtable*/
255
+ const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
256
+ FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
257
+ FinanceCompany._htFinanceCompanies.add(
258
+ financeCompany.ObjectId,
259
+ financeCompany,
260
+ );
261
+ }
262
+ // tslint:disable-next-line:no-console
263
+ console.log('return from hash table');
264
+ return FinanceCompany._htFinanceCompanies.get(companyId);
265
+ }
266
+
267
+ static async createFinanceCompany(
268
+ dbTransaction: any,
269
+ loginUser: LoginUserBase,
270
+ companyId: string,
271
+ compSystemCode: string,
272
+ compSystemRefId: string,
273
+ accSystemCode: string,
274
+ ) {
275
+ /*Instantiate a new FinanceCompany*/
276
+ const financeCompany = new FinanceCompany(
277
+ compSystemCode,
278
+ compSystemRefId,
279
+ accSystemCode,
280
+ );
281
+
282
+ /*Validating if the finance company already exists in finance_Company*/
283
+ const company = await FinanceCompany._financeCompanyRepository.findOne({
284
+ where: {
285
+ CompSystemCode: compSystemCode,
286
+ CompSystemRefId: compSystemRefId,
287
+ AccSystemCode: accSystemCode,
288
+ },
289
+ });
290
+
291
+ if (company) {
292
+ throw Error(
293
+ 'There is already another Finance Company with the compSystemCode, CompSystemRefId and accSystemCode specified.',
294
+ );
295
+ }
296
+
297
+ /*Generating the companyId*/
298
+ financeCompany.ObjectId = companyId;
299
+ const accSystemRefId = ComponentConfig.getComponentConfigValue(
300
+ 'finance-config.json',
301
+ 'accountSystemRefId',
302
+ );
303
+
304
+ const postedToAccSystemYN = ComponentConfig.getComponentConfigValue(
305
+ 'finance-config.json',
306
+ 'postedToAccSystemYN',
307
+ );
308
+
309
+ financeCompany.PostedById = loginUser.ObjectId || null;
310
+ financeCompany.PostedDateTime = new Date();
311
+
312
+ /*Save the FinanceCompany to the finance_Company table*/
313
+ await FinanceCompany._financeCompanyRepository.create(
314
+ {
315
+ CompanyId: financeCompany.CompanyId,
316
+ CompSystemCode: financeCompany.CompSystemCode,
317
+ CompSystemRefId: financeCompany.CompSystemRefId,
318
+ AccSystemCode: financeCompany.AccSystemCode,
319
+ AccSystemRefId:
320
+ postedToAccSystemYN === 'Y'
321
+ ? accSystemRefId
322
+ : financeCompany.AccSystemRefId,
323
+ PostedToAccSystemYN:
324
+ postedToAccSystemYN || financeCompany.PostedToAccSystemYN,
325
+ PostedById: financeCompany.PostedById,
326
+ PostedDateTime: financeCompany.PostedDateTime,
327
+ },
328
+ {
329
+ transaction: dbTransaction,
330
+ },
331
+ );
332
+
333
+ /*Add the details to hashtable*/
334
+ const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
335
+ FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
336
+ FinanceCompany._htFinanceCompanies.add(
337
+ financeCompany.ObjectId,
338
+ financeCompany,
339
+ );
340
+
341
+ // tslint:disable-next-line:no-console
342
+ console.log('return from hash table');
343
+ return FinanceCompany._htFinanceCompanies.get(companyId);
344
+ }
345
+
346
+ static async findAccount(
347
+ loginUser: LoginUserBase,
348
+ dbTransaction: any,
349
+ accountNo: string,
350
+ ): Promise<Account> {
351
+ try {
352
+ //Part 1: Privilege Check
353
+ //Check if the user has the privilege to view the account
354
+ const systemCode =
355
+ ApplicationConfig.getComponentConfigValue('system-code');
356
+ const isPrivileged = await loginUser.checkPrivileges(
357
+ systemCode,
358
+ 'FINANCECOMPANY_VIEW_ACCOUNT',
359
+ );
360
+
361
+ //If the user is not privileged, throw an error
362
+ if (!isPrivileged) {
363
+ throw new ClassError(
364
+ 'FinanceCompany',
365
+ 'findAccountErrMsg0X',
366
+ 'User not privileged to view finance company account',
367
+ );
368
+ }
369
+
370
+ //Part 2: Find Account
371
+ //Find the account using the accountNo
372
+ const record = await FinanceCompany._AccountRepository.findOne({
373
+ where: {
374
+ AccountNo: accountNo,
375
+ },
376
+ transaction: dbTransaction,
377
+ });
378
+
379
+ if (record) {
380
+ const account = new Account(dbTransaction);
381
+ account.init(record.get({ plain: true }));
382
+ return account;
383
+ }
384
+ return undefined;
385
+ } catch (error) {
386
+ throw error;
387
+ }
388
+ }
389
+
390
+ async createCustomer<T extends FinanceCustomerBase>(
391
+ dbTransaction: any,
392
+ custSystemCode: string,
393
+ custSystemRefId: string,
394
+ customer: T,
395
+ loginUser: LoginUser,
396
+ ) {
397
+ try {
398
+ if (!custSystemCode || !custSystemRefId) {
399
+ throw new Error(
400
+ 'CustSystemCode and CustomerRefId are required fields.',
401
+ );
402
+ }
403
+
404
+ const financeCustomerData =
405
+ await FinanceCompany._FinanceCustomerRepository.findOne({
406
+ where: {
407
+ CompanyId: this._CompanyId,
408
+ CustSystemCode: custSystemCode,
409
+ CustSystemRefId: custSystemRefId,
410
+ },
411
+ });
412
+
413
+ if (financeCustomerData) {
414
+ throw new Error('Customer already created previously.');
415
+ }
416
+
417
+ let AccCustomerRefId = 'REF';
418
+ if (this.AccountingSystem) {
419
+ AccCustomerRefId = await this.AccountingSystem.createCustomer(
420
+ customer,
421
+ dbTransaction,
422
+ );
423
+ }
424
+
425
+ customer.CompanyId = this._CompanyId;
426
+
427
+ const newCustomer = await customer.save(
428
+ AccCustomerRefId,
429
+ custSystemCode,
430
+ custSystemRefId,
431
+ dbTransaction,
432
+ );
433
+
434
+ const activity = new Activity();
435
+ activity.ActivityId = this._createId().toUpperCase();
436
+ activity.Action = ActionEnum.ADD;
437
+ activity.Description = 'Add Finance Customer';
438
+ activity.EntityType = 'FinanceCustomer';
439
+ activity.EntityId = newCustomer.CustomerId;
440
+ activity.EntityValueBefore = JSON.stringify({});
441
+ activity.EntityValueAfter = JSON.stringify(newCustomer);
442
+
443
+ await activity.create(loginUser.ObjectId, dbTransaction);
444
+
445
+ return customer;
446
+ } catch (error) {
447
+ throw error;
448
+ }
449
+ }
450
+
451
+ async postJournal(
452
+ dbTransaction: any,
453
+ journalEntry: JournalEntry,
454
+ loginUser: LoginUserBase,
455
+ ) {
456
+ const debitTransactions = await journalEntry.DebitTransactions;
457
+ const creditTransactions = await journalEntry.CreditTransactions;
458
+ try {
459
+ if (creditTransactions.length < 1 || debitTransactions?.length < 1) {
460
+ throw new Error(
461
+ 'There should be at least 1 debit ledger transaction and 1 credit ledger transaction in the journal entry',
462
+ );
463
+ }
464
+
465
+ let totalCreditAmount = creditTransactions.reduce(
466
+ (accumulator, currentValue) => accumulator + currentValue.CreditAmount,
467
+ 0,
468
+ );
469
+ let totalDebitAmount = debitTransactions.reduce(
470
+ (accumulator, currentValue) => accumulator + currentValue.DebitAmount,
471
+ 0,
472
+ );
473
+ console.log('totalCreditAmount: ', totalCreditAmount);
474
+ console.log('totalDebitAmount: ', totalDebitAmount);
475
+
476
+ if (typeof totalCreditAmount === 'string') {
477
+ totalCreditAmount = parseFloat(totalCreditAmount);
478
+ }
479
+
480
+ if (typeof totalDebitAmount === 'string') {
481
+ totalDebitAmount = parseFloat(totalDebitAmount);
482
+ }
483
+ if (totalCreditAmount.toFixed(2) !== totalDebitAmount.toFixed(2)) {
484
+ throw new Error(
485
+ 'Credit ledger transaction and debit ledger transaction should the same amount',
486
+ );
487
+ }
488
+
489
+ const newJournalEntry = await journalEntry.save(
490
+ loginUser.ObjectId,
491
+ dbTransaction,
492
+ );
493
+
494
+ for (const ledgerTransaction of debitTransactions) {
495
+ ledgerTransaction.JournalEntryId = newJournalEntry.JournalEntryId;
496
+
497
+ await ledgerTransaction.save(dbTransaction);
498
+ // const dt = await ledgerTransaction.newLedgerTransaction(
499
+ // TransactionTypeOptions.DEBIT,
500
+ // newJournalEntry.JournalEntryId,
501
+ // );
502
+ // await dt.save();
503
+ }
504
+
505
+ for (const ledgerTransaction of creditTransactions) {
506
+ ledgerTransaction.JournalEntryId = newJournalEntry.JournalEntryId;
507
+
508
+ await ledgerTransaction.save(dbTransaction);
509
+ // const ct = await ledgerTransaction.newLedgerTransaction(
510
+ // TransactionTypeOptions.CREDIT,
511
+ // newJournalEntry.JournalEntryId,
512
+ // );
513
+ // await ct.save();
514
+ }
515
+
516
+ // await this.AccountingSystem.postJournalEntry(newJournalEntry);
517
+
518
+ const payload = {
519
+ Action: 'Create',
520
+ Activity: 'Post Journal Entry',
521
+ Description: `Journal Entry (ID: ${newJournalEntry.JournalEntryId}) has been created`,
522
+ EntityType: 'JournalEntry',
523
+ EntityValueBefore: JSON.stringify({}),
524
+ EntityValueAfter: JSON.stringify(newJournalEntry),
525
+ PerformedById: loginUser.ObjectId,
526
+ PerformedAt: new Date(),
527
+ EntityId: newJournalEntry.JournalEntryId,
528
+ };
529
+
530
+ await axios.post(
531
+ `${process.env.COMMON_API_URL}/activity-histories`,
532
+ payload,
533
+ );
534
+ } catch (error) {
535
+ throw error;
536
+ }
537
+ }
538
+
539
+ async createAccount(
540
+ dbTransaction: any,
541
+ account: Account,
542
+ loginUser: LoginUserBase,
543
+ ) {
544
+ try {
545
+ let accSystemRefId: string;
546
+
547
+ if (!account.AccountType) {
548
+ throw new Error('AccountType is required.');
549
+ }
550
+
551
+ // Retrieve & validate accountNoLength from finance config file
552
+ const accountNoLength = ComponentConfig.getComponentConfigValue(
553
+ '@tomei/finance',
554
+ 'accountNoLength',
555
+ );
556
+
557
+ if (accountNoLength && account.AccountNo.length > accountNoLength) {
558
+ throw new Error(
559
+ `Account No length should not exceed ${accountNoLength} characters.`,
560
+ );
561
+ }
562
+
563
+ let createAccountPayload: any = {
564
+ Name: account.Name,
565
+ AcctNum: account.AccountNo,
566
+ AccountType: account.AccountType,
567
+ AccountSubType: account.AccountSubtype,
568
+ };
569
+
570
+ if (this.AccountingSystem) {
571
+ accSystemRefId = await this.AccountingSystem.createAccount(
572
+ account,
573
+ dbTransaction,
574
+ );
575
+ }
576
+
577
+ if (account.isParentAccountExists()) {
578
+ createAccountPayload = {
579
+ ...createAccountPayload,
580
+ CurrencyRef: 'MYR',
581
+ ParentRef: account.ParentAccountNo,
582
+ SubAccount: true,
583
+ };
584
+ }
585
+
586
+ console.log(
587
+ 'Finance Company Create Account: Before accSystemAccountId Create',
588
+ );
589
+
590
+ console.log(
591
+ 'Finance Company Create Account: After accSystemAccountId Create',
592
+ );
593
+
594
+ console.log('Finance Company Create Account: Before new Account Create');
595
+
596
+ if (accSystemRefId) {
597
+ account.PostedToAccSystemYN = 'Y';
598
+ account.PostedById = loginUser.ObjectId;
599
+ account.PostedDateTime = new Date();
600
+ }
601
+
602
+ const newAccount = await account.save(
603
+ this.CompanyId,
604
+ accSystemRefId || 'REF',
605
+ loginUser.ObjectId,
606
+ dbTransaction,
607
+ );
608
+
609
+ console.log('Finance Company Create Account: After new Account Create');
610
+
611
+ const payload = {
612
+ Action: 'Create',
613
+ Activity: 'Account Created',
614
+ Description: `Account (ID: ${newAccount.AccountNo}) has been created`,
615
+ EntityType: 'Account',
616
+ EntityValueBefore: JSON.stringify({}),
617
+ EntityValueAfter: JSON.stringify(newAccount),
618
+ PerformedById: loginUser.ObjectId,
619
+ PerformedAt: new Date(),
620
+ EntityId: newAccount.AccountNo,
621
+ };
622
+
623
+ await axios.post(
624
+ `${process.env.COMMON_API_URL}/activity-histories`,
625
+ payload,
626
+ );
627
+
628
+ return account;
629
+ } catch (error) {
630
+ throw error;
631
+ }
632
+ }
633
+
634
+ /**
635
+ * Issue an invoice
636
+ *
637
+ * @param dbTransaction - The database transaction to be used
638
+ * @param loginUser - The user issuing the invoice
639
+ * @param invoice - The document containing the invoice details
640
+ * @param customer - The customer to be issued the invoice
641
+ * @param dtAccountNo - The account number of the Account Receivable (AR) account to debit. If not provided, will debit the customer's default AR account
642
+ *
643
+ * @returns {Document} - Document object representing the full details of the invoice issued
644
+ */
645
+ async issueInvoice<C extends FinanceCustomerBase>(
646
+ /*todo: loginUser & customer is NOT supposed to be optional */
647
+ dbTransaction: any,
648
+ invoice: Document,
649
+ loginUser?: LoginUserBase,
650
+ customer?: C,
651
+ dtAccountNo?: string,
652
+ ): Promise<Document> {
653
+ try {
654
+ /*Check if the invoice number already exists (should be unique)*/
655
+ const duplicateInvoice = await FinanceCompany._DocumentRepository.findOne(
656
+ {
657
+ where: {
658
+ DocNo: invoice.DocNo,
659
+ },
660
+ transaction: dbTransaction,
661
+ },
662
+ );
663
+
664
+ if (duplicateInvoice) {
665
+ throw new Error('Invoice number already exists');
666
+ }
667
+
668
+ const documentItems = await invoice.getDocumentItems(dbTransaction);
669
+
670
+ /*Check if the document has at least 1 document item*/
671
+ if (!documentItems.length) {
672
+ throw new Error('Document must have at least 1 document item');
673
+ }
674
+
675
+ /*Check if each document item has CtAccountNo provided*/
676
+ for (const invoiceItem of documentItems) {
677
+ if (!invoiceItem.CtAccountNo) {
678
+ throw new Error(
679
+ 'Each document item should have CtAccountNo provided',
680
+ );
681
+ }
682
+ }
683
+
684
+ /*Set up the document type*/
685
+ invoice.DocType = DocType.INVOICE;
686
+
687
+ /*Generating the invoice*/
688
+ let invoiceMedia: {
689
+ HTMLMedia: MediasModel;
690
+ PDFMedia: MediasModel;
691
+ };
692
+
693
+ if (invoice.UseAccSystemDocYN === 'Y') {
694
+ /*todo: Posting to accounting system to generate invoice*/
695
+ const accInvoiceRefIdDetail = await this.AccountingSystem.createInvoice(
696
+ {
697
+ customer,
698
+ invoice,
699
+ paymentMode: 'credit',
700
+ },
701
+ dbTransaction,
702
+ );
703
+ invoice.DocNo = accInvoiceRefIdDetail.invoiceNo;
704
+ invoice.AccSystemRefId = accInvoiceRefIdDetail.invoiceRefId;
705
+ invoice.PostedToAccSystemYN = 'Y';
706
+ invoice.PostedById = loginUser.ObjectId;
707
+ invoice.PostedDateTime = new Date();
708
+
709
+ //for each document item, insert the document item accounting reference id that have matching name
710
+ for (const documentItem of documentItems) {
711
+ const accItem = accInvoiceRefIdDetail.invoiceItems.find(
712
+ (item) => item.name === documentItem.Name,
713
+ );
714
+ if (accItem) {
715
+ documentItem.DocNo = accInvoiceRefIdDetail.invoiceNo;
716
+ documentItem.AccSystemRefId = accItem.itemRefId;
717
+ documentItem.PostedToAccSystemYN = 'Y';
718
+ documentItem.PostedById = loginUser.ObjectId;
719
+ documentItem.PostedDateTime = new Date();
720
+ }
721
+ }
722
+ } else {
723
+ /*todo: check config file to see which invoice template is to be used for specific project*/
724
+
725
+ /*Generating invoice based on template*/
726
+ invoiceMedia = await invoice.generateInvoice(
727
+ invoice.IssuedById,
728
+ customer,
729
+ dbTransaction,
730
+ );
731
+ }
732
+
733
+ /*Saving the document and document items to the database*/
734
+ await FinanceCompany._DocumentRepository.create(
735
+ {
736
+ DocNo: invoice.DocNo,
737
+ DocType: invoice.DocType,
738
+ DocDate: invoice.DocDate,
739
+ CompanyId: invoice.CompanyId,
740
+ Currency: invoice.Currency,
741
+ Amount: invoice.Amount,
742
+ Description: invoice.Description,
743
+ Status: invoice.Status,
744
+ IssuedById: invoice.IssuedById,
745
+ IssuedToId: invoice.IssuedToId,
746
+ IssuedToType: invoice.IssuedToType,
747
+ RelatedObjectId: invoice.RelatedObjectId,
748
+ RelatedObjectType: invoice.RelatedObjectType,
749
+ CreatedById: invoice.CreatedById,
750
+ CreatedAt: new Date(),
751
+ UpdatedById: invoice.UpdatedById,
752
+ UpdatedAt: new Date(),
753
+ DocPDFFileMediaId:
754
+ invoice.UseAccSystemDocYN == 'N'
755
+ ? invoiceMedia.PDFMedia.MediaId
756
+ : null,
757
+ DocHTMLFileMediaId:
758
+ invoice.UseAccSystemDocYN == 'N'
759
+ ? invoiceMedia.HTMLMedia.MediaId
760
+ : null,
761
+ AccSystemRefId: invoice.AccSystemRefId,
762
+ PostedToAccSystemYN: invoice.PostedToAccSystemYN,
763
+ PostedById:
764
+ invoice.UseAccSystemDocYN == 'Y' ? invoice.PostedById : null,
765
+ PostedDateTime: new Date(),
766
+ UseAccSystemDocYN: invoice.UseAccSystemDocYN,
767
+ },
768
+ {
769
+ transaction: dbTransaction,
770
+ },
771
+ );
772
+
773
+ for (const documentItem of documentItems) {
774
+ await FinanceCompany._DocumentItemRepository.create(
775
+ {
776
+ DocumentItemId: this._createId().toUpperCase(),
777
+ DocNo: invoice.DocNo,
778
+ Name: documentItem.Name,
779
+ NameBM: documentItem.NameBM,
780
+ Description: documentItem.Description,
781
+ ItemId: documentItem.ItemId,
782
+ ItemType: documentItem.ItemType,
783
+ ItemSKU: documentItem.ItemSKU,
784
+ ItemSerialNo: documentItem.ItemSerialNo,
785
+ Currency: documentItem.Currency,
786
+ UnitPrice: documentItem.UnitPrice,
787
+ Quantity: documentItem.Quantity,
788
+ QuantityUOM: documentItem.QuantityUOM,
789
+ Amount: documentItem.Amount,
790
+ TaxCode: documentItem.TaxCode,
791
+ TaxAmount: documentItem.TaxAmount,
792
+ TaxRate: documentItem.TaxRate,
793
+ TaxInclusiveYN: documentItem.TaxInclusiveYN,
794
+ DtAccountNo: documentItem.DtAccountNo
795
+ ? documentItem.DtAccountNo
796
+ : null,
797
+ CtAccountNo: documentItem.CtAccountNo
798
+ ? documentItem.CtAccountNo
799
+ : null,
800
+ AccSystemRefId: documentItem.AccSystemRefId,
801
+ PostedToAccSystemYN: documentItem.PostedToAccSystemYN,
802
+ PostedById: documentItem.PostedById,
803
+ PostedDateTime: documentItem.PostedDateTime,
804
+ },
805
+ {
806
+ transaction: dbTransaction,
807
+ },
808
+ );
809
+ }
810
+
811
+ const transactionDate = new Date();
812
+ const htCreditAccountAmount = new HashTable();
813
+ const htCreditAccountCurrency = new HashTable();
814
+ const htCreditAccountPurpose = new HashTable();
815
+
816
+ documentItems.forEach((invoiceItem) => {
817
+ if (!htCreditAccountAmount.exists(invoiceItem.CtAccountNo)) {
818
+ //add the credit account to the hash table
819
+ htCreditAccountAmount.add(
820
+ invoiceItem.CtAccountNo,
821
+ invoiceItem.Amount,
822
+ );
823
+
824
+ htCreditAccountCurrency.add(
825
+ invoiceItem.CtAccountNo,
826
+ invoiceItem.Currency,
827
+ );
828
+
829
+ htCreditAccountPurpose.add(invoiceItem.CtAccountNo, invoiceItem.Name);
830
+ } else {
831
+ //update the credit account amount
832
+ const d = htCreditAccountAmount.get(invoiceItem.CtAccountNo);
833
+ htCreditAccountAmount.add(
834
+ invoiceItem.CtAccountNo,
835
+ d + invoiceItem.Amount,
836
+ );
837
+ }
838
+ });
839
+
840
+ const savedItems = htCreditAccountAmount.list();
841
+
842
+ for (const item of savedItems) {
843
+ const journalEntry = new JournalEntry(dbTransaction);
844
+ //Temporary fix to successfully save journal entry in PostJournal
845
+ journalEntry.init({
846
+ CompanyId: this.CompanyId,
847
+ Name: 'Issue Invoice ' + invoice.DocNo,
848
+ });
849
+ // const account = await Account.initAccount(dbTransaction, item.value[0]);
850
+ const creditAmount = item.value[1];
851
+ const currency = htCreditAccountCurrency.get(item.value[0]);
852
+ const purpose = htCreditAccountPurpose.get(item.value[0]);
853
+
854
+ const dt = await journalEntry.newLedgerTransaction(
855
+ TransactionTypeOptions.DEBIT,
856
+ );
857
+
858
+ if (dtAccountNo) {
859
+ /*Transacting using AR account provided*/
860
+ dt.AccountNo = dtAccountNo;
861
+ } else {
862
+ /*Transacting based on default customer AR account*/
863
+ const arAccount = await customer.getAccountReceivable();
864
+ dt.AccountNo = arAccount.AccountNo;
865
+ }
866
+
867
+ dt.Currency = currency ? currency : 'MYR';
868
+ dt.DebitAmount = creditAmount ? creditAmount : 0.0;
869
+ dt.Date = transactionDate;
870
+ dt.Description = `${purpose}`;
871
+ dt.Name = `${purpose}`;
872
+ dt.RelatedDocNo = invoice.DocNo;
873
+ dt.RelatedObjectId = invoice.RelatedObjectId;
874
+ dt.RelatedObjectType = invoice.RelatedObjectType;
875
+
876
+ const ct = await journalEntry.newLedgerTransaction(
877
+ TransactionTypeOptions.CREDIT,
878
+ );
879
+ ct.AccountNo = item.value[0];
880
+ ct.Currency = currency ? currency : 'MYR';
881
+ ct.CreditAmount = creditAmount ? creditAmount : 0.0;
882
+ ct.Date = transactionDate;
883
+ ct.Description = customer.FullName;
884
+ ct.Name = customer.FullName;
885
+ ct.RelatedDocNo = invoice.DocNo;
886
+ ct.RelatedObjectId = invoice.RelatedObjectId;
887
+ ct.RelatedObjectType = invoice.RelatedObjectType;
888
+
889
+ await this.postJournal(dbTransaction, journalEntry, loginUser);
890
+ }
891
+
892
+ return invoice;
893
+ } catch (err) {
894
+ // tslint:disable-next-line:no-console
895
+ console.log('Issue invoice err: ', err);
896
+ throw err;
897
+ }
898
+ }
899
+
900
+ static async findCustomer(
901
+ custSystemRefId: string,
902
+ dbTransaction?: any,
903
+ ): Promise<FinanceCustomerModel> {
904
+ const data = await FinanceCompany._FinanceCustomerRepository.findOne({
905
+ where: {
906
+ CustSystemRefId: custSystemRefId,
907
+ },
908
+ transaction: dbTransaction,
909
+ });
910
+
911
+ return data;
912
+ }
913
+
914
+ /**
915
+ * Issue a debit note
916
+ *
917
+ * @param dbTransaction - The database transaction to be used
918
+ * @param loginUser - The user issuing the invoice
919
+ * @param invoice - The document containing the invoice details
920
+ * @param customer - The customer to be issued the invoice
921
+ * @param dtAccountNo - The account number of the Account Receivable (AR) account to debit. If not provided, will debit the customer's default AR account
922
+ *
923
+ * @returns {Document} - Document object representing the full details of the invoice issued
924
+ */
925
+ async issueDebitNote(
926
+ dbTransaction: any,
927
+ loginUser: LoginUserBase,
928
+ invoice: Document,
929
+ customer: FinanceCustomerBase,
930
+ dtAccountNo?: string,
931
+ ): Promise<Document> {
932
+ try {
933
+ /*Check if the invoice number already exists (should be unique)*/
934
+ const duplicateInvoice = await FinanceCompany._DocumentRepository.findOne(
935
+ {
936
+ where: {
937
+ DocNo: invoice.DocNo,
938
+ },
939
+ transaction: dbTransaction,
940
+ },
941
+ );
942
+
943
+ if (duplicateInvoice) {
944
+ throw new Error('Invoice number already exists');
945
+ }
946
+
947
+ const documentItems = await invoice.getDocumentItems(dbTransaction);
948
+
949
+ /*Check if the document has at least 1 document item*/
950
+ if (!documentItems.length) {
951
+ throw new Error('Document must have at least 1 document item');
952
+ }
953
+
954
+ /*Check if each document item has CtAccountNo provided*/
955
+ for (const invoiceItem of documentItems) {
956
+ if (!invoiceItem.CtAccountNo) {
957
+ throw new Error(
958
+ 'Each document item should have CtAccountNo provided',
959
+ );
960
+ }
961
+ }
962
+
963
+ /*Set up the document type*/
964
+ invoice.DocType = DocType.DEBIT_NOTE;
965
+ // invoice.DocNo = this._createId().toUpperCase();
966
+
967
+ /*Generating the invoice*/
968
+ if (invoice.UseAccSystemDocYN === 'Y') {
969
+ /*todo: Posting to accounting system to generate invoice*/
970
+ const accInvoiceRefIDetail = await this.AccountingSystem.createInvoice(
971
+ {
972
+ customer,
973
+ invoice,
974
+ paymentMode: 'credit',
975
+ },
976
+ dbTransaction,
977
+ );
978
+ invoice.AccSystemRefId = accInvoiceRefIDetail.invoiceRefId;
979
+ invoice.PostedToAccSystemYN = 'Y';
980
+ invoice.PostedById = loginUser.ObjectId;
981
+ invoice.PostedDateTime = new Date();
982
+
983
+ //for each document item, insert the document item accounting reference id that have matching name
984
+ for (const documentItem of documentItems) {
985
+ const accItem = accInvoiceRefIDetail.invoiceItems.find(
986
+ (item) => item.name === documentItem.Name,
987
+ );
988
+ if (accItem) {
989
+ documentItem.AccSystemRefId = accItem.itemRefId;
990
+ documentItem.PostedToAccSystemYN = 'Y';
991
+ documentItem.PostedById = loginUser.ObjectId;
992
+ documentItem.PostedDateTime = new Date();
993
+ }
994
+ }
995
+ } else {
996
+ /*todo: check config file to see which invoice template is to be used for specific project*/
997
+
998
+ /*Generating invoice based on template*/
999
+ invoice.generateInvoice(loginUser.IDNo, customer, dbTransaction);
1000
+ }
1001
+
1002
+ /*Saving the document and document items to the database*/
1003
+ await FinanceCompany._DocumentRepository.create(
1004
+ {
1005
+ DocNo: invoice.DocNo,
1006
+ DocType: invoice.DocType,
1007
+ DocDate: invoice.DocDate,
1008
+ CompanyId: invoice.CompanyId,
1009
+ Currency: invoice.Currency,
1010
+ Amount: invoice.Amount,
1011
+ Description: invoice.Description,
1012
+ Status: invoice.Status,
1013
+ IssuedById: loginUser.ObjectId,
1014
+ IssuedToId: customer.ObjectId,
1015
+ IssuedToType: invoice.IssuedToType,
1016
+ RelatedObjectId: invoice.RelatedObjectId,
1017
+ RelatedObjectType: invoice.RelatedObjectType,
1018
+ CreatedById: loginUser.ObjectId,
1019
+ CreatedAt: new Date(),
1020
+ UpdatedById: loginUser.ObjectId,
1021
+ UpdatedAt: new Date(),
1022
+ DocPDFFileMediaId: invoice.DocPDFFileMediaId,
1023
+ DocHTMLFileMediaId: invoice.DocHTMLFileMediaId,
1024
+ AccSystemRefId: invoice.AccSystemRefId,
1025
+ PostedToAccSystemYN: invoice.PostedToAccSystemYN,
1026
+ PostedById:
1027
+ invoice.PostedToAccSystemYN == 'Y' ? invoice.PostedById : null,
1028
+ PostedDateTime: new Date(),
1029
+ UseAccSystemDocYN: invoice.UseAccSystemDocYN,
1030
+ },
1031
+ {
1032
+ transaction: dbTransaction,
1033
+ },
1034
+ );
1035
+
1036
+ documentItems.forEach(async (documentItem) => {
1037
+ await FinanceCompany._DocumentItemRepository.create(
1038
+ {
1039
+ DocumentItemId: this._createId().toUpperCase(),
1040
+ DocNo: documentItem.DocNo,
1041
+ Name: documentItem.Name,
1042
+ NameBM: documentItem.NameBM,
1043
+ Description: documentItem.Description,
1044
+ ItemId: documentItem.ItemId,
1045
+ ItemType: documentItem.ItemType,
1046
+ ItemSKU: documentItem.ItemSKU,
1047
+ ItemSerialNo: documentItem.ItemSerialNo,
1048
+ Currency: documentItem.Currency,
1049
+ UnitPrice: documentItem.UnitPrice,
1050
+ Quantity: documentItem.Quantity,
1051
+ QuantityUOM: documentItem.QuantityUOM,
1052
+ Amount: documentItem.Amount,
1053
+ TaxCode: documentItem.TaxCode,
1054
+ TaxAmount: documentItem.TaxAmount,
1055
+ TaxRate: documentItem.TaxRate,
1056
+ TaxInclusiveYN: documentItem.TaxInclusiveYN,
1057
+ DtAccountNo: documentItem.DtAccountNo,
1058
+ CtAccountNo: documentItem.CtAccountNo,
1059
+ AccSystemRefId: documentItem.AccSystemRefId,
1060
+ PostedToAccSystemYN: documentItem.PostedToAccSystemYN,
1061
+ PostedById: documentItem.PostedById,
1062
+ PostedDateTime: documentItem.PostedDateTime,
1063
+ },
1064
+ {
1065
+ transaction: dbTransaction,
1066
+ },
1067
+ );
1068
+ });
1069
+
1070
+ const transactionDate = new Date();
1071
+ const htCreditAccountAmount = new HashTable();
1072
+ const htCreditAccountCurrency = new HashTable();
1073
+ const htCreditAccountPurpose = new HashTable();
1074
+
1075
+ documentItems.forEach((invoiceItem) => {
1076
+ if (!htCreditAccountAmount.exists(invoiceItem.CtAccountNo)) {
1077
+ //add the credit account to the hash table
1078
+ htCreditAccountAmount.add(
1079
+ invoiceItem.CtAccountNo,
1080
+ invoiceItem.Amount,
1081
+ );
1082
+
1083
+ htCreditAccountCurrency.add(
1084
+ invoiceItem.CtAccountNo,
1085
+ invoiceItem.Currency,
1086
+ );
1087
+
1088
+ htCreditAccountPurpose.add(invoiceItem.CtAccountNo, invoiceItem.Name);
1089
+ } else {
1090
+ //update the credit account amount
1091
+ const d = htCreditAccountAmount.get(invoiceItem.CtAccountNo);
1092
+ htCreditAccountAmount.add(
1093
+ invoiceItem.CtAccountNo,
1094
+ d + invoiceItem.Amount,
1095
+ );
1096
+ }
1097
+ });
1098
+
1099
+ const savedItems = htCreditAccountAmount.list();
1100
+
1101
+ for (const item of savedItems) {
1102
+ const journalEntry = new JournalEntry(dbTransaction);
1103
+ //Temporary fix to successfully save journal entry in PostJournal
1104
+ journalEntry.init({
1105
+ CompanyId: this.CompanyId,
1106
+ Name: 'issue Invoice ' + invoice.DocNo,
1107
+ });
1108
+ // const account = await Account.initAccount(dbTransaction, item.value[0]);
1109
+ const creditAmount = item.value[1];
1110
+ const currency = htCreditAccountCurrency.get(item.value[0]);
1111
+ const purpose = htCreditAccountPurpose.get(item.value[0]);
1112
+
1113
+ const dt = await journalEntry.newLedgerTransaction(
1114
+ TransactionTypeOptions.DEBIT,
1115
+ );
1116
+
1117
+ if (dtAccountNo) {
1118
+ /*Transacting using AR account provided*/
1119
+ dt.AccountNo = dtAccountNo;
1120
+ } else {
1121
+ /*Transacting based on default customer AR account*/
1122
+ const arAccount = await customer.getAccountReceivable();
1123
+ dt.AccountNo = arAccount.AccountNo;
1124
+ }
1125
+
1126
+ dt.Currency = currency ? currency : 'MYR';
1127
+ dt.DebitAmount = creditAmount ? creditAmount : 0.0;
1128
+ dt.Date = transactionDate;
1129
+ dt.Description = `${purpose}`;
1130
+ dt.Name = `${purpose}`;
1131
+ dt.RelatedDocNo = invoice.DocNo;
1132
+ dt.RelatedObjectId = invoice.RelatedObjectId;
1133
+ dt.RelatedObjectType = invoice.RelatedObjectType;
1134
+
1135
+ const ct = await journalEntry.newLedgerTransaction(
1136
+ TransactionTypeOptions.CREDIT,
1137
+ );
1138
+ ct.AccountNo = item.value[0];
1139
+ ct.Currency = currency ? currency : 'MYR';
1140
+ ct.CreditAmount = creditAmount ? creditAmount : 0.0;
1141
+ ct.Date = transactionDate;
1142
+ ct.Description = customer.FullName;
1143
+ ct.Name = customer.FullName;
1144
+ ct.RelatedDocNo = invoice.DocNo;
1145
+ ct.RelatedObjectId = invoice.RelatedObjectId;
1146
+ ct.RelatedObjectType = invoice.RelatedObjectType;
1147
+
1148
+ await this.postJournal(dbTransaction, journalEntry, loginUser);
1149
+ }
1150
+
1151
+ return invoice;
1152
+ } catch (err) {
1153
+ // tslint:disable-next-line:no-console
1154
+ console.log('Issue debit note err: ', err);
1155
+ throw err;
1156
+ }
1157
+ }
1158
+
1159
+ /**
1160
+ * Issue a credit note
1161
+ *
1162
+ * @param dbTransaction - The database transaction to be used
1163
+ * @param loginUser - The user issuing the credit note
1164
+ * @param creditNote - The document containing the credit note details
1165
+ * @param customer - The customer to be issued the credit note
1166
+ * @param ctAccountNo - The account number of the Account Payable (AP) account to debit. If not provided, will debit the customer's default AR account
1167
+ *
1168
+ * @returns {Document} - Document object representing the full details of the invoice issued
1169
+ */
1170
+ async issueCreditNote(
1171
+ dbTransaction: any,
1172
+ loginUser: LoginUserBase,
1173
+ creditNote: Document,
1174
+ customer: FinanceCustomerBase,
1175
+ ctAccountNo?: string,
1176
+ ): Promise<Document> {
1177
+ try {
1178
+ /*Check if the invoice number already exists (should be unique)*/
1179
+ const duplicateCreditNote =
1180
+ await FinanceCompany._DocumentRepository.findOne({
1181
+ where: {
1182
+ DocNo: creditNote.DocNo,
1183
+ },
1184
+ transaction: dbTransaction,
1185
+ });
1186
+
1187
+ if (duplicateCreditNote) {
1188
+ throw new Error('Invoice number already exists');
1189
+ }
1190
+
1191
+ const documentItems = await creditNote.getDocumentItems(dbTransaction);
1192
+
1193
+ /*Check if the document has at least 1 document item*/
1194
+ if (!documentItems.length) {
1195
+ throw new Error('Document must have at least 1 document item');
1196
+ }
1197
+
1198
+ //Map each creditNote.DocumentItems and do checking below:
1199
+ //1. Set actualDocument to the instantiation of existing Document. (to check valid Document)
1200
+ //2. Check for each actualDocument to make sure IssuedToId all the same. If not, throw ClassError.
1201
+ //3. Check if each document item has DtAccountNo provided
1202
+ for (const invoiceItem of documentItems) {
1203
+ if (!invoiceItem.DtAccountNo) {
1204
+ throw new Error(
1205
+ 'Each document item should have DtAccountNo provided',
1206
+ );
1207
+ }
1208
+
1209
+ const actualDocument = await Document.initDocument(
1210
+ dbTransaction,
1211
+ invoiceItem.ItemId,
1212
+ );
1213
+
1214
+ if (actualDocument.IssuedToId !== creditNote.IssuedToId) {
1215
+ throw new ClassError(
1216
+ 'FinanceCompany',
1217
+ 'FinanceCompanyErrMsgOX',
1218
+ 'To issue credit note, all invoices must belong to same customer.',
1219
+ );
1220
+ }
1221
+ }
1222
+
1223
+ /*Set up the document type*/
1224
+ creditNote.DocType = DocType.CREDIT_NOTE;
1225
+ creditNote.Status = DocumentStatus.SETTLED;
1226
+
1227
+ /*Saving the document and document items to the database*/
1228
+ await FinanceCompany._DocumentRepository.create(
1229
+ {
1230
+ DocNo: creditNote.DocNo,
1231
+ DocType: creditNote.DocType,
1232
+ DocDate: creditNote.DocDate,
1233
+ CompanyId: creditNote.CompanyId,
1234
+ Currency: creditNote.Currency,
1235
+ Amount: creditNote.Amount,
1236
+ Description: creditNote.Description,
1237
+ Status: creditNote.Status,
1238
+ IssuedById: loginUser.ObjectId,
1239
+ IssuedToId: customer.ObjectId,
1240
+ IssuedToType: creditNote.IssuedToType,
1241
+ RelatedObjectId: creditNote.RelatedObjectId,
1242
+ RelatedObjectType: creditNote.RelatedObjectType,
1243
+ CreatedById: loginUser.ObjectId,
1244
+ CreatedAt: new Date(),
1245
+ UpdatedById: loginUser.ObjectId,
1246
+ UpdatedAt: new Date(),
1247
+ DocPDFFileMediaId: creditNote.DocPDFFileMediaId,
1248
+ DocHTMLFileMediaId: creditNote.DocHTMLFileMediaId,
1249
+ AccSystemRefId: creditNote.AccSystemRefId,
1250
+ PostedToAccSystemYN: creditNote.PostedToAccSystemYN,
1251
+ PostedById:
1252
+ creditNote.PostedToAccSystemYN == 'Y'
1253
+ ? creditNote.PostedById
1254
+ : null,
1255
+ PostedDateTime: new Date(),
1256
+ UseAccSystemDocYN: creditNote.UseAccSystemDocYN,
1257
+ },
1258
+ {
1259
+ transaction: dbTransaction,
1260
+ },
1261
+ );
1262
+
1263
+ for (const docItem of documentItems) {
1264
+ await FinanceCompany._DocumentItemRepository.create(
1265
+ {
1266
+ DocumentItemId: this._createId().toUpperCase(),
1267
+ DocNo: docItem.DocNo,
1268
+ Name: docItem.Name,
1269
+ NameBM: docItem.NameBM,
1270
+ Description: docItem.Description,
1271
+ ItemId: docItem.ItemId,
1272
+ ItemType: docItem.ItemType,
1273
+ ItemSKU: docItem.ItemSKU,
1274
+ ItemSerialNo: docItem.ItemSerialNo,
1275
+ Currency: docItem.Currency,
1276
+ UnitPrice: docItem.UnitPrice,
1277
+ Quantity: docItem.Quantity,
1278
+ QuantityUOM: docItem.QuantityUOM,
1279
+ Amount: docItem.Amount,
1280
+ TaxCode: docItem.TaxCode,
1281
+ TaxAmount: docItem.TaxAmount,
1282
+ TaxRate: docItem.TaxRate,
1283
+ TaxInclusiveYN: docItem.TaxInclusiveYN,
1284
+ DtAccountNo: docItem.DtAccountNo ? docItem.DtAccountNo : null,
1285
+ CtAccountNo: docItem.CtAccountNo ? docItem.CtAccountNo : null,
1286
+ },
1287
+ {
1288
+ transaction: dbTransaction,
1289
+ },
1290
+ );
1291
+
1292
+ //Call Document.settleByCreditNote to settle the invoice
1293
+ await Document.settleByCreditNote(
1294
+ loginUser,
1295
+ dbTransaction,
1296
+ docItem.ItemId,
1297
+ docItem.Amount,
1298
+ );
1299
+ }
1300
+
1301
+ /*Generating the credit note*/
1302
+ if (creditNote.UseAccSystemDocYN === 'Y') {
1303
+ /*todo: Posting to accounting system to generate creditNote*/
1304
+ // await this.AccountingSystem.createCreditNote(creditNote);
1305
+ } else {
1306
+ /*todo: check config file to see which invoice template is to be used for specific project*/
1307
+
1308
+ /*Generating credit note based on template*/
1309
+ creditNote.generateCreditNote(loginUser.IDNo, customer);
1310
+ }
1311
+
1312
+ const journalEntry = new JournalEntry(dbTransaction);
1313
+ //Temporary fix to successfully save journal entry in PostJournal
1314
+ journalEntry.init({
1315
+ CompanyId: this.CompanyId,
1316
+ Name: 'Issue Credit Note ' + creditNote.DocNo,
1317
+ });
1318
+
1319
+ const transactionDate = new Date();
1320
+
1321
+ const creditTransaction = await journalEntry.newLedgerTransaction(
1322
+ TransactionTypeOptions.CREDIT,
1323
+ );
1324
+
1325
+ if (ctAccountNo) {
1326
+ /*Transacting using AR account provided*/
1327
+ creditTransaction.AccountNo = ctAccountNo;
1328
+ } else {
1329
+ /*Transacting based on default customer AR account*/
1330
+ // creditTransaction.AccountNo = customer.CustSystemCode + '-AP';
1331
+ // getAccountPayable
1332
+ const arAccount = await customer.getAccountPayable();
1333
+ creditTransaction.AccountNo = arAccount.AccountNo;
1334
+ }
1335
+
1336
+ creditTransaction.Currency = creditNote.Currency;
1337
+ creditTransaction.CreditAmount = creditNote.Amount;
1338
+ creditTransaction.Date = transactionDate;
1339
+ creditTransaction.Description = creditNote.DocNo;
1340
+ creditTransaction.Name = creditNote.DocNo;
1341
+ creditTransaction.RelatedDocNo = creditNote.DocNo;
1342
+ creditTransaction.RelatedObjectId = creditNote.RelatedObjectId;
1343
+ creditTransaction.RelatedObjectType = creditNote.RelatedObjectType;
1344
+
1345
+ for (const invoiceItem of documentItems) {
1346
+ const itemLedger = await journalEntry.newLedgerTransaction(
1347
+ TransactionTypeOptions.DEBIT,
1348
+ );
1349
+ itemLedger.AccountNo = invoiceItem.DtAccountNo;
1350
+ itemLedger.Currency = invoiceItem.Currency;
1351
+ itemLedger.DebitAmount = invoiceItem.Amount;
1352
+ itemLedger.Date = transactionDate;
1353
+ itemLedger.Description = invoiceItem.Name;
1354
+ // itemLedger.Name = invoiceItem.Name;
1355
+ itemLedger.RelatedDocNo = creditNote.DocNo;
1356
+ itemLedger.RelatedObjectId = creditNote.RelatedObjectId;
1357
+ itemLedger.RelatedObjectType = creditNote.RelatedObjectType;
1358
+ }
1359
+
1360
+ await this.postJournal(dbTransaction, journalEntry, loginUser);
1361
+
1362
+ const entityValueAfter = {
1363
+ LedgerNo: creditTransaction.LedgerNo,
1364
+ TransactionType: creditTransaction.TransactionType,
1365
+ JournalEntryId: creditTransaction.JournalEntryId,
1366
+ AccountNo: creditTransaction.AccountNo,
1367
+ Date: creditTransaction.Date,
1368
+ Name: creditTransaction.Name,
1369
+ Description: creditTransaction.Description,
1370
+ Currency: creditTransaction.Currency,
1371
+ DebitAmount: creditTransaction.DebitAmount,
1372
+ CreditAmount: creditTransaction.CreditAmount,
1373
+ RelatedObjectId: creditTransaction.RelatedObjectId,
1374
+ RelatedObjectType: creditTransaction.RelatedObjectType,
1375
+ RelatedDocNo: creditTransaction.RelatedDocNo,
1376
+ RelatedPaymentId: creditTransaction.RelatedPaymentId,
1377
+ };
1378
+ const payload = {
1379
+ Action: 'Create',
1380
+ Activity: 'Issuing a Credit Note Transaction',
1381
+ Description: `Credit Transaction (ID: ${creditTransaction.LedgerNo}) has been created`,
1382
+ EntityType: 'CreditTransaction',
1383
+ EntityValueBefore: JSON.stringify({}),
1384
+ EntityValueAfter: JSON.stringify(entityValueAfter),
1385
+ PerformedById: loginUser.ObjectId,
1386
+ PerformedAt: transactionDate,
1387
+ EntityId: creditTransaction.LedgerNo,
1388
+ };
1389
+
1390
+ await axios.post(
1391
+ `${process.env.COMMON_API_URL}/activity-histories`,
1392
+ payload,
1393
+ );
1394
+
1395
+ return creditNote;
1396
+ } catch (err) {
1397
+ // tslint:disable-next-line:no-console
1398
+ console.log('Issue credit note err: ', err);
1399
+ throw err;
1400
+ }
1401
+ }
1402
+
1403
+ /**
1404
+ * Register a payment collected
1405
+ *
1406
+ * @param dbTransaction - The database transaction to be used
1407
+ * @param loginUser - The user collecting the payment
1408
+ * @param payment - The payment object containing payment details
1409
+ * @param customer - The customer making the payment
1410
+ * @param ctAccountNo - The account number of the customer's Account Receivable account to transact, else the default customer account payable receivable will be used
1411
+ * @param receiptNo - The document receipt number
1412
+ *
1413
+ * @returns {Payment} - Payment object representing the full detals of the payment collection recorded
1414
+ */
1415
+ async collectPayment(
1416
+ dbTransaction: any,
1417
+ loginUser: LoginUserBase,
1418
+ payment: Payment,
1419
+ collectPaymentType: CollectPaymentType = CollectPaymentType.AUTOMATIC,
1420
+ ): Promise<Payment> {
1421
+ try {
1422
+ /*validation 1: Make sure that the payment has at least 1 payment item*/
1423
+ const paymentItems = await payment.getPaymentItems();
1424
+ if (paymentItems.length < 1) {
1425
+ throw new Error(
1426
+ 'Atleast one payment item is required to identify what payment is being paid for.',
1427
+ );
1428
+ }
1429
+
1430
+ /*validation 2: Make sure that the payment has at least 1 payment method*/
1431
+ const paymentPaidWithItems = await payment.getPaymentPaidWith();
1432
+ if (paymentPaidWithItems.length < 1) {
1433
+ throw new Error(
1434
+ 'Atleast one payment paid with item is required to identify how the payment was made.',
1435
+ );
1436
+ }
1437
+
1438
+ /*Saving payment, payment items, and payment paid with details to the database*/
1439
+ payment.PaymentId = this._createId().toUpperCase();
1440
+ payment.PaymentType = PaymentType.PAYMENT_RECEIVED;
1441
+ payment.ReceivedBy = loginUser.ObjectId;
1442
+ payment.IssuedBy = loginUser.ObjectId;
1443
+
1444
+ await FinanceCompany._PaymentRepository.create(
1445
+ {
1446
+ PaymentId: payment.PaymentId,
1447
+ PaymentType: payment.PaymentType,
1448
+ PaymentDate: payment.PaymentDate,
1449
+ Description: payment.Description,
1450
+ Currency: payment.Currency,
1451
+ Amount: payment.Amount,
1452
+ Status:
1453
+ collectPaymentType === CollectPaymentType.AUTOMATIC
1454
+ ? PaymentStatus.CONFIRMED
1455
+ : PaymentStatus.PENDING,
1456
+ PostedToAccSystemYN: 'N',
1457
+ ReceivedBy: payment.ReceivedBy,
1458
+ IssuedBy: payment.IssuedBy,
1459
+ Remarks: payment.Remarks,
1460
+ RelatedObjectId: payment.RelatedObjectId,
1461
+ RelatedObjectType: payment.RelatedObjectType,
1462
+ ReceiptDocNo: payment.ReceiptDocNo,
1463
+ UpdatedAt: new Date(),
1464
+ UpdatedBy: loginUser.ObjectId,
1465
+ CreatedAt: new Date(),
1466
+ CreatedBy: loginUser.ObjectId,
1467
+ },
1468
+ { transaction: dbTransaction },
1469
+ );
1470
+
1471
+ for (const paymentItem of paymentItems) {
1472
+ await FinanceCompany._PaymentItemRepository.create(
1473
+ {
1474
+ PaymentId: payment.PaymentId,
1475
+ PayForObjectId: paymentItem.PayForObjectId,
1476
+ PayForObjectType: paymentItem.PayForObjectType,
1477
+ Currency: paymentItem.Currency,
1478
+ Amount: paymentItem.Amount,
1479
+ Name: paymentItem.Name,
1480
+ Description: paymentItem.Description,
1481
+ },
1482
+ { transaction: dbTransaction },
1483
+ );
1484
+
1485
+ // await paymentItem.paid(dbTransaction);
1486
+ }
1487
+
1488
+ for (const paymentPaidWithItem of paymentPaidWithItems) {
1489
+ // Validate payment method type used
1490
+ await PaymentMethodType.initMethodType(
1491
+ dbTransaction,
1492
+ paymentPaidWithItem.MethodTypeId,
1493
+ );
1494
+
1495
+ await FinanceCompany._PaymentPaidWithRepository.create(
1496
+ {
1497
+ PaymentId: payment.PaymentId,
1498
+ MethodTypeId: paymentPaidWithItem.MethodTypeId,
1499
+ Currency: paymentPaidWithItem.Currency,
1500
+ Amount: paymentPaidWithItem.Amount,
1501
+ Status: paymentPaidWithItem.Status,
1502
+ TransactionId: paymentPaidWithItem.TransactionId,
1503
+ RefBank: paymentPaidWithItem.RefBank,
1504
+ RefName: paymentPaidWithItem.RefName,
1505
+ RefNo: paymentPaidWithItem.RefNo,
1506
+ RefOther1: paymentPaidWithItem.RefOther1,
1507
+ RefOther2: paymentPaidWithItem.RefOther2,
1508
+ RefOther3: paymentPaidWithItem.RefOther3,
1509
+ RefOther4: paymentPaidWithItem.RefOther4,
1510
+ RefOther5: paymentPaidWithItem.RefOther5,
1511
+ Remarks: paymentPaidWithItem.Remarks,
1512
+ PaymentMediaId: paymentPaidWithItem.PaymentMediaId,
1513
+ },
1514
+ { transaction: dbTransaction },
1515
+ );
1516
+ }
1517
+
1518
+ /*todo: saving a record into the activity history table*/
1519
+
1520
+ return payment;
1521
+ } catch (error) {
1522
+ throw error;
1523
+ }
1524
+ }
1525
+
1526
+ /**
1527
+ * Method to make payment to a customer.
1528
+ *
1529
+ * @param dbTransaction - The database transaction to be used
1530
+ * @param loginUser - The user logging in and the user who collected the payment.
1531
+ * @param payment - The payment object containing payment details
1532
+ * @param customer - The customer who is receiving the payment.
1533
+ * @param ctAccountNo - The account number of the customer's Account Payable account to transact, else the default customer account payable will be used.
1534
+ *
1535
+ * @returns {Payment} - Payment object representing the full details of the payment collection recorded
1536
+ */
1537
+ async makePayment(
1538
+ dbTransaction: any,
1539
+ loginUser: LoginUserBase,
1540
+ payment: Payment,
1541
+ customer: FinanceCustomerBase,
1542
+ dtAccountNo?: string,
1543
+ ): Promise<Payment> {
1544
+ let paymentMethodType: PaymentMethodType;
1545
+ try {
1546
+ const paymentPaidWith = await payment.getPaymentPaidWith();
1547
+ if (paymentPaidWith.length < 1) {
1548
+ throw new Error(
1549
+ 'Atleast one payment paid with item is required to identify how the payment was made.',
1550
+ );
1551
+ }
1552
+ paymentMethodType = await PaymentMethodType.initMethodType(
1553
+ dbTransaction,
1554
+ paymentPaidWith[0].MethodTypeId,
1555
+ );
1556
+
1557
+ const paymentItems = await payment.getPaymentItems();
1558
+ if (paymentItems.length < 1) {
1559
+ throw new Error(
1560
+ 'Atleast one payment item is required to identify what payment is being paid for',
1561
+ );
1562
+ }
1563
+
1564
+ payment.PaymentId = this._createId().toUpperCase();
1565
+ payment.PaymentType = PaymentType.PAYOUT;
1566
+ payment.ReceivedBy = loginUser.ObjectId;
1567
+ payment.IssuedBy = loginUser.ObjectId;
1568
+
1569
+ await FinanceCompany._PaymentRepository.create(
1570
+ {
1571
+ PaymentId: payment.PaymentId,
1572
+ PaymentType: payment.PaymentType,
1573
+ PaymentDate: payment.PaymentDate,
1574
+ Description: payment.Description,
1575
+ Currency: payment.Currency,
1576
+ ReceivedBy: payment.ReceivedBy,
1577
+ IssuedBy: payment.IssuedBy,
1578
+ Remarks: payment.Remarks,
1579
+ RelatedObjectId: payment.RelatedObjectId,
1580
+ RelatedObjectType: payment.RelatedObjectType,
1581
+ Amount: payment.Amount,
1582
+ Status: PaymentStatus.CONFIRMED,
1583
+ PostedToAccSystemYN: 'N',
1584
+ UpdatedAt: new Date(),
1585
+ UpdatedBy: loginUser.ObjectId,
1586
+ CreatedAt: new Date(),
1587
+ CreatedBy: loginUser.ObjectId,
1588
+ },
1589
+ { transaction: dbTransaction },
1590
+ );
1591
+
1592
+ paymentItems.forEach(async (paymentItem) => {
1593
+ await FinanceCompany._PaymentItemRepository.create(
1594
+ {
1595
+ PaymentId: payment.PaymentId,
1596
+ PayForObjectId: paymentItem.PayForObjectId,
1597
+ PayForObjectType: paymentItem.PayForObjectType,
1598
+ Currency: paymentItem.Currency,
1599
+ Amount: paymentItem.Amount,
1600
+ Name: paymentItem.Name,
1601
+ Description: paymentItem.Description,
1602
+ },
1603
+ { transaction: dbTransaction },
1604
+ );
1605
+
1606
+ await paymentItem.paid(dbTransaction);
1607
+ });
1608
+
1609
+ for (const paymentPaidWithItem of paymentPaidWith) {
1610
+ await FinanceCompany._PaymentPaidWithRepository.create(
1611
+ {
1612
+ PaymentId: payment.PaymentId,
1613
+ MethodTypeId: paymentPaidWithItem.MethodTypeId,
1614
+ Currency: paymentPaidWithItem.Currency,
1615
+ Amount: paymentPaidWithItem.Amount,
1616
+ Status: paymentPaidWithItem.Status,
1617
+ TransactionId: paymentPaidWithItem.TransactionId,
1618
+ RefBank: paymentPaidWithItem.RefBank,
1619
+ RefName: paymentPaidWithItem.RefName,
1620
+ RefNo: paymentPaidWithItem.RefNo,
1621
+ RefOther1: paymentPaidWithItem.RefOther1,
1622
+ RefOther2: paymentPaidWithItem.RefOther2,
1623
+ RefOther3: paymentPaidWithItem.RefOther3,
1624
+ RefOther4: paymentPaidWithItem.RefOther4,
1625
+ RefOther5: paymentPaidWithItem.RefOther5,
1626
+ Remarks: paymentPaidWithItem.Remarks,
1627
+ PaymentMediaId: paymentPaidWithItem.PaymentMediaId,
1628
+ },
1629
+ { transaction: dbTransaction },
1630
+ );
1631
+ }
1632
+ const transactionDate = new Date();
1633
+ for (const paymentPaidWithItem of paymentPaidWith) {
1634
+ try {
1635
+ paymentMethodType = await PaymentMethodType.initMethodType(
1636
+ dbTransaction,
1637
+ paymentPaidWithItem.MethodTypeId,
1638
+ );
1639
+
1640
+ const journalEntry = new JournalEntry(dbTransaction);
1641
+ //Temporary fix to successfully save journal entry in PostJournal
1642
+ journalEntry.init({
1643
+ CompanyId: this.CompanyId,
1644
+ Name: 'Make Payment for ' + payment.PaymentId,
1645
+ });
1646
+
1647
+ const creditLT = await journalEntry.newLedgerTransaction(
1648
+ TransactionTypeOptions.CREDIT,
1649
+ );
1650
+ creditLT.AccountNo = paymentMethodType.AccountNo;
1651
+ creditLT.Currency = paymentPaidWithItem.Currency;
1652
+ creditLT.CreditAmount = paymentPaidWithItem.Amount;
1653
+ creditLT.Date = transactionDate;
1654
+ creditLT.Description = customer.FullName;
1655
+ creditLT.Name = customer.FullName;
1656
+ creditLT.RelatedObjectId = payment.RelatedObjectId;
1657
+ creditLT.RelatedObjectType = payment.RelatedObjectType;
1658
+
1659
+ const debitLT = await journalEntry.newLedgerTransaction(
1660
+ TransactionTypeOptions.DEBIT,
1661
+ );
1662
+ if (dtAccountNo) {
1663
+ debitLT.AccountNo = dtAccountNo;
1664
+ } else {
1665
+ const apAccount = await customer.getAccountPayable();
1666
+ debitLT.AccountNo = apAccount.AccountNo;
1667
+ }
1668
+ debitLT.Currency = paymentPaidWithItem.Currency;
1669
+ debitLT.DebitAmount = paymentPaidWithItem.Amount;
1670
+ debitLT.Date = transactionDate;
1671
+ debitLT.Description = paymentMethodType.Name;
1672
+ debitLT.Name = paymentMethodType.Name;
1673
+ debitLT.RelatedObjectId = payment.RelatedObjectId;
1674
+ debitLT.RelatedObjectType = payment.RelatedObjectType;
1675
+
1676
+ await this.postJournal(dbTransaction, journalEntry, loginUser);
1677
+ } catch (error) {
1678
+ if (error instanceof RecordNotFoundError) {
1679
+ throw new Error('Invalid Payment Method Type Id');
1680
+ } else {
1681
+ throw error;
1682
+ }
1683
+ }
1684
+ }
1685
+ /*todo: saving a record into the activity history table*/
1686
+
1687
+ return payment;
1688
+ } catch (error) {
1689
+ if (error instanceof RecordNotFoundError) {
1690
+ throw new Error('Invalid PaymentMethodType id');
1691
+ } else {
1692
+ throw error;
1693
+ }
1694
+ }
1695
+ }
1696
+
1697
+ get PaymentMethods(): Promise<PaymentMethod[]> {
1698
+ return new Promise((resolve, reject) => {
1699
+ if (this.CompanyId !== 'New') {
1700
+ FinanceCompany._PaymentMethodRepository
1701
+ .findAll({
1702
+ where: {
1703
+ CompanyId: this.CompanyId,
1704
+ },
1705
+ // transaction: this._DbTransaction,
1706
+ })
1707
+ .then((paymentMethod) => {
1708
+ const paymentMethodObjects = paymentMethod.map(
1709
+ (paymentMethodData) => {
1710
+ return new Promise((resolve, reject) => {
1711
+ FinanceCompany._PaymentMethodTypeRepository
1712
+ .findAll({
1713
+ where: {
1714
+ MethodId: paymentMethodData.MethodId,
1715
+ },
1716
+ // transaction: this._DbTransaction,
1717
+ raw: true,
1718
+ })
1719
+ .then((paymentMethodTypes) => {
1720
+ const paymentMethodObjects = {
1721
+ ...paymentMethodData.get({ plain: true }),
1722
+ Types: paymentMethodTypes,
1723
+ };
1724
+ resolve(paymentMethodObjects);
1725
+ })
1726
+ .catch((err) => {
1727
+ reject(err);
1728
+ });
1729
+ }).then((paymentMethods) => paymentMethods);
1730
+ },
1731
+ );
1732
+ return Promise.all(paymentMethodObjects);
1733
+ })
1734
+ .then((paymentMethodObjects) => {
1735
+ this._PaymentMethods = paymentMethodObjects;
1736
+ resolve(this._PaymentMethods);
1737
+ })
1738
+ .catch((err) => {
1739
+ reject(err);
1740
+ });
1741
+ } else {
1742
+ resolve(this._PaymentMethods);
1743
+ }
1744
+ });
1745
+ }
1746
+
1747
+ get TaxCodes(): Promise<Tax[]> {
1748
+ return new Promise((resolve, reject) => {
1749
+ if (this.CompanyId !== 'New') {
1750
+ FinanceCompany._TaxRepository
1751
+ .findAll({
1752
+ where: {
1753
+ CompanyId: this.CompanyId,
1754
+ },
1755
+ transaction: this._DbTransaction,
1756
+ })
1757
+ .then((taxes) => {
1758
+ const taxList = [];
1759
+ taxes.forEach((tax) => {
1760
+ taxList.push(
1761
+ new Tax({
1762
+ TaxCode: tax.TaxCode,
1763
+ TaxRate:
1764
+ typeof tax.TaxRate === 'number'
1765
+ ? tax.TaxRate
1766
+ : parseFloat(tax.TaxRate),
1767
+ Description: tax.Description,
1768
+ CompanyId: tax.CompanyId,
1769
+ CreatedAt: tax.CreatedAt,
1770
+ CreatedById: tax.CreatedById,
1771
+ UpdatedAt: tax.UpdatedAt,
1772
+ UpdatedById: tax.UpdatedById,
1773
+ }),
1774
+ );
1775
+ });
1776
+ this._Taxes = taxList;
1777
+ resolve(this._Taxes);
1778
+ })
1779
+ .catch((err) => {
1780
+ reject(err);
1781
+ });
1782
+ } else {
1783
+ resolve(this._Taxes);
1784
+ }
1785
+ });
1786
+ }
1787
+
1788
+ /**
1789
+ * Method to load / reload the payment methods based on the configuration file
1790
+ */
1791
+ async LoadPaymentMethods(
1792
+ companyId: string,
1793
+ paymentMethods: any,
1794
+ transaction?: any,
1795
+ ): Promise<void> {
1796
+ const paymentMethod = new PaymentMethod();
1797
+ for (const method in paymentMethods) {
1798
+ const paymentMethodData =
1799
+ await FinanceCompany._PaymentMethodRepository.findOne({
1800
+ where: {
1801
+ MethodId: paymentMethods[method].id,
1802
+ Name: paymentMethods[method].name,
1803
+ },
1804
+ transaction: transaction,
1805
+ });
1806
+
1807
+ if (!paymentMethodData) {
1808
+ const newPaymentMethod =
1809
+ await FinanceCompany._PaymentMethodRepository.create(
1810
+ {
1811
+ MethodId: paymentMethods[method].id,
1812
+ Name: paymentMethods[method].name,
1813
+ CompanyId: companyId,
1814
+ },
1815
+ {
1816
+ transaction: transaction,
1817
+ },
1818
+ );
1819
+
1820
+ this._PaymentMethods.push(newPaymentMethod);
1821
+ }
1822
+ this._PaymentMethods.push(paymentMethodData);
1823
+ }
1824
+
1825
+ this._PaymentMethods.forEach(async (item) => {
1826
+ const p = item?.get({ plain: true });
1827
+
1828
+ for (const method in paymentMethods) {
1829
+ if (!p) {
1830
+ continue;
1831
+ }
1832
+
1833
+ if (p.MethodId === paymentMethods[method]?.id) {
1834
+ const paymentMethodTypeData =
1835
+ await FinanceCompany._PaymentMethodTypeRepository.findOne({
1836
+ where: {
1837
+ MethodId: p.MethodId,
1838
+ },
1839
+ transaction: transaction,
1840
+ });
1841
+
1842
+ if (!paymentMethodTypeData) {
1843
+ const configPaymentMethodTypes = paymentMethods[method]?.types;
1844
+
1845
+ for (const methodType in configPaymentMethodTypes) {
1846
+ // TODO: Create a seeder for payment method account
1847
+ /*validate whether account data already exists*/
1848
+ const accountData =
1849
+ await FinanceCompany._AccountRepository.findOne({
1850
+ where: {
1851
+ AccountNo: configPaymentMethodTypes[methodType].accountNo,
1852
+ },
1853
+ transaction: transaction,
1854
+ });
1855
+
1856
+ /*generating account data if not exist */
1857
+ if (!accountData) {
1858
+ const accountPayload = {
1859
+ CompanyId: companyId,
1860
+ Name: configPaymentMethodTypes[methodType].name,
1861
+ AccountType: 'PaymentMethod',
1862
+ CreatedAt: new Date(),
1863
+ CreatedById: 'System',
1864
+ AccSystemRefId: 'REF',
1865
+ PostedToAccSystemYN: 'N',
1866
+ };
1867
+
1868
+ try {
1869
+ await FinanceCompany._AccountRepository.create(
1870
+ {
1871
+ AccountNo: configPaymentMethodTypes[methodType].accountNo,
1872
+ ...accountPayload,
1873
+ },
1874
+ {
1875
+ transaction: transaction,
1876
+ },
1877
+ );
1878
+
1879
+ await FinanceCompany._AccountRepository.create(
1880
+ {
1881
+ AccountNo:
1882
+ configPaymentMethodTypes[methodType]
1883
+ .processingFeeAccountNo,
1884
+ ...accountPayload,
1885
+ },
1886
+ {
1887
+ transaction: transaction,
1888
+ },
1889
+ );
1890
+ } catch (err) {
1891
+ throw err;
1892
+ }
1893
+ }
1894
+
1895
+ const paymentMethodTypePayload = {
1896
+ MethodId: p.MethodId,
1897
+ MethodTypeId: configPaymentMethodTypes[methodType].id,
1898
+ Name: configPaymentMethodTypes[methodType].name,
1899
+ AccountNo: configPaymentMethodTypes[methodType].accountNo,
1900
+ ProcessingFeeRate:
1901
+ configPaymentMethodTypes[methodType].processingFeeRate,
1902
+ ProcessingFeeAccountNo:
1903
+ configPaymentMethodTypes[methodType].processingFeeAccountNo,
1904
+ };
1905
+
1906
+ try {
1907
+ await paymentMethod.newPaymentMethodType(
1908
+ paymentMethodTypePayload,
1909
+ transaction,
1910
+ );
1911
+ } catch (err) {}
1912
+ }
1913
+ }
1914
+ }
1915
+ }
1916
+ });
1917
+ }
1918
+
1919
+ /**
1920
+ * Method to load / reload the payment methods based on the configuration file
1921
+ */
1922
+ async LoadTaxCodes(
1923
+ companyId: string,
1924
+ companyTaxes: any,
1925
+ transaction?: any,
1926
+ ): Promise<void> {
1927
+ for (const tax in companyTaxes) {
1928
+ let tx = await FinanceCompany._TaxRepository.findOne({
1929
+ where: {
1930
+ TaxCode: companyTaxes[tax].taxCode,
1931
+ },
1932
+ transaction: transaction,
1933
+ });
1934
+
1935
+ if (!tx) {
1936
+ const newTx = await FinanceCompany._TaxRepository.create(
1937
+ {
1938
+ TaxCode: companyTaxes[tax].taxCode,
1939
+ TaxRate: companyTaxes[tax].taxRate,
1940
+ Description: companyTaxes[tax].description,
1941
+ CompanyId: companyId,
1942
+ UpdatedAt: new Date(),
1943
+ CreatedById: 'System',
1944
+ CreatedAt: new Date(),
1945
+ UpdatedById: 'System',
1946
+ },
1947
+ {
1948
+ transaction: transaction,
1949
+ },
1950
+ );
1951
+
1952
+ tx = newTx;
1953
+ }
1954
+ this._Taxes.push(new Tax(tx.get({ plain: true })));
1955
+ }
1956
+ }
1957
+
1958
+ async collectPaymentForMultipleCustomers(
1959
+ dbTransaction: any,
1960
+ loginUser: LoginUserBase,
1961
+ payment: Payment,
1962
+ creditTransaction: {
1963
+ AccountNo: string;
1964
+ Currency: string;
1965
+ Amount: number;
1966
+ }[],
1967
+ receiptNo?: string,
1968
+ collectPaymentType: CollectPaymentType = CollectPaymentType.AUTOMATIC,
1969
+ ): Promise<Payment> {
1970
+ //Method to collect payment for multiple customer.
1971
+ try {
1972
+ //Part 1: Privilege Checking\
1973
+ const systemCode = await ApplicationConfig.getComponentConfigValue(
1974
+ 'system-code',
1975
+ );
1976
+ const isPrivileged = await loginUser.checkPrivileges(
1977
+ systemCode,
1978
+ 'Collect Payment For Multiple Customers',
1979
+ );
1980
+
1981
+ if (!isPrivileged) {
1982
+ throw new Error('User is not authorized to perform this action');
1983
+ }
1984
+
1985
+ //Part 2: Validation
1986
+ //Make sure that the payment has at least 1 payment item
1987
+ const paymentItems = await payment.getPaymentItems();
1988
+ if (paymentItems.length < 1) {
1989
+ throw new Error(
1990
+ 'Atleast one payment item is required to identify what payment is being paid for.',
1991
+ );
1992
+ }
1993
+
1994
+ //Make sure that the payment has at least 1 payment method
1995
+ const paymentPaidWithItems = await payment.getPaymentPaidWith();
1996
+ if (paymentPaidWithItems.length < 1) {
1997
+ throw new Error(
1998
+ 'Atleast one payment paid with item is required to identify how the payment was made.',
1999
+ );
2000
+ }
2001
+
2002
+ //Make sure the payment items length is equal to creditTransactions length records.
2003
+ if (paymentItems.length !== creditTransaction.length) {
2004
+ throw new Error(
2005
+ 'Payment items length is not equal to creditTransaction length',
2006
+ );
2007
+ }
2008
+
2009
+ //Make sure the payment items total Amount is equal to credit transactions total Amount.
2010
+ let totalAmount = 0;
2011
+ paymentItems.forEach((paymentItem) => {
2012
+ totalAmount += paymentItem.Amount;
2013
+ });
2014
+
2015
+ const totalCreditTransactionAmount = creditTransaction.reduce(
2016
+ (accumulator, currentValue) => {
2017
+ return accumulator + currentValue.Amount;
2018
+ },
2019
+ 0,
2020
+ );
2021
+
2022
+ if (totalAmount !== totalCreditTransactionAmount) {
2023
+ throw new Error(
2024
+ 'Payment items total Amount is dnot equal to credit transactions total Amount',
2025
+ );
2026
+ }
2027
+
2028
+ //Part 3:Generate Receipt (KIV)
2029
+
2030
+ //Part 4: Saving Payment, Payment Items and Payment Paid with Details to the Database
2031
+
2032
+ //Set below Payment attributes:
2033
+ payment.PaymentId = this._createId().toUpperCase();
2034
+ payment.PaymentType = PaymentType.PAYMENT_RECEIVED;
2035
+ payment.ReceivedBy = loginUser.ObjectId;
2036
+ payment.IssuedBy = loginUser.ObjectId;
2037
+
2038
+ //Call Payment Repository create() method by passing:
2039
+ await FinanceCompany._PaymentRepository.create(
2040
+ {
2041
+ PaymentId: payment.PaymentId,
2042
+ PaymentType: payment.PaymentType,
2043
+ PaymentDate: payment.PaymentDate,
2044
+ Description: payment.Description,
2045
+ Currency: payment.Currency,
2046
+ Amount: payment.Amount,
2047
+ Status:
2048
+ collectPaymentType === CollectPaymentType.MANUAL
2049
+ ? PaymentStatus.PENDING
2050
+ : PaymentStatus.CONFIRMED,
2051
+ PostedToAccSystemYN: 'N',
2052
+ ReceivedBy: payment.ReceivedBy,
2053
+ IssuedBy: payment.IssuedBy,
2054
+ Remarks: payment.Remarks,
2055
+ RelatedObjectId: payment.RelatedObjectId,
2056
+ RelatedObjectType: payment.RelatedObjectType,
2057
+ ReceiptDocNo: payment.ReceiptDocNo,
2058
+ UpdatedAt: new Date(),
2059
+ UpdatedBy: loginUser.ObjectId,
2060
+ CreatedAt: new Date(),
2061
+ CreatedBy: loginUser.ObjectId,
2062
+ },
2063
+ { transaction: dbTransaction },
2064
+ );
2065
+
2066
+ //Map Payment.PaymentItems and insert record by passing dbTransaction
2067
+ paymentItems.forEach(async (paymentItem) => {
2068
+ await FinanceCompany._PaymentItemRepository.create(
2069
+ {
2070
+ PaymentId: payment.PaymentId,
2071
+ PayForObjectId: paymentItem.PayForObjectId,
2072
+ PayForObjectType: paymentItem.PayForObjectType,
2073
+ Currency: paymentItem.Currency,
2074
+ Amount: paymentItem.Amount,
2075
+ Name: paymentItem.Name,
2076
+ Description: paymentItem.Description,
2077
+ },
2078
+ { transaction: dbTransaction },
2079
+ );
2080
+ });
2081
+
2082
+ // Map Payment.PaymentPaidWith and insert record by passing dbTransaction.
2083
+ paymentPaidWithItems.forEach(async (paymentPaidWithItem) => {
2084
+ await FinanceCompany._PaymentPaidWithRepository.create(
2085
+ {
2086
+ PaymentId: payment.PaymentId,
2087
+ MethodTypeId: paymentPaidWithItem.MethodTypeId,
2088
+ Currency: paymentPaidWithItem.Currency,
2089
+ Amount: paymentPaidWithItem.Amount,
2090
+ Status: paymentPaidWithItem.Status,
2091
+ LedgerNo: paymentPaidWithItem.TransactionId,
2092
+ RefBank: paymentPaidWithItem.RefBank,
2093
+ RefName: paymentPaidWithItem.RefName,
2094
+ RefNo: paymentPaidWithItem.RefNo,
2095
+ RefOther1: paymentPaidWithItem.RefOther1,
2096
+ RefOther2: paymentPaidWithItem.RefOther2,
2097
+ RefOther3: paymentPaidWithItem.RefOther3,
2098
+ RefOther4: paymentPaidWithItem.RefOther4,
2099
+ RefOther5: paymentPaidWithItem.RefOther5,
2100
+ Remarks: paymentPaidWithItem.Remarks,
2101
+ PaymentMediaId: paymentPaidWithItem.PaymentMediaId,
2102
+ },
2103
+ { transaction: dbTransaction },
2104
+ );
2105
+ });
2106
+
2107
+ //Part 5: Registering the Journal Entries for the transaction
2108
+ //Initialise TransactionDate
2109
+ // const transactionDate = new Date();
2110
+ // //Initialise new JournalEntry
2111
+ // const journalEntry = new JournalEntry(dbTransaction);
2112
+ // journalEntry.init({
2113
+ // CompanyId: this.CompanyId,
2114
+ // Name: 'Collect-payments for ' + payment.PaymentId,
2115
+ // });
2116
+
2117
+ // // For each Payment.PaymentPaidWith:
2118
+ // for (const paymentPaidWithItem of paymentPaidWithItems) {
2119
+ // const paymentMethodType = await PaymentMethodType.initMethodType(
2120
+ // dbTransaction,
2121
+ // paymentPaidWithItem.MethodTypeId,
2122
+ // );
2123
+
2124
+ // //Initialise new Debit LedgerTransaction by calling JournalEntry.newLedgerTransaction() method and set below attributes:
2125
+ // const debitLT = await journalEntry.newLedgerTransaction(
2126
+ // TransactionTypeOptions.DEBIT,
2127
+ // );
2128
+ // debitLT.AccountNo = paymentMethodType.AccountNo;
2129
+ // debitLT.Currency = paymentPaidWithItem.Currency;
2130
+ // debitLT.DebitAmount = paymentPaidWithItem.Amount;
2131
+ // debitLT.Date = transactionDate;
2132
+ // debitLT.Description = 'Payment Received';
2133
+ // debitLT.RelatedPaymentId = payment.PaymentId;
2134
+ // }
2135
+
2136
+ // //For each creditTransactions, Initialise new Credit LedgerTransaction by calling JournalEntry.newLedgerTransaction() method
2137
+ // for (const ct of creditTransaction) {
2138
+ // const creditLT = await journalEntry.newLedgerTransaction(
2139
+ // TransactionTypeOptions.CREDIT,
2140
+ // );
2141
+ // creditLT.AccountNo = ct.AccountNo;
2142
+ // creditLT.Currency = ct.Currency;
2143
+ // creditLT.CreditAmount = ct.Amount;
2144
+ // creditLT.Date = transactionDate;
2145
+ // creditLT.Description = 'Payment Received';
2146
+ // creditLT.RelatedPaymentId = payment.PaymentId;
2147
+ // }
2148
+
2149
+ // // Call this.postJournal()
2150
+ // await this.postJournal(dbTransaction, journalEntry, loginUser);
2151
+
2152
+ return payment;
2153
+ } catch (error) {
2154
+ throw error;
2155
+ }
2156
+ }
2157
+
2158
+ /**
2159
+ * Confirms a payment and performs necessary actions such as creating a payment receipt and registering journal entries.
2160
+ * @param dbTransaction - The database transaction object.
2161
+ * @param loginUser - The logged-in user object.
2162
+ * @param customer - The customer object.
2163
+ * @param payment - The payment object.
2164
+ * @param status - The status of the payment (CONFIRMED, REJECTED, or FAILED).
2165
+ * @param remarks - Additional remarks for the payment.
2166
+ * @param ctAccountNo - Optional. The account number for the credit transaction.
2167
+ * @param receiptNo - Optional. The receipt number for the payment.
2168
+ * @returns A promise that resolves when the payment is confirmed.
2169
+ * @throws Throws an error if the user does not have the necessary privileges, if the payment is not found, if the payment status is not 'Pending', or if an invalid status is provided.
2170
+ */
2171
+ async confirmPayment(
2172
+ dbTransaction: any,
2173
+ loginUser: LoginUserBase,
2174
+ customer: FinanceCustomerBase,
2175
+ payment: Payment,
2176
+ status:
2177
+ | PaymentStatus.CONFIRMED
2178
+ | PaymentStatus.REJECTED
2179
+ | PaymentStatus.FAILED,
2180
+ remarks: string,
2181
+ ctAccountNo?: string,
2182
+ receiptNo?: string,
2183
+ ): Promise<void> {
2184
+ try {
2185
+ this._DbTransaction = dbTransaction;
2186
+ const systemCode =
2187
+ ApplicationConfig.getComponentConfigValue('system-code');
2188
+
2189
+ const isPrivileged = await loginUser.checkPrivileges(
2190
+ systemCode,
2191
+ 'FinanceCompany - Confirm Payment',
2192
+ );
2193
+ if (!isPrivileged) {
2194
+ throw new ClassError(
2195
+ 'FinanceCompany',
2196
+ 'FinanceCompanyConfirmPaymentErrMsg00',
2197
+ `You do not have 'Payment - Confirm' privilege.`,
2198
+ );
2199
+ }
2200
+
2201
+ if (payment.PaymentId === 'New') {
2202
+ throw new ClassError(
2203
+ 'FinanceCompany',
2204
+ 'FinanceCompanyConfirmPaymentErrMsg01',
2205
+ `Payment not found.`,
2206
+ );
2207
+ }
2208
+
2209
+ if (payment.Status !== PaymentStatus.PENDING) {
2210
+ throw new ClassError(
2211
+ 'Payment',
2212
+ 'PaymentConfirmPaymentErrMsg02',
2213
+ `Payment status is not 'Pending'.`,
2214
+ );
2215
+ }
2216
+
2217
+ payment.Remarks = remarks;
2218
+ let receiptDocNo = null;
2219
+ switch (status) {
2220
+ case PaymentStatus.REJECTED:
2221
+ payment.Status = PaymentStatus.REJECTED;
2222
+ break;
2223
+ case PaymentStatus.FAILED:
2224
+ payment.Status = PaymentStatus.FAILED;
2225
+ break;
2226
+ case PaymentStatus.CONFIRMED:
2227
+ payment.Status = PaymentStatus.CONFIRMED;
2228
+ //Creating the payment receipt
2229
+ /*Generating the receipt*/
2230
+ const receiptDocuments: any =
2231
+ await FinanceCompany._DocumentRepository.findAll({
2232
+ where: {
2233
+ DocType: DocType.RECEIPT,
2234
+ CompanyId: this.ObjectId,
2235
+ },
2236
+ });
2237
+
2238
+ const receipt = new Document(dbTransaction);
2239
+
2240
+ if (receiptNo) {
2241
+ receipt.DocNo = receiptNo;
2242
+ } else {
2243
+ receipt.DocNo = `EZC-RCT-${receiptDocuments.length + 1}`;
2244
+ }
2245
+ receipt.DocType = DocType.RECEIPT;
2246
+ receipt.DocDate = new Date();
2247
+ receipt.CompanyId = this.ObjectId;
2248
+ receipt.Currency = payment.Currency;
2249
+ receipt.Description = 'Payment Received';
2250
+ receipt.IssuedById = loginUser.ObjectId;
2251
+ receipt.IssuedToId = customer.CustomerId;
2252
+ receipt.IssuedToType = type(customer);
2253
+ receipt.RelatedObjectId = payment.RelatedObjectId;
2254
+ receipt.RelatedObjectType = payment.RelatedObjectType;
2255
+ receipt.UseAccSystemDocYN = 'N';
2256
+ receipt.CreatedById = loginUser.ObjectId;
2257
+ receipt.UpdatedById = loginUser.ObjectId;
2258
+
2259
+ const paymentItems = await payment.getPaymentItems(dbTransaction);
2260
+ for (const paymentItem of paymentItems) {
2261
+ const receiptItem = new DocumentItem(dbTransaction, receipt);
2262
+ receiptItem.Name = `Payment for ${paymentItem.PayForObjectType} No. ${paymentItem.PayForObjectId}`;
2263
+ receiptItem.NameBM = `Bayaran untuk ${paymentItem.PayForObjectType} No. ${paymentItem.PayForObjectId}`;
2264
+ receiptItem.Description = '-';
2265
+ receiptItem.Currency = paymentItem.Currency;
2266
+ receiptItem.UnitPrice = paymentItem.Amount;
2267
+ receiptItem.Quantity = 1;
2268
+ receiptItem.Amount = receiptItem.UnitPrice * receiptItem.Quantity;
2269
+ receiptItem.ItemId = receipt.DocNo;
2270
+ receiptItem.ItemType = type(payment);
2271
+ receiptItem.CtAccountNo = ctAccountNo;
2272
+
2273
+ await paymentItem.paid(dbTransaction);
2274
+ await receipt.newDocumentItem(receiptItem);
2275
+ }
2276
+
2277
+ const receiptMedia = await receipt.generateReceipt(
2278
+ receipt.IssuedById,
2279
+ customer,
2280
+ dbTransaction,
2281
+ );
2282
+
2283
+ /*Saving the receipt document and document items to the database*/
2284
+ await FinanceCompany._DocumentRepository.create(
2285
+ {
2286
+ DocNo: receipt.DocNo,
2287
+ DocType: receipt.DocType,
2288
+ DocDate: receipt.DocDate,
2289
+ CompanyId: receipt.CompanyId,
2290
+ Currency: receipt.Currency,
2291
+ Amount: receipt.Amount,
2292
+ Description: receipt.Description,
2293
+ Status: receipt.Status,
2294
+ IssuedById: receipt.IssuedById,
2295
+ IssuedToId: receipt.IssuedToId,
2296
+ IssuedToType: receipt.IssuedToType,
2297
+ RelatedObjectId: receipt.RelatedObjectId,
2298
+ RelatedObjectType: receipt.RelatedObjectType,
2299
+ CreatedById: receipt.CreatedById,
2300
+ CreatedAt: new Date(),
2301
+ UpdatedById: receipt.UpdatedById,
2302
+ UpdatedAt: new Date(),
2303
+ DocPDFFileMediaId: receiptMedia.PDFMedia.MediaId,
2304
+ DocHTMLFileMediaId: receiptMedia.HTMLMedia.MediaId,
2305
+ AccSystemRefId: receipt.AccSystemRefId,
2306
+ PostedToAccSystemYN: receipt.PostedToAccSystemYN,
2307
+ PostedById:
2308
+ receipt.PostedToAccSystemYN == 'Y' ? receipt.PostedById : null,
2309
+ PostedDateTime: new Date(),
2310
+ UseAccSystemDocYN: receipt.UseAccSystemDocYN,
2311
+ },
2312
+ {
2313
+ transaction: dbTransaction,
2314
+ },
2315
+ );
2316
+
2317
+ const receiptItems = await receipt.getDocumentItems(dbTransaction);
2318
+
2319
+ for (const receiptItem of receiptItems) {
2320
+ await FinanceCompany._DocumentItemRepository.create(
2321
+ {
2322
+ DocumentItemId: this._createId().toUpperCase(),
2323
+ DocNo: receipt.DocNo,
2324
+ Name: receiptItem.Name,
2325
+ NameBM: receiptItem.NameBM,
2326
+ Description: receiptItem.Description,
2327
+ ItemId: receiptItem.ItemId,
2328
+ ItemType: receiptItem.ItemType,
2329
+ ItemSKU: receiptItem.ItemSKU,
2330
+ ItemSerialNo: receiptItem.ItemSerialNo,
2331
+ Currency: receiptItem.Currency,
2332
+ UnitPrice: receiptItem.UnitPrice,
2333
+ Quantity: receiptItem.Quantity,
2334
+ QuantityUOM: receiptItem.QuantityUOM,
2335
+ Amount: receiptItem.Amount,
2336
+ TaxCode: receiptItem.TaxCode,
2337
+ TaxAmount: receiptItem.TaxAmount,
2338
+ TaxRate: receiptItem.TaxRate,
2339
+ TaxInclusiveYN: receiptItem.TaxInclusiveYN,
2340
+ DtAccountNo: receiptItem.DtAccountNo
2341
+ ? receiptItem.DtAccountNo
2342
+ : null,
2343
+ CtAccountNo: receiptItem.CtAccountNo
2344
+ ? receiptItem.CtAccountNo
2345
+ : null,
2346
+ },
2347
+ {
2348
+ transaction: dbTransaction,
2349
+ },
2350
+ );
2351
+ }
2352
+ //Set the receipt doc no to the payment
2353
+ payment.ReceiptDocNo = receipt.DocNo;
2354
+ receiptDocNo = receipt.DocNo;
2355
+
2356
+ /*Registering the Journal Entries for the transaction*/
2357
+ const transactionDate = new Date();
2358
+ const paymentPaidWithItems = await payment.getPaymentPaidWith(
2359
+ dbTransaction,
2360
+ );
2361
+
2362
+ for (const paymentPaidWith of paymentPaidWithItems) {
2363
+ let paymentMethodType = await PaymentMethodType.initMethodType(
2364
+ dbTransaction,
2365
+ paymentPaidWith.MethodTypeId,
2366
+ );
2367
+
2368
+ const journalEntry = new JournalEntry(dbTransaction);
2369
+
2370
+ journalEntry.init({
2371
+ CompanyId: this.CompanyId,
2372
+ Name: 'Collect-payments for ' + payment.PaymentId,
2373
+ });
2374
+
2375
+ const debitLT = await journalEntry.newLedgerTransaction(
2376
+ TransactionTypeOptions.DEBIT,
2377
+ );
2378
+ debitLT.AccountNo = paymentMethodType.AccountNo;
2379
+ debitLT.Currency = paymentPaidWith.Currency;
2380
+ debitLT.DebitAmount = paymentPaidWith.Amount;
2381
+ debitLT.Date = transactionDate;
2382
+ debitLT.Description = 'Payment Received'; //customer.FullName;
2383
+ debitLT.Name = customer.FullName;
2384
+ debitLT.RelatedObjectId = payment.PaymentId;
2385
+ debitLT.RelatedObjectType = 'Payment';
2386
+ debitLT.RelatedPaymentId = payment.PaymentId;
2387
+
2388
+ const creditLT = await journalEntry.newLedgerTransaction(
2389
+ TransactionTypeOptions.CREDIT,
2390
+ );
2391
+
2392
+ if (ctAccountNo) {
2393
+ creditLT.AccountNo = ctAccountNo;
2394
+ } else {
2395
+ const arAccount = await customer.getAccountReceivable();
2396
+ creditLT.AccountNo = arAccount.AccountNo;
2397
+ }
2398
+ creditLT.Currency = paymentPaidWith.Currency;
2399
+ creditLT.CreditAmount = paymentPaidWith.Amount;
2400
+ creditLT.Date = transactionDate;
2401
+ creditLT.Description = 'Payment Received'; //paymentMethodType.Name;
2402
+ creditLT.Name = paymentMethodType.Name;
2403
+ creditLT.RelatedObjectId = payment.PaymentId;
2404
+ creditLT.RelatedObjectType = type(payment);
2405
+ creditLT.RelatedPaymentId = payment.PaymentId;
2406
+
2407
+ await this.postJournal(dbTransaction, journalEntry, loginUser);
2408
+ }
2409
+ break;
2410
+ default:
2411
+ throw new ClassError(
2412
+ 'FinanceCompany',
2413
+ 'FinanceCompanyConfirmPaymentErrMsg03',
2414
+ `Invalid status.`,
2415
+ );
2416
+ }
2417
+
2418
+ await FinanceCompany._PaymentRepository.update(
2419
+ {
2420
+ ReceiptDocNo: receiptDocNo,
2421
+ Status: payment.Status,
2422
+ UpdatedAt: new Date(),
2423
+ UpdatedBy: loginUser.ObjectId,
2424
+ },
2425
+ {
2426
+ where: {
2427
+ PaymentId: payment.PaymentId,
2428
+ },
2429
+ transaction: dbTransaction,
2430
+ },
2431
+ );
2432
+ } catch (error) {
2433
+ throw error;
2434
+ }
2435
+ }
2436
+ }