@tomei/finance 0.3.90 → 0.3.92

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 (287) hide show
  1. package/.commitlintrc.json +22 -22
  2. package/.eslintrc.js +72 -72
  3. package/.husky/commit-msg +4 -4
  4. package/.husky/pre-commit +4 -4
  5. package/.prettierrc +4 -4
  6. package/CONTRIBUTING.md +30 -30
  7. package/LICENSE +21 -21
  8. package/README.md +13 -13
  9. package/configs/config.js +336 -336
  10. package/dist/account/account.js +9 -2
  11. package/dist/account/account.js.map +1 -1
  12. package/dist/customer/customer.d.ts +2 -1
  13. package/dist/customer/customer.js +10 -7
  14. package/dist/customer/customer.js.map +1 -1
  15. package/dist/finance-company/finance-company.js +4 -0
  16. package/dist/finance-company/finance-company.js.map +1 -1
  17. package/dist/tsconfig.tsbuildinfo +1 -1
  18. package/invoice-template/assets/css/style.css.map +12 -12
  19. package/invoice-template/assets/css/style.min.css +1 -1
  20. package/invoice-template/assets/img/arrow_bg.svg +11 -11
  21. package/invoice-template/assets/img/coffy_shop_img.svg +18 -18
  22. package/invoice-template/assets/img/logo_accent.svg +3 -3
  23. package/invoice-template/assets/img/logo_white.svg +4 -4
  24. package/invoice-template/assets/img/sign.svg +12 -12
  25. package/invoice-template/assets/js/html2canvas.min.js +10379 -10379
  26. package/invoice-template/assets/js/jquery.min.js +1 -1
  27. package/invoice-template/assets/sass/common/_color_variable.scss +12 -12
  28. package/invoice-template/assets/sass/common/_typography.scss +178 -178
  29. package/invoice-template/assets/sass/style.scss +12 -12
  30. package/migrations/finance-account-migration.js +97 -97
  31. package/migrations/finance-company-migration.js +29 -29
  32. package/migrations/finance-customer-migration.js +51 -51
  33. package/migrations/finance-document-item-migration.js +111 -111
  34. package/migrations/finance-document-migration.js +116 -116
  35. package/migrations/finance-payment-item-migration.js +52 -52
  36. package/migrations/finance-payment-method-migration.js +31 -31
  37. package/migrations/finance-payment-method-type-migration.js +55 -55
  38. package/migrations/finance-payment-migration.js +74 -74
  39. package/migrations/finance-post-history-migration.js +45 -45
  40. package/nest-cli.json +19 -19
  41. package/package.json +79 -79
  42. package/src/account/account.repository.ts +11 -11
  43. package/src/account/account.ts +264 -251
  44. package/src/account/interfaces/account-attr.interface.ts +31 -31
  45. package/src/account-system-entity/account-system-entity.ts +65 -65
  46. package/src/account-system-entity/post-history.repository.ts +11 -11
  47. package/src/config.ts +369 -369
  48. package/src/customer/customer.ts +277 -266
  49. package/src/customer/finance-customer.repository.ts +13 -13
  50. package/src/customer/interfaces/customer.repository.interface.ts +3 -3
  51. package/src/customer/interfaces/finance-customer-attr.interface.ts +10 -10
  52. package/src/customer/interfaces/finance-customer.repository.interface.ts +4 -4
  53. package/src/database.ts +15 -15
  54. package/src/document/document-item.repository.ts +11 -11
  55. package/src/document/document.repository.ts +11 -11
  56. package/src/document/interfaces/document-attr.interface.ts +28 -28
  57. package/src/document/interfaces/document-item-attr.interface.ts +22 -22
  58. package/src/document/interfaces/document-item.repository.interface.ts +4 -4
  59. package/src/enum/doc-type.enum.ts +8 -8
  60. package/src/enum/index.ts +17 -17
  61. package/src/enum/payment-method.enum.ts +3 -3
  62. package/src/enum/payment-type.enum.ts +4 -4
  63. package/src/finance-company/finance-company.repository.ts +11 -11
  64. package/src/finance-company/finance-company.ts +1572 -1560
  65. package/src/helpers/typeof.ts +29 -29
  66. package/src/index.ts +30 -30
  67. package/src/interfaces/account-system.interface.ts +20 -20
  68. package/src/interfaces/index.ts +3 -3
  69. package/src/models/account.entity.ts +206 -206
  70. package/src/models/customer.entity.ts +93 -93
  71. package/src/models/document-item.entity.ts +143 -143
  72. package/src/models/document.entity.ts +203 -203
  73. package/src/models/finance-company.entity.ts +25 -25
  74. package/src/models/journal-entry.entity.ts +110 -110
  75. package/src/models/ledger-transaction.entity.ts +148 -148
  76. package/src/models/payment-item.entity.ts +60 -60
  77. package/src/models/payment-method-type.entity.ts +70 -70
  78. package/src/models/payment-method.entity.ts +51 -51
  79. package/src/models/payment.entity.ts +129 -129
  80. package/src/models/post-history.entity.ts +41 -41
  81. package/src/payment/interfaces/payment-attr.interface.ts +19 -19
  82. package/src/payment/interfaces/payment-params.interface.ts +8 -8
  83. package/src/payment/payment.repository.ts +11 -11
  84. package/src/payment/payment.ts +200 -200
  85. package/src/payment-item/interfaces/payment-item-attr.interface.ts +10 -10
  86. package/src/payment-item/payment-item.repository.ts +11 -11
  87. package/src/payment-item/payment-item.ts +58 -58
  88. package/src/payment-method/payment-method.repository.ts +11 -11
  89. package/src/payment-method-type/payment-method-type.repository.ts +11 -11
  90. package/tsconfig.build.json +4 -4
  91. package/tsconfig.json +22 -22
  92. package/tslint.json +18 -18
  93. package/dist/account/entities/account.entity.d.ts +0 -26
  94. package/dist/account/entities/account.entity.js +0 -199
  95. package/dist/account/entities/account.entity.js.map +0 -1
  96. package/dist/account/entities/fee-associated-object.entity.d.ts +0 -6
  97. package/dist/account/entities/fee-associated-object.entity.js +0 -52
  98. package/dist/account/entities/fee-associated-object.entity.js.map +0 -1
  99. package/dist/account/index.d.ts +0 -6
  100. package/dist/account/index.js +0 -10
  101. package/dist/account/index.js.map +0 -1
  102. package/dist/account/interfaces/account.repository.interface.d.ts +0 -3
  103. package/dist/account/interfaces/account.repository.interface.js +0 -3
  104. package/dist/account/interfaces/account.repository.interface.js.map +0 -1
  105. package/dist/account-system-entity/index.d.ts +0 -2
  106. package/dist/account-system-entity/index.js +0 -6
  107. package/dist/account-system-entity/index.js.map +0 -1
  108. package/dist/account-system-entity/post-history.entity.d.ts +0 -11
  109. package/dist/account-system-entity/post-history.entity.js +0 -65
  110. package/dist/account-system-entity/post-history.entity.js.map +0 -1
  111. package/dist/base/account-system.interface.d.ts +0 -7
  112. package/dist/base/account-system.interface.js +0 -3
  113. package/dist/base/account-system.interface.js.map +0 -1
  114. package/dist/base/address.base.abstract.d.ts +0 -4
  115. package/dist/base/address.base.abstract.js +0 -10
  116. package/dist/base/address.base.abstract.js.map +0 -1
  117. package/dist/base/base.address.d.ts +0 -8
  118. package/dist/base/base.address.js +0 -15
  119. package/dist/base/base.address.js.map +0 -1
  120. package/dist/base/base.object.d.ts +0 -7
  121. package/dist/base/base.object.js +0 -14
  122. package/dist/base/base.object.js.map +0 -1
  123. package/dist/base/base.owner.d.ts +0 -23
  124. package/dist/base/base.owner.js +0 -34
  125. package/dist/base/base.owner.js.map +0 -1
  126. package/dist/base/base.repository.abstract.d.ts +0 -12
  127. package/dist/base/base.repository.abstract.js +0 -22
  128. package/dist/base/base.repository.abstract.js.map +0 -1
  129. package/dist/base/base.repository.interface.d.ts +0 -9
  130. package/dist/base/base.repository.interface.js +0 -3
  131. package/dist/base/base.repository.interface.js.map +0 -1
  132. package/dist/base/index.d.ts +0 -4
  133. package/dist/base/index.js +0 -10
  134. package/dist/base/index.js.map +0 -1
  135. package/dist/base/object/base.object.abstract.d.ts +0 -5
  136. package/dist/base/object/base.object.abstract.js +0 -7
  137. package/dist/base/object/base.object.abstract.js.map +0 -1
  138. package/dist/base/object/base.object.d.ts +0 -7
  139. package/dist/base/object/base.object.js +0 -14
  140. package/dist/base/object/base.object.js.map +0 -1
  141. package/dist/base/object/object.interface.d.ts +0 -4
  142. package/dist/base/object/object.interface.js +0 -3
  143. package/dist/base/object/object.interface.js.map +0 -1
  144. package/dist/base/object.base.abstract.d.ts +0 -5
  145. package/dist/base/object.base.abstract.js +0 -11
  146. package/dist/base/object.base.abstract.js.map +0 -1
  147. package/dist/base/owner/base.owner.abstract.d.ts +0 -20
  148. package/dist/base/owner/base.owner.abstract.js +0 -8
  149. package/dist/base/owner/base.owner.abstract.js.map +0 -1
  150. package/dist/base/owner/base.owner.d.ts +0 -22
  151. package/dist/base/owner/base.owner.js +0 -31
  152. package/dist/base/owner/base.owner.js.map +0 -1
  153. package/dist/base/owner/owner.interface.d.ts +0 -19
  154. package/dist/base/owner/owner.interface.js +0 -3
  155. package/dist/base/owner/owner.interface.js.map +0 -1
  156. package/dist/base/person.base.abstract.d.ts +0 -21
  157. package/dist/base/person.base.abstract.js +0 -17
  158. package/dist/base/person.base.abstract.js.map +0 -1
  159. package/dist/base/repository/base.repository.abstract.d.ts +0 -12
  160. package/dist/base/repository/base.repository.abstract.js +0 -22
  161. package/dist/base/repository/base.repository.abstract.js.map +0 -1
  162. package/dist/base/repository/base.repository.interface.d.ts +0 -9
  163. package/dist/base/repository/base.repository.interface.js +0 -3
  164. package/dist/base/repository/base.repository.interface.js.map +0 -1
  165. package/dist/customer/customer.repository.d.ts +0 -0
  166. package/dist/customer/customer.repository.js +0 -1
  167. package/dist/customer/customer.repository.js.map +0 -1
  168. package/dist/customer/entities/customer.entity.d.ts +0 -13
  169. package/dist/customer/entities/customer.entity.js +0 -111
  170. package/dist/customer/entities/customer.entity.js.map +0 -1
  171. package/dist/customer/index.d.ts +0 -7
  172. package/dist/customer/index.js +0 -10
  173. package/dist/customer/index.js.map +0 -1
  174. package/dist/document/entities/document-item.entity.d.ts +0 -27
  175. package/dist/document/entities/document-item.entity.js +0 -174
  176. package/dist/document/entities/document-item.entity.js.map +0 -1
  177. package/dist/document/entities/document.entity.d.ts +0 -22
  178. package/dist/document/entities/document.entity.js +0 -139
  179. package/dist/document/entities/document.entity.js.map +0 -1
  180. package/dist/document/index.d.ts +0 -11
  181. package/dist/document/index.js +0 -20
  182. package/dist/document/index.js.map +0 -1
  183. package/dist/document/interfaces/document.repository.interface.d.ts +0 -3
  184. package/dist/document/interfaces/document.repository.interface.js +0 -3
  185. package/dist/document/interfaces/document.repository.interface.js.map +0 -1
  186. package/dist/enum/document-type.enum.d.ts +0 -7
  187. package/dist/enum/document-type.enum.js +0 -12
  188. package/dist/enum/document-type.enum.js.map +0 -1
  189. package/dist/enum/intuit-client.enum.d.ts +0 -13
  190. package/dist/enum/intuit-client.enum.js +0 -19
  191. package/dist/enum/intuit-client.enum.js.map +0 -1
  192. package/dist/finance-company/entities/finance-company.entity.d.ts +0 -13
  193. package/dist/finance-company/entities/finance-company.entity.js +0 -113
  194. package/dist/finance-company/entities/finance-company.entity.js.map +0 -1
  195. package/dist/finance-company/finance-company.entity.d.ts +0 -13
  196. package/dist/finance-company/finance-company.entity.js +0 -66
  197. package/dist/finance-company/finance-company.entity.js.map +0 -1
  198. package/dist/finance-company/index.d.ts +0 -3
  199. package/dist/finance-company/index.js +0 -8
  200. package/dist/finance-company/index.js.map +0 -1
  201. package/dist/finance-company/interfaces/finance-company-attr.interface.d.ts +0 -10
  202. package/dist/finance-company/interfaces/finance-company-attr.interface.js +0 -3
  203. package/dist/finance-company/interfaces/finance-company-attr.interface.js.map +0 -1
  204. package/dist/finance-company/interfaces/finance-company.repository.interface.d.ts +0 -3
  205. package/dist/finance-company/interfaces/finance-company.repository.interface.js +0 -3
  206. package/dist/finance-company/interfaces/finance-company.repository.interface.js.map +0 -1
  207. package/dist/interfaces/account-system-entity.interface.d.ts +0 -7
  208. package/dist/interfaces/account-system-entity.interface.js +0 -3
  209. package/dist/interfaces/account-system-entity.interface.js.map +0 -1
  210. package/dist/interfaces/customer.repository.interface.d.ts +0 -3
  211. package/dist/interfaces/customer.repository.interface.js +0 -3
  212. package/dist/interfaces/customer.repository.interface.js.map +0 -1
  213. package/dist/interfaces/finance-customer.repository.interface.d.ts +0 -3
  214. package/dist/interfaces/finance-customer.repository.interface.js +0 -3
  215. package/dist/interfaces/finance-customer.repository.interface.js.map +0 -1
  216. package/dist/interfaces/systemConfig.interface.d.ts +0 -0
  217. package/dist/interfaces/systemConfig.interface.js +0 -1
  218. package/dist/interfaces/systemConfig.interface.js.map +0 -1
  219. package/dist/intuit-client/client.d.ts +0 -14
  220. package/dist/intuit-client/client.js +0 -44
  221. package/dist/intuit-client/client.js.map +0 -1
  222. package/dist/journal-entry/entities/journal-entry.entity.d.ts +0 -16
  223. package/dist/journal-entry/entities/journal-entry.entity.js +0 -129
  224. package/dist/journal-entry/entities/journal-entry.entity.js.map +0 -1
  225. package/dist/journal-entry/index.d.ts +0 -6
  226. package/dist/journal-entry/index.js +0 -10
  227. package/dist/journal-entry/index.js.map +0 -1
  228. package/dist/journal-entry/interfaces/journal-entry.repository.interface.d.ts +0 -10
  229. package/dist/journal-entry/interfaces/journal-entry.repository.interface.js +0 -3
  230. package/dist/journal-entry/interfaces/journal-entry.repository.interface.js.map +0 -1
  231. package/dist/ledger-transaction/entities/ledger-transaction.entity.d.ts +0 -19
  232. package/dist/ledger-transaction/entities/ledger-transaction.entity.js +0 -157
  233. package/dist/ledger-transaction/entities/ledger-transaction.entity.js.map +0 -1
  234. package/dist/ledger-transaction/index.d.ts +0 -6
  235. package/dist/ledger-transaction/index.js +0 -10
  236. package/dist/ledger-transaction/index.js.map +0 -1
  237. package/dist/payment/entities/payment-item.entity.d.ts +0 -10
  238. package/dist/payment/entities/payment-item.entity.js +0 -62
  239. package/dist/payment/entities/payment-item.entity.js.map +0 -1
  240. package/dist/payment/entities/payment.entity.d.ts +0 -25
  241. package/dist/payment/entities/payment.entity.js +0 -152
  242. package/dist/payment/entities/payment.entity.js.map +0 -1
  243. package/dist/payment/index.d.ts +0 -10
  244. package/dist/payment/index.js +0 -18
  245. package/dist/payment/index.js.map +0 -1
  246. package/dist/payment/interfaces/payment-item-attr.interface.d.ts +0 -8
  247. package/dist/payment/interfaces/payment-item-attr.interface.js +0 -7
  248. package/dist/payment/interfaces/payment-item-attr.interface.js.map +0 -1
  249. package/dist/payment/interfaces/payment-item.repository.interface.d.ts +0 -3
  250. package/dist/payment/interfaces/payment-item.repository.interface.js +0 -3
  251. package/dist/payment/interfaces/payment-item.repository.interface.js.map +0 -1
  252. package/dist/payment/interfaces/payment.repository.interface.d.ts +0 -3
  253. package/dist/payment/interfaces/payment.repository.interface.js +0 -3
  254. package/dist/payment/interfaces/payment.repository.interface.js.map +0 -1
  255. package/dist/payment/payment-item.repository.d.ts +0 -5
  256. package/dist/payment/payment-item.repository.js +0 -12
  257. package/dist/payment/payment-item.repository.js.map +0 -1
  258. package/dist/payment-item/interfaces/payment-item.repository.interface.d.ts +0 -3
  259. package/dist/payment-item/interfaces/payment-item.repository.interface.js +0 -3
  260. package/dist/payment-item/interfaces/payment-item.repository.interface.js.map +0 -1
  261. package/dist/quickbook-client/client.d.ts +0 -39
  262. package/dist/quickbook-client/client.js +0 -205
  263. package/dist/quickbook-client/client.js.map +0 -1
  264. package/dist/quickbook-client/constant.d.ts +0 -1
  265. package/dist/quickbook-client/constant.js +0 -5
  266. package/dist/quickbook-client/constant.js.map +0 -1
  267. package/dist/quickbook-client/index.d.ts +0 -5
  268. package/dist/quickbook-client/index.js +0 -6
  269. package/dist/quickbook-client/index.js.map +0 -1
  270. package/dist/quickbook-client/interfaces/quickbook-client-call-options.interface.d.ts +0 -6
  271. package/dist/quickbook-client/interfaces/quickbook-client-call-options.interface.js +0 -3
  272. package/dist/quickbook-client/interfaces/quickbook-client-call-options.interface.js.map +0 -1
  273. package/dist/quickbook-client/interfaces/quickbook-client-create-account-options.interface.d.ts +0 -9
  274. package/dist/quickbook-client/interfaces/quickbook-client-create-account-options.interface.js +0 -3
  275. package/dist/quickbook-client/interfaces/quickbook-client-create-account-options.interface.js.map +0 -1
  276. package/dist/quickbook-client/interfaces/quickbook-client-create-customer-options.interface.d.ts +0 -7
  277. package/dist/quickbook-client/interfaces/quickbook-client-create-customer-options.interface.js +0 -3
  278. package/dist/quickbook-client/interfaces/quickbook-client-create-customer-options.interface.js.map +0 -1
  279. package/dist/quickbook-client/interfaces/quickbook-client-options.interface.d.ts +0 -8
  280. package/dist/quickbook-client/interfaces/quickbook-client-options.interface.js +0 -3
  281. package/dist/quickbook-client/interfaces/quickbook-client-options.interface.js.map +0 -1
  282. package/dist/quickbook-client/quickbook-client.module-definition.d.ts +0 -2
  283. package/dist/quickbook-client/quickbook-client.module-definition.js +0 -9
  284. package/dist/quickbook-client/quickbook-client.module-definition.js.map +0 -1
  285. package/dist/quickbook-client/quickbook-client.module.d.ts +0 -6
  286. package/dist/quickbook-client/quickbook-client.module.js +0 -34
  287. package/dist/quickbook-client/quickbook-client.module.js.map +0 -1
@@ -1,1560 +1,1572 @@
1
- import axios from 'axios';
2
- import * as cuid from 'cuid';
3
- import {
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
- PaymentStatus,
20
- PaymentType,
21
- TransactionTypeOptions,
22
- } from '../enum';
23
- import PaymentMethodType from '../payment-method-type/payment-method-type';
24
- import { PaymentRepository } from '../payment/payment.repository';
25
- import { PaymentItemRepository } from '../payment-item/payment-item.repository';
26
- import Payment from '../payment/payment';
27
- import { DocumentRepository } from '../document/document.repository';
28
- import { DocumentItemRepository } from '../document/document-item.repository';
29
- import { PaymentMethodRepository } from '../payment-method/payment-method.repository';
30
- import { PaymentMethodTypeRepository } from '../payment-method-type/payment-method-type.repository';
31
- import PaymentMethod from '../payment-method/payment-method';
32
- import { AccountRepository } from '../account/account.repository';
33
- import { PaymentPaidWithRepository } from '../payment-paid-with/payment-paid-with.repository';
34
-
35
- // eslint-disable-next-line @typescript-eslint/no-var-requires
36
- const getConfig = require('../config');
37
- // eslint-disable-next-line @typescript-eslint/no-var-requires
38
- const config = getConfig();
39
-
40
- export default class FinanceCompany extends ObjectBase {
41
- private _CompanyId = 'New';
42
- private _CompSystemCode = '';
43
- private _CompSystemRefId = '';
44
- private _AccSystemCode = '';
45
- private static _htFinanceCompanyIds = new HashTable();
46
- private static _htFinanceCompanies = new HashTable();
47
- private static _financeCompanyRepository = new FinanceCompanyRepository();
48
- private static _PaymentRepository = new PaymentRepository();
49
- private static _PaymentItemRepository = new PaymentItemRepository();
50
- private static _PaymentPaidWithRepository = new PaymentPaidWithRepository();
51
- private static _PaymentMethodRepository = new PaymentMethodRepository();
52
- private static _PaymentMethodTypeRepository =
53
- new PaymentMethodTypeRepository();
54
- private static _DocumentRepository = new DocumentRepository();
55
- private static _DocumentItemRepository = new DocumentItemRepository();
56
- private static _FinanceCustomerRepository = new FinanceCustomerRepository();
57
- private static _LedgerTransactionRepository =
58
- new LedgerTransactionRepository();
59
-
60
- private static _AccountRepository = new AccountRepository();
61
-
62
- private _AccountingSystem: IAccountSystem;
63
-
64
- private _DbTransaction: any;
65
- private _PaymentMethods = [];
66
-
67
- get CompSystemCode(): string {
68
- return this._CompSystemCode;
69
- }
70
-
71
- private set CompSystemCode(code: string) {
72
- this._CompSystemCode = code;
73
- }
74
-
75
- get CompSystemRefId() {
76
- return this._CompSystemRefId;
77
- }
78
-
79
- set CompSystemRefId(id: string) {
80
- this._CompSystemRefId = id;
81
- }
82
-
83
- get AccSystemCode() {
84
- return this._AccSystemCode;
85
- }
86
-
87
- private set AccSystemCode(code: string) {
88
- this._AccSystemCode = code;
89
- }
90
-
91
- get CompanyId() {
92
- return this._CompanyId;
93
- }
94
-
95
- get ObjectId() {
96
- return this._CompanyId;
97
- }
98
-
99
- private set ObjectId(id: string) {
100
- this._CompanyId = id;
101
- }
102
-
103
- get ObjectName() {
104
- return `${this.CompSystemCode}-${this.CompSystemRefId}-${this.AccSystemCode}`;
105
- }
106
-
107
- get TableName() {
108
- return 'finance_Company';
109
- }
110
-
111
- get AccountingSystem(): IAccountSystem {
112
- return this._AccountingSystem;
113
- }
114
-
115
- set AccountingSystem(system: IAccountSystem) {
116
- this._AccountingSystem = system;
117
- }
118
-
119
- constructor(
120
- compSystemCode: string,
121
- compSystemRefId: string,
122
- accSystemCode: string,
123
- ) {
124
- super();
125
- this.CompSystemCode = compSystemCode;
126
- this.CompSystemRefId = compSystemRefId;
127
- this.AccSystemCode = accSystemCode;
128
- }
129
-
130
- static async getFinanceCompanyId(
131
- compSystemCode: string,
132
- compSystemRefId: string,
133
- accSystemCode: string,
134
- ): Promise<string> {
135
- let sCompanyId = '';
136
- /*Assemble the hashtable key*/
137
- const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
138
- /*Check if the FinanceCompany has previously being loaded*/
139
- if (!FinanceCompany._htFinanceCompanyIds.get(sKey)) {
140
- /*Instantiate a new FinanceCompany*/
141
- const financeCompany = new FinanceCompany(
142
- compSystemCode,
143
- compSystemRefId,
144
- accSystemCode,
145
- );
146
-
147
- /*Retrieve the finance company from finance_Company table using compSystemCode,
148
- * CompSystemRefId and accSystemCode */
149
- const company = await FinanceCompany._financeCompanyRepository.findOne({
150
- where: {
151
- CompSystemCode: compSystemCode,
152
- CompSystemRefId: compSystemRefId,
153
- AccSystemCode: accSystemCode,
154
- },
155
- });
156
-
157
- /*Retrieve and store the companyId from the result*/
158
- financeCompany.ObjectId = company.CompanyId;
159
- sCompanyId = financeCompany.ObjectId;
160
-
161
- /*Add the details into the hashtable*/
162
- FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
163
- FinanceCompany._htFinanceCompanies.add(sCompanyId, financeCompany);
164
- }
165
-
166
- if (typeof FinanceCompany._htFinanceCompanyIds.get(sKey) === 'string') {
167
- sCompanyId = FinanceCompany._htFinanceCompanyIds.get(sKey);
168
- }
169
-
170
- return sCompanyId;
171
- }
172
-
173
- static async getFinanceCompany(companyId: string): Promise<FinanceCompany> {
174
- /*Check if the finance company is previously be loaded*/
175
- if (!FinanceCompany._htFinanceCompanies.get(companyId)) {
176
- /*Retrieve the finance company from finance_Company table using compSystemCode,
177
- * CompSystemRefId and accSystemCode */
178
- const company = await FinanceCompany._financeCompanyRepository.findOne({
179
- where: { CompanyId: companyId },
180
- });
181
-
182
- if (!company) {
183
- throw Error('No finance company found. Please create first.');
184
- }
185
-
186
- /*Using the result returned, instantiate a new FinanceCompany*/
187
- const compSystemCode = company.CompSystemCode;
188
- const compSystemRefId = company.CompSystemRefId;
189
- const accSystemCode = company.AccSystemCode;
190
-
191
- const financeCompany = new FinanceCompany(
192
- compSystemCode,
193
- compSystemRefId,
194
- accSystemCode,
195
- );
196
-
197
- /*Retrieve and store the CompanyId from the result*/
198
- financeCompany.ObjectId = company.CompanyId;
199
- financeCompany._CompanyId = company.CompanyId;
200
-
201
- /*Add the details into the hashtable*/
202
- const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
203
- FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
204
- FinanceCompany._htFinanceCompanies.add(
205
- financeCompany.ObjectId,
206
- financeCompany,
207
- );
208
- }
209
- // tslint:disable-next-line:no-console
210
- console.log('return from hash table');
211
- return FinanceCompany._htFinanceCompanies.get(companyId);
212
- }
213
-
214
- static async createFinanceCompany(
215
- dbTransaction: any,
216
- loginUser: LoginUserBase,
217
- companyId: string,
218
- compSystemCode: string,
219
- compSystemRefId: string,
220
- accSystemCode: string,
221
- ) {
222
- /*Instantiate a new FinanceCompany*/
223
- const financeCompany = new FinanceCompany(
224
- compSystemCode,
225
- compSystemRefId,
226
- accSystemCode,
227
- );
228
-
229
- /*Validating if the finance company already exists in finance_Company*/
230
- const company = await FinanceCompany._financeCompanyRepository.findOne({
231
- where: {
232
- CompSystemCode: compSystemCode,
233
- CompSystemRefId: compSystemRefId,
234
- AccSystemCode: accSystemCode,
235
- },
236
- });
237
-
238
- if (company) {
239
- throw Error(
240
- 'There is already another Finance Company with the compSystemCode, CompSystemRefId and accSystemCode specified.',
241
- );
242
- }
243
-
244
- /*Generating the companyId*/
245
- financeCompany.ObjectId = companyId;
246
-
247
- /*Save the FinanceCompany to the finance_Company table*/
248
- await FinanceCompany._financeCompanyRepository.create(
249
- {
250
- CompanyId: financeCompany.CompanyId,
251
- CompSystemCode: financeCompany.CompSystemCode,
252
- CompSystemRefId: financeCompany.CompSystemRefId,
253
- AccSystemCode: financeCompany.AccSystemCode,
254
- },
255
- {
256
- transaction: dbTransaction,
257
- },
258
- );
259
-
260
- /*Add the details to hashtable*/
261
- const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
262
- FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
263
- FinanceCompany._htFinanceCompanies.add(
264
- financeCompany.ObjectId,
265
- financeCompany,
266
- );
267
-
268
- // tslint:disable-next-line:no-console
269
- console.log('return from hash table');
270
- return FinanceCompany._htFinanceCompanies.get(companyId);
271
- }
272
-
273
- async createCustomer(
274
- dbTransaction: any,
275
- custSystemCode: string,
276
- custSystemRefId: string,
277
- customer: FinanceCustomerBase,
278
- ) {
279
- try {
280
- if (!custSystemCode || !custSystemRefId) {
281
- throw new Error(
282
- 'CustSystemCode and CustomerRefId are required fields.',
283
- );
284
- }
285
-
286
- const financeCustomerData =
287
- await FinanceCompany._FinanceCustomerRepository.findOne({
288
- where: {
289
- CompanyId: this._CompanyId,
290
- CustSystemCode: custSystemCode,
291
- CustSystemRefId: custSystemRefId,
292
- },
293
- });
294
-
295
- if (financeCustomerData) {
296
- throw new Error('Customer already created previously.');
297
- }
298
-
299
- // Retrieve the type of accounting system, API Key, and API secret based on SysCode from the Config.ts
300
- const customerId = await this.AccountingSystem.createCustomer({
301
- customer,
302
- });
303
-
304
- customer.CompanyId = this._CompanyId;
305
-
306
- const newCustomer = await customer.save(
307
- customerId,
308
- custSystemCode,
309
- custSystemRefId,
310
- dbTransaction,
311
- );
312
-
313
- const payload = {
314
- Action: 'Create',
315
- Activity: 'Finance Customer Created',
316
- Description: `Customer (ID: ${newCustomer.CustomerId}) has been created for ${this.AccSystemCode}`,
317
- EntityType: 'finance_Customer',
318
- EntityValueBefore: JSON.stringify({}),
319
- EntityValueAfter: JSON.stringify(newCustomer),
320
- PerformedById: 'test',
321
- PerformedAt: new Date(),
322
- EntityId: newCustomer.CustomerId,
323
- };
324
-
325
- await axios.post(
326
- `${process.env.COMMON_API_URL}/activity-histories`,
327
- payload,
328
- );
329
-
330
- return customer;
331
- } catch (error) {
332
- throw error;
333
- }
334
- }
335
-
336
- async postJournal(dbTransaction: any, journalEntry: JournalEntry) {
337
- const debitTransactions = await journalEntry.DebitTransactions;
338
- const creditTransactions = await journalEntry.CreditTransactions;
339
- try {
340
- if (creditTransactions.length < 1 || debitTransactions?.length < 1) {
341
- throw new Error(
342
- 'There should be at least 1 debit ledger transaction and 1 credit ledger transaction in the journal entry',
343
- );
344
- }
345
-
346
- const totalCreditAmount = creditTransactions.reduce(
347
- (accumulator, currentValue) => accumulator + currentValue.CreditAmount,
348
- 0,
349
- );
350
- const totalDebitAmount = debitTransactions.reduce(
351
- (accumulator, currentValue) => accumulator + currentValue.DebitAmount,
352
- 0,
353
- );
354
-
355
- if (totalCreditAmount !== totalDebitAmount) {
356
- throw new Error(
357
- 'Credit ledger transaction and debit ledger transaction should the same amount',
358
- );
359
- }
360
-
361
- const newJournalEntry = await journalEntry.save('test', dbTransaction);
362
-
363
- for (const ledgerTransaction of debitTransactions) {
364
- ledgerTransaction.TransactionId = cuid();
365
- ledgerTransaction.JournalEntryId = newJournalEntry.JournalEntryId;
366
-
367
- await ledgerTransaction.save(dbTransaction);
368
- // const dt = await ledgerTransaction.newLedgerTransaction(
369
- // TransactionTypeOptions.DEBIT,
370
- // newJournalEntry.JournalEntryId,
371
- // );
372
- // await dt.save();
373
- }
374
-
375
- for (const ledgerTransaction of creditTransactions) {
376
- ledgerTransaction.TransactionId = cuid();
377
- ledgerTransaction.JournalEntryId = newJournalEntry.JournalEntryId;
378
-
379
- await ledgerTransaction.save(dbTransaction);
380
- // const ct = await ledgerTransaction.newLedgerTransaction(
381
- // TransactionTypeOptions.CREDIT,
382
- // newJournalEntry.JournalEntryId,
383
- // );
384
- // await ct.save();
385
- }
386
-
387
- await this.AccountingSystem.postJournalEntry(newJournalEntry);
388
-
389
- const payload = {
390
- Action: 'Create',
391
- Activity: 'Post Journal Entry',
392
- Description: `Journal Entry (ID: ${newJournalEntry.JournalEntryId}) has been created`,
393
- EntityType: 'JournalEntry',
394
- EntityValueBefore: JSON.stringify({}),
395
- EntityValueAfter: JSON.stringify(newJournalEntry),
396
- PerformedById: 'test',
397
- PerformedAt: new Date(),
398
- EntityId: newJournalEntry.JournalEntryId,
399
- };
400
-
401
- await axios.post(
402
- `${process.env.COMMON_API_URL}/activity-histories`,
403
- payload,
404
- );
405
- } catch (error) {
406
- throw error;
407
- }
408
- }
409
-
410
- async createAccount(dbTransaction: any, account: Account) {
411
- try {
412
- if (!account.AccountType) {
413
- throw new Error('AccountType is required.');
414
- }
415
-
416
- let createAccountPayload: any = {
417
- Name: account.Name,
418
- AcctNum: account.AccountNo,
419
- AccountType: account.AccountType,
420
- AccountSubType: account.AccountSubtype,
421
- };
422
-
423
- if (account.isParentAccountExists()) {
424
- createAccountPayload = {
425
- ...createAccountPayload,
426
- CurrencyRef: 'MYR',
427
- ParentRef: account.ParentAccountNo,
428
- SubAccount: true,
429
- };
430
- }
431
-
432
- const accSystemAccountId = await this.AccountingSystem.createAccount(
433
- createAccountPayload,
434
- );
435
- account.PostedToAccSystemYN = 'Y';
436
- const newAccount = await account.save(
437
- this.CompanyId,
438
- accSystemAccountId,
439
- 'test',
440
- dbTransaction,
441
- );
442
-
443
- const payload = {
444
- Action: 'Create',
445
- Activity: 'Account Created',
446
- Description: `Account (ID: ${newAccount.AccountNo}) has been created`,
447
- EntityType: 'Account',
448
- EntityValueBefore: JSON.stringify({}),
449
- EntityValueAfter: JSON.stringify(newAccount),
450
- PerformedById: 'test',
451
- PerformedAt: new Date(),
452
- EntityId: newAccount.AccountNo,
453
- };
454
-
455
- await axios.post(
456
- `${process.env.COMMON_API_URL}/activity-histories`,
457
- payload,
458
- );
459
-
460
- return account;
461
- } catch (error) {
462
- throw error;
463
- }
464
- }
465
-
466
- /**
467
- * Issue an invoice
468
- *
469
- * @param dbTransaction - The database transaction to be used
470
- * @param loginUser - The user issuing the invoice
471
- * @param invoice - The document containing the invoice details
472
- * @param customer - The customer to be issued the invoice
473
- * @param dtAccountNo - The account number of the Account Receivable (AR) account to debit. If not provided, will debit the customer's default AR account
474
- *
475
- * @returns {Document} - Document object representing the full details of the invoice issued
476
- */
477
- async issueInvoice(
478
- /*todo: loginUser & customer is NOT supposed to be optional */
479
- dbTransaction: any,
480
- invoice: Document,
481
- loginUser?: LoginUserBase,
482
- customer?: FinanceCustomerBase,
483
- dtAccountNo?: string,
484
- ): Promise<Document> {
485
- try {
486
- /*Check if the invoice number already exists (should be unique)*/
487
- const duplicateInvoice = await FinanceCompany._DocumentRepository.findOne(
488
- {
489
- where: {
490
- DocNo: invoice.DocNo,
491
- },
492
- transaction: dbTransaction,
493
- },
494
- );
495
-
496
- if (duplicateInvoice) {
497
- throw new Error('Invoice number already exists');
498
- }
499
-
500
- const documentItems = await invoice.DocumentItems;
501
-
502
- /*Check if the document has at least 1 document item*/
503
- if (!documentItems.length) {
504
- throw new Error('Document must have at least 1 document item');
505
- }
506
-
507
- /*Check if each document item has CtAccountNo provided*/
508
- for (const invoiceItem of documentItems) {
509
- if (!invoiceItem.CtAccountNo) {
510
- throw new Error(
511
- 'Each document item should have CtAccountNo provided',
512
- );
513
- }
514
- }
515
-
516
- /*Set up the document type*/
517
- invoice.DocType = DocType.INVOICE;
518
- // invoice.DocNo = cuid();
519
-
520
- /*Saving the document and document items to the database*/
521
- await FinanceCompany._DocumentRepository.create(
522
- {
523
- DocNo: invoice.DocNo,
524
- DocType: invoice.DocType,
525
- DocDate: invoice.DocDate,
526
- CompanyId: invoice.CompanyId,
527
- Currency: invoice.Currency,
528
- Amount: invoice.Amount,
529
- Description: invoice.Description,
530
- Status: invoice.Status,
531
- IssuedById: invoice.IssuedById,
532
- IssuedToId: invoice.IssuedToId,
533
- IssuedToType: invoice.IssuedToType,
534
- RelatedObjectId: invoice.RelatedObjectId,
535
- RelatedObjectType: invoice.RelatedObjectType,
536
- CreatedById: invoice.CreatedById,
537
- CreatedAt: new Date(),
538
- UpdatedById: invoice.UpdatedById,
539
- UpdatedAt: new Date(),
540
- DocPDFFileMediaId: invoice.DocPDFFileMediaId,
541
- DocHTMLFileMediaId: invoice.DocHTMLFileMediaId,
542
- AccSystemRefId: invoice.AccSystemRefId,
543
- PostedToAccSystemYN: invoice.PostedToAccSystemYN,
544
- PostedById: invoice.PostedById,
545
- PostedDateTime: new Date(),
546
- UseAccSystemDocYN: invoice.UseAccSystemDocYN,
547
- },
548
- {
549
- transaction: dbTransaction,
550
- },
551
- );
552
-
553
- for (const documentItem of documentItems) {
554
- await FinanceCompany._DocumentItemRepository.create(
555
- {
556
- DocumentItemId: cuid(),
557
- DocNo: invoice.DocNo,
558
- Name: documentItem.Name,
559
- NameBM: documentItem.NameBM,
560
- Description: documentItem.Description,
561
- ItemId: documentItem.ItemId,
562
- ItemType: documentItem.ItemType,
563
- ItemSKU: documentItem.ItemSKU,
564
- ItemSerialNo: documentItem.ItemSerialNo,
565
- Currency: documentItem.Currency,
566
- UnitPrice: documentItem.UnitPrice,
567
- Quantity: documentItem.Quantity,
568
- QuantityUOM: documentItem.QuantityUOM,
569
- Amount: documentItem.Amount,
570
- TaxCode: documentItem.TaxCode,
571
- TaxAmount: documentItem.TaxAmount,
572
- TaxRate: documentItem.TaxRate,
573
- TaxInclusiveYN: documentItem.TaxInclusiveYN,
574
- DtAccountNo: documentItem.DtAccountNo
575
- ? documentItem.DtAccountNo
576
- : null,
577
- CtAccountNo: documentItem.CtAccountNo
578
- ? documentItem.CtAccountNo
579
- : null,
580
- },
581
- {
582
- transaction: dbTransaction,
583
- },
584
- );
585
- }
586
-
587
- /*Generating the invoice*/
588
- if (invoice.UseAccSystemDocYN === 'Y') {
589
- /*todo: Posting to accounting system to generate invoice*/
590
- await this.AccountingSystem.createInvoice(invoice);
591
- } else {
592
- /*todo: check config file to see which invoice template is to be used for specific project*/
593
-
594
- /*Generating invoice based on template*/
595
- invoice.generateInvoice(invoice.IssuedById, customer);
596
- }
597
-
598
- const transactionDate = new Date();
599
- const htCreditAccountAmount = new HashTable();
600
- const htCreditAccountCurrency = new HashTable();
601
- const htCreditAccountPurpose = new HashTable();
602
-
603
- documentItems.forEach((invoiceItem) => {
604
- if (!htCreditAccountAmount.exists(invoiceItem.CtAccountNo)) {
605
- //add the credit account to the hash table
606
- htCreditAccountAmount.add(
607
- invoiceItem.CtAccountNo,
608
- invoiceItem.Amount,
609
- );
610
-
611
- htCreditAccountCurrency.add(
612
- invoiceItem.CtAccountNo,
613
- invoiceItem.Currency,
614
- );
615
-
616
- htCreditAccountPurpose.add(invoiceItem.CtAccountNo, invoiceItem.Name);
617
- } else {
618
- //update the credit account amount
619
- const d = htCreditAccountAmount.get(invoiceItem.CtAccountNo);
620
- htCreditAccountAmount.add(
621
- invoiceItem.CtAccountNo,
622
- d + invoiceItem.Amount,
623
- );
624
- }
625
- });
626
-
627
- const savedItems = htCreditAccountAmount.list();
628
-
629
- for (let i = 0; i < savedItems.length; i++) {
630
- const journalEntry = new JournalEntry(dbTransaction);
631
- //Temporary fix to successfully save journal entry in PostJournal
632
- journalEntry.init({
633
- CompanyId: this.CompanyId,
634
- Name: 'Issue Invoice ' + invoice.DocNo,
635
- });
636
- const account = await Account.initAccount(
637
- dbTransaction,
638
- savedItems[i].value[0],
639
- );
640
- const creditAmount = savedItems[i].value[1];
641
- const currency = htCreditAccountCurrency.get(savedItems[i].value[0]);
642
- const purpose = htCreditAccountPurpose.get(savedItems[i].value[0]);
643
-
644
- const dt = await journalEntry.newLedgerTransaction(
645
- TransactionTypeOptions.DEBIT,
646
- );
647
-
648
- if (dtAccountNo) {
649
- /*Transacting using AR account provided*/
650
- dt.AccountNo = dtAccountNo;
651
- } else {
652
- /*Transacting based on default customer AR account*/
653
- dt.AccountNo = (await customer.AccountReceivable).AccountNo;
654
- }
655
-
656
- dt.Currency = currency ? currency : 'MYR';
657
- dt.DebitAmount = creditAmount ? creditAmount : 0.0;
658
- dt.Date = transactionDate;
659
- dt.Description = `Transaction for ${purpose} - ${savedItems[i].value[0]}`;
660
- dt.Name = `Transaction for ${purpose}`;
661
- dt.RelatedDocNo = invoice.DocNo;
662
-
663
- const ct = await journalEntry.newLedgerTransaction(
664
- TransactionTypeOptions.CREDIT,
665
- );
666
- ct.AccountNo = savedItems[i].value[0];
667
- ct.Currency = currency ? currency : 'MYR';
668
- ct.CreditAmount = creditAmount ? creditAmount : 0.0;
669
- ct.Date = transactionDate;
670
- ct.Description = `Transaction for ${purpose}`;
671
- ct.Name = account.Name;
672
- ct.RelatedDocNo = invoice.DocNo;
673
-
674
- await this.postJournal(dbTransaction, journalEntry);
675
- }
676
-
677
- return invoice;
678
- } catch (err) {
679
- // tslint:disable-next-line:no-console
680
- console.log('Issue invoice err: ', err);
681
- throw err;
682
- }
683
- }
684
-
685
- /**
686
- * Issue a debit note
687
- *
688
- * @param dbTransaction - The database transaction to be used
689
- * @param loginUser - The user issuing the invoice
690
- * @param invoice - The document containing the invoice details
691
- * @param customer - The customer to be issued the invoice
692
- * @param dtAccountNo - The account number of the Account Receivable (AR) account to debit. If not provided, will debit the customer's default AR account
693
- *
694
- * @returns {Document} - Document object representing the full details of the invoice issued
695
- */
696
- async issueDebitNote(
697
- dbTransaction: any,
698
- loginUser: LoginUserBase,
699
- invoice: Document,
700
- customer: FinanceCustomerBase,
701
- dtAccountNo?: string,
702
- ): Promise<Document> {
703
- try {
704
- /*Check if the invoice number already exists (should be unique)*/
705
- const duplicateInvoice = await FinanceCompany._DocumentRepository.findOne(
706
- {
707
- where: {
708
- DocNo: invoice.DocNo,
709
- },
710
- transaction: dbTransaction,
711
- },
712
- );
713
-
714
- if (duplicateInvoice) {
715
- throw new Error('Invoice number already exists');
716
- }
717
-
718
- const documentItems = await invoice.DocumentItems;
719
-
720
- /*Check if the document has at least 1 document item*/
721
- if (!documentItems.length) {
722
- throw new Error('Document must have at least 1 document item');
723
- }
724
-
725
- /*Check if each document item has CtAccountNo provided*/
726
- for (const invoiceItem of documentItems) {
727
- if (!invoiceItem.CtAccountNo) {
728
- throw new Error(
729
- 'Each document item should have CtAccountNo provided',
730
- );
731
- }
732
- }
733
-
734
- /*Set up the document type*/
735
- invoice.DocType = DocType.DEBIT_NOTE;
736
- // invoice.DocNo = cuid();
737
-
738
- /*Saving the document and document items to the database*/
739
- await FinanceCompany._DocumentRepository.create(
740
- {
741
- DocNo: invoice.DocNo,
742
- DocType: invoice.DocType,
743
- DocDate: invoice.DocDate,
744
- CompanyId: invoice.CompanyId,
745
- Currency: invoice.Currency,
746
- Amount: invoice.Amount,
747
- Description: invoice.Description,
748
- Status: invoice.Status,
749
- IssuedById: loginUser.ObjectId,
750
- IssuedToId: customer.ObjectId,
751
- IssuedToType: invoice.IssuedToType,
752
- RelatedObjectId: invoice.RelatedObjectId,
753
- RelatedObjectType: invoice.RelatedObjectType,
754
- CreatedById: loginUser.ObjectId,
755
- CreatedAt: new Date(),
756
- UpdatedById: loginUser.ObjectId,
757
- UpdatedAt: new Date(),
758
- DocPDFFileMediaId: invoice.DocPDFFileMediaId,
759
- DocHTMLFileMediaId: invoice.DocHTMLFileMediaId,
760
- AccSystemRefId: invoice.AccSystemRefId,
761
- PostedToAccSystemYN: invoice.PostedToAccSystemYN,
762
- PostedById: invoice.PostedById,
763
- PostedDateTime: new Date(),
764
- UseAccSystemDocYN: invoice.UseAccSystemDocYN,
765
- },
766
- {
767
- transaction: dbTransaction,
768
- },
769
- );
770
-
771
- documentItems.forEach(async (documentItem) => {
772
- await FinanceCompany._DocumentItemRepository.create(
773
- {
774
- DocumentItemId: cuid(),
775
- DocNo: documentItem.DocNo,
776
- Name: documentItem.Name,
777
- NameBM: documentItem.NameBM,
778
- Description: documentItem.Description,
779
- ItemId: documentItem.ItemId,
780
- ItemType: documentItem.ItemType,
781
- ItemSKU: documentItem.ItemSKU,
782
- ItemSerialNo: documentItem.ItemSerialNo,
783
- Currency: documentItem.Currency,
784
- UnitPrice: documentItem.UnitPrice,
785
- Quantity: documentItem.Quantity,
786
- QuantityUOM: documentItem.QuantityUOM,
787
- Amount: documentItem.Amount,
788
- TaxCode: documentItem.TaxCode,
789
- TaxAmount: documentItem.TaxAmount,
790
- TaxRate: documentItem.TaxRate,
791
- TaxInclusiveYN: documentItem.TaxInclusiveYN,
792
- DtAccountNo: documentItem.DtAccountNo,
793
- CtAccountNo: documentItem.CtAccountNo,
794
- },
795
- {
796
- transaction: dbTransaction,
797
- },
798
- );
799
- });
800
-
801
- /*Generating the invoice*/
802
- if (invoice.UseAccSystemDocYN === 'Y') {
803
- /*todo: Posting to accounting system to generate invoice*/
804
- await this.AccountingSystem.createInvoice(invoice);
805
- } else {
806
- /*todo: check config file to see which invoice template is to be used for specific project*/
807
-
808
- /*Generating invoice based on template*/
809
- invoice.generateInvoice(loginUser.IDNo, customer);
810
- }
811
-
812
- const transactionDate = new Date();
813
- const htCreditAccountAmount = new HashTable();
814
- const htCreditAccountCurrency = 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
- } else {
829
- //update the credit account amount
830
- const d = htCreditAccountAmount.get(invoiceItem.CtAccountNo);
831
- htCreditAccountAmount.add(
832
- invoiceItem.CtAccountNo,
833
- d + invoiceItem.Amount,
834
- );
835
- }
836
- });
837
-
838
- const savedItems = htCreditAccountAmount.list();
839
-
840
- for (let i = 0; i < savedItems.length; i++) {
841
- const journalEntry = new JournalEntry(dbTransaction);
842
- //Temporary fix to successfully save journal entry in PostJournal
843
- journalEntry.init({
844
- CompanyId: this.CompanyId,
845
- Name: 'issue Invoice ' + invoice.DocNo,
846
- });
847
- const account = await Account.initAccount(
848
- dbTransaction,
849
- savedItems[i].value[0],
850
- );
851
- const creditAmount = savedItems[i].value[1];
852
- const currency = htCreditAccountCurrency.get(savedItems[i].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
- dt.AccountNo = (await customer.AccountReceivable).AccountNo;
864
- }
865
-
866
- dt.Currency = currency ? currency : 'MYR';
867
- dt.DebitAmount = creditAmount ? creditAmount : 0.0;
868
- dt.Date = transactionDate;
869
- dt.Description = account.Name;
870
- dt.Name = account.Name;
871
- dt.RelatedDocNo = invoice.DocNo;
872
-
873
- const ct = await journalEntry.newLedgerTransaction(
874
- TransactionTypeOptions.CREDIT,
875
- );
876
- ct.AccountNo = savedItems[i].key;
877
- ct.Currency = currency ? currency : 'MYR';
878
- ct.CreditAmount = creditAmount ? creditAmount : 0.0;
879
- ct.Date = transactionDate;
880
- ct.Description = account.Name;
881
- ct.Name = account.Name;
882
- ct.RelatedDocNo = invoice.DocNo;
883
-
884
- await this.postJournal(dbTransaction, journalEntry);
885
- }
886
-
887
- return invoice;
888
- } catch (err) {
889
- // tslint:disable-next-line:no-console
890
- console.log('Issue debit note err: ', err);
891
- throw err;
892
- }
893
- }
894
-
895
- /**
896
- * Issue a credit note
897
- *
898
- * @param dbTransaction - The database transaction to be used
899
- * @param loginUser - The user issuing the credit note
900
- * @param creditNote - The document containing the credit note details
901
- * @param customer - The customer to be issued the credit note
902
- * @param ctAccountNo - The account number of the Account Payable (AP) account to debit. If not provided, will debit the customer's default AR account
903
- *
904
- * @returns {Document} - Document object representing the full details of the invoice issued
905
- */
906
- async issueCreditNote(
907
- dbTransaction: any,
908
- loginUser: LoginUserBase,
909
- creditNote: Document,
910
- customer: FinanceCustomerBase,
911
- ctAccountNo?: string,
912
- ): Promise<Document> {
913
- try {
914
- /*Check if the invoice number already exists (should be unique)*/
915
- const duplicateCreditNote =
916
- await FinanceCompany._DocumentRepository.findOne({
917
- where: {
918
- DocNo: creditNote.DocNo,
919
- },
920
- transaction: dbTransaction,
921
- });
922
-
923
- if (duplicateCreditNote) {
924
- throw new Error('Invoice number already exists');
925
- }
926
-
927
- const documentItems = await creditNote.DocumentItems;
928
-
929
- /*Check if the document has at least 1 document item*/
930
- if (!documentItems.length) {
931
- throw new Error('Document must have at least 1 document item');
932
- }
933
-
934
- /*Check if each document item has CtAccountNo provided*/
935
- for (const invoiceItem of documentItems) {
936
- if (!invoiceItem.CtAccountNo) {
937
- throw new Error(
938
- 'Each document item should have CtAccountNo provided',
939
- );
940
- }
941
- }
942
-
943
- /*Set up the document type*/
944
- creditNote.DocType = DocType.DEBIT_NOTE;
945
-
946
- /*Saving the document and document items to the database*/
947
- await FinanceCompany._DocumentRepository.create(
948
- {
949
- DocNo: creditNote.DocNo,
950
- DocType: creditNote.DocType,
951
- DocDate: creditNote.DocDate,
952
- CompanyId: creditNote.CompanyId,
953
- Currency: creditNote.Currency,
954
- Amount: creditNote.Amount,
955
- Description: creditNote.Description,
956
- Status: creditNote.Status,
957
- IssuedById: loginUser.ObjectId,
958
- IssuedToId: customer.ObjectId,
959
- IssuedToType: creditNote.IssuedToType,
960
- RelatedObjectId: creditNote.RelatedObjectId,
961
- RelatedObjectType: creditNote.RelatedObjectType,
962
- CreatedById: loginUser.ObjectId,
963
- CreatedAt: new Date(),
964
- UpdatedById: loginUser.ObjectId,
965
- UpdatedAt: new Date(),
966
- DocPDFFileMediaId: creditNote.DocPDFFileMediaId,
967
- DocHTMLFileMediaId: creditNote.DocHTMLFileMediaId,
968
- AccSystemRefId: creditNote.AccSystemRefId,
969
- PostedToAccSystemYN: creditNote.PostedToAccSystemYN,
970
- PostedById: creditNote.PostedById,
971
- PostedDateTime: new Date(),
972
- UseAccSystemDocYN: creditNote.UseAccSystemDocYN,
973
- },
974
- {
975
- transaction: dbTransaction,
976
- },
977
- );
978
-
979
- documentItems.forEach(async (documentItem) => {
980
- await FinanceCompany._DocumentItemRepository.create(
981
- {
982
- DocumentItemId: documentItem.DocumentItemId,
983
- DocNo: documentItem.DocNo,
984
- Name: documentItem.Name,
985
- NameBM: documentItem.NameBM,
986
- Description: documentItem.Description,
987
- ItemId: documentItem.ItemId,
988
- ItemType: documentItem.ItemType,
989
- ItemSKU: documentItem.ItemSKU,
990
- ItemSerialNo: documentItem.ItemSerialNo,
991
- Currency: documentItem.Currency,
992
- UnitPrice: documentItem.UnitPrice,
993
- Quantity: documentItem.Quantity,
994
- QuantityUOM: documentItem.QuantityUOM,
995
- Amount: documentItem.Amount,
996
- TaxCode: documentItem.TaxCode,
997
- TaxAmount: documentItem.TaxAmount,
998
- TaxRate: documentItem.TaxRate,
999
- TaxInclusiveYN: documentItem.TaxInclusiveYN,
1000
- DtAccountNo: documentItem.DtAccountNo,
1001
- CtAccountNo: documentItem.CtAccountNo,
1002
- },
1003
- {
1004
- transaction: dbTransaction,
1005
- },
1006
- );
1007
- });
1008
-
1009
- /*Generating the credit note*/
1010
- if (creditNote.UseAccSystemDocYN === 'Y') {
1011
- /*todo: Posting to accounting system to generate creditNote*/
1012
- await this.AccountingSystem.createCreditNote(creditNote);
1013
- } else {
1014
- /*todo: check config file to see which invoice template is to be used for specific project*/
1015
-
1016
- /*Generating credit note based on template*/
1017
- creditNote.generateCreditNote(loginUser.IDNo, customer);
1018
- }
1019
-
1020
- const journalEntry = new JournalEntry(dbTransaction);
1021
- //Temporary fix to successfully save journal entry in PostJournal
1022
- journalEntry.init({
1023
- CompanyId: this.CompanyId,
1024
- Name: 'Issue Debit Note ' + creditNote.DocNo,
1025
- });
1026
-
1027
- const transactionDate = new Date();
1028
-
1029
- const creditTransaction = await journalEntry.newLedgerTransaction(
1030
- TransactionTypeOptions.CREDIT,
1031
- );
1032
-
1033
- if (ctAccountNo) {
1034
- /*Transacting using AR account provided*/
1035
- creditTransaction.AccountNo = ctAccountNo;
1036
- } else {
1037
- /*Transacting based on default customer AR account*/
1038
- creditTransaction.AccountNo = customer.CustSystemCode + '-AP';
1039
- }
1040
-
1041
- creditTransaction.Currency = creditNote.Currency;
1042
- creditTransaction.CreditAmount = creditNote.Amount;
1043
- creditTransaction.Date = transactionDate;
1044
- creditTransaction.Description = creditNote.DocNo;
1045
- creditTransaction.Name = creditNote.DocNo;
1046
-
1047
- for (const invoiceItem of documentItems) {
1048
- const itemLedger = await journalEntry.newLedgerTransaction(
1049
- TransactionTypeOptions.DEBIT,
1050
- );
1051
- itemLedger.AccountNo = invoiceItem.CtAccountNo;
1052
- itemLedger.Currency = invoiceItem.Currency;
1053
- itemLedger.DebitAmount = invoiceItem.Amount;
1054
- itemLedger.Date = transactionDate;
1055
- itemLedger.Description = invoiceItem.Description;
1056
- itemLedger.Name = invoiceItem.Name;
1057
- }
1058
-
1059
- this.postJournal(dbTransaction, journalEntry);
1060
-
1061
- const payload = {
1062
- Action: 'Create',
1063
- Activity: 'Issuing a Credit Note Transaction',
1064
- Description: `Credit Transaction (ID: ${creditTransaction.TransactionId}) has been created`,
1065
- EntityType: 'CreditTransaction',
1066
- EntityValueBefore: JSON.stringify({}),
1067
- EntityValueAfter: JSON.stringify(creditTransaction),
1068
- PerformedById: 'test',
1069
- PerformedAt: transactionDate,
1070
- EntityId: creditTransaction.TransactionId,
1071
- };
1072
-
1073
- await axios.post(
1074
- `${process.env.COMMON_API_URL}/activity-histories`,
1075
- payload,
1076
- );
1077
-
1078
- return creditNote;
1079
- } catch (err) {
1080
- // tslint:disable-next-line:no-console
1081
- console.log('Issue credit note err: ', err);
1082
- throw err;
1083
- }
1084
- }
1085
-
1086
- /**
1087
- * Register a payment collected
1088
- *
1089
- * @param dbTransaction - The database transaction to be used
1090
- * @param loginUser - The user collecting the payment
1091
- * @param payment - The payment object containing payment details
1092
- * @param customer - The customer making the payment
1093
- * @param ctAccountNo - The account number of the customer's Account Receivable account to transact, else the default customer account payable receivable will be used
1094
- *
1095
- * @returns {Payment} - Payment object representing the full detals of the payment collection recorded
1096
- */
1097
- async collectPayment(
1098
- dbTransaction: any,
1099
- loginUser: LoginUserBase,
1100
- payment: Payment,
1101
- customer: FinanceCustomerBase,
1102
- ctAccountNo?: string,
1103
- ): Promise<Payment> {
1104
- try {
1105
- /*validation 1: Make sure that the payment has at least 1 payment item*/
1106
- const paymentItems = await payment.PaymentItems;
1107
- if (paymentItems.length < 1) {
1108
- throw new Error(
1109
- 'Atleast one payment item is required to identify what payment is being paid for.',
1110
- );
1111
- }
1112
-
1113
- /*validation 2: Make sure that the payment has at least 1 payment method*/
1114
- const paymentPaidWithItems = await payment.PaymentPaidWith;
1115
- if (paymentPaidWithItems.length < 1) {
1116
- throw new Error(
1117
- 'Atleast one payment paid with item is required to identify how the payment was made.',
1118
- );
1119
- }
1120
-
1121
- payment.PaymentId = cuid();
1122
- payment.PaymentType = PaymentType.PAYMENT_RECEIVED;
1123
- payment.ReceivedBy = loginUser.ObjectId;
1124
-
1125
- await FinanceCompany._PaymentRepository.create(
1126
- {
1127
- PaymentId: payment.PaymentId,
1128
- PaymentType: payment.PaymentType,
1129
- PaymentDate: payment.PaymentDate,
1130
- Description: payment.Description,
1131
- Currency: payment.Currency,
1132
- Amount: payment.Amount,
1133
- Status: PaymentStatus.SUCCESSFUL,
1134
- PostedToAccSystemYN: 'N',
1135
- ReceivedBy: payment.ReceivedBy,
1136
- UpdatedAt: new Date(),
1137
- UpdatedBy: loginUser.ObjectId,
1138
- CreatedAt: new Date(),
1139
- CreatedBy: loginUser.ObjectId,
1140
- },
1141
- { transaction: dbTransaction },
1142
- );
1143
-
1144
- for (const paymentItem of paymentItems) {
1145
- await FinanceCompany._PaymentItemRepository.create(
1146
- {
1147
- PaymentId: payment.PaymentId,
1148
- PayForObjectId: paymentItem.PayForObjectId,
1149
- PayForObjectType: paymentItem.PayForObjectType,
1150
- Currency: paymentItem.Currency,
1151
- Amount: paymentItem.Amount,
1152
- Name: paymentItem.Name,
1153
- Description: paymentItem.Description,
1154
- },
1155
- { transaction: dbTransaction },
1156
- );
1157
- }
1158
-
1159
- for (const paymentPaidWithItem of paymentPaidWithItems) {
1160
- await FinanceCompany._PaymentPaidWithRepository.create(
1161
- {
1162
- PaymentId: payment.PaymentId,
1163
- MethodTypeId: paymentPaidWithItem.MethodTypeId,
1164
- Currency: paymentPaidWithItem.Currency,
1165
- Amount: paymentPaidWithItem.Amount,
1166
- Status: paymentPaidWithItem.Status,
1167
- TransactionId: paymentPaidWithItem.TransactionId,
1168
- RefBank: paymentPaidWithItem.RefBank,
1169
- RefName: paymentPaidWithItem.RefName,
1170
- RefNo: paymentPaidWithItem.RefNo,
1171
- RefOther1: paymentPaidWithItem.RefOther1,
1172
- RefOther2: paymentPaidWithItem.RefOther2,
1173
- RefOther3: paymentPaidWithItem.RefOther3,
1174
- RefOther4: paymentPaidWithItem.RefOther4,
1175
- RefOther5: paymentPaidWithItem.RefOther5,
1176
- Remarks: paymentPaidWithItem.Remarks,
1177
- PaymentMediaId: paymentPaidWithItem.PaymentMediaId,
1178
- },
1179
- { transaction: dbTransaction },
1180
- );
1181
- }
1182
-
1183
- /*Registering the Journal Entries for the transaction*/
1184
- const transactionDate = new Date();
1185
-
1186
- for (const paymentPaidWith of paymentPaidWithItems) {
1187
- let paymentMethodType: PaymentMethodType;
1188
-
1189
- try {
1190
- paymentMethodType = await PaymentMethodType.initMethodType(
1191
- dbTransaction,
1192
- paymentPaidWith.MethodTypeId,
1193
- );
1194
- } catch (error) {
1195
- if (error instanceof RecordNotFoundError) {
1196
- throw new Error('Invalid Payment Method Type Id');
1197
- } else {
1198
- throw error;
1199
- }
1200
- }
1201
- const journalEntry = new JournalEntry(dbTransaction);
1202
-
1203
- journalEntry.init({
1204
- CompanyId: this.CompanyId,
1205
- Name: 'Collect-payments for ' + payment.PaymentId,
1206
- });
1207
-
1208
- const debitLT = await journalEntry.newLedgerTransaction(
1209
- TransactionTypeOptions.DEBIT,
1210
- );
1211
- debitLT.AccountNo = paymentMethodType.AccountNo;
1212
- debitLT.Currency = paymentPaidWith.Currency;
1213
- debitLT.DebitAmount = paymentPaidWith.Amount;
1214
- debitLT.Date = transactionDate;
1215
- debitLT.Description = customer.FullName;
1216
- debitLT.Name = customer.FullName;
1217
-
1218
- const creditLT = await journalEntry.newLedgerTransaction(
1219
- TransactionTypeOptions.CREDIT,
1220
- );
1221
-
1222
- if (ctAccountNo) {
1223
- creditLT.AccountNo = ctAccountNo;
1224
- } else {
1225
- creditLT.AccountNo = (await customer.AccountReceivable).AccountNo;
1226
- }
1227
- creditLT.Currency = paymentPaidWith.Currency;
1228
- creditLT.CreditAmount = paymentPaidWith.Amount;
1229
- creditLT.Date = transactionDate;
1230
- creditLT.Description = paymentMethodType.Name;
1231
- creditLT.Name = paymentMethodType.Name;
1232
-
1233
- await this.postJournal(dbTransaction, journalEntry);
1234
- }
1235
-
1236
- /*todo: saving a record into the activity history table*/
1237
-
1238
- return payment;
1239
- } catch (error) {
1240
- throw error;
1241
- }
1242
- }
1243
-
1244
- /**
1245
- * Method to make payment to a customer.
1246
- *
1247
- * @param dbTransaction - The database transaction to be used
1248
- * @param loginUser - The user logging in and the user who collected the payment.
1249
- * @param payment - The payment object containing payment details
1250
- * @param customer - The customer who is receiving the payment.
1251
- * @param ctAccountNo - The account number of the customer's Account Payable account to transact, else the default customer account payable will be used.
1252
- *
1253
- * @returns {Payment} - Payment object representing the full details of the payment collection recorded
1254
- */
1255
- async makePayment(
1256
- dbTransaction: any,
1257
- loginUser: LoginUserBase,
1258
- payment: Payment,
1259
- customer: FinanceCustomerBase,
1260
- dtAccountNo?: string,
1261
- ): Promise<Payment> {
1262
- let paymentMethodType: PaymentMethodType;
1263
- try {
1264
- const paymentPaidWith = await payment.PaymentPaidWith;
1265
- if (paymentPaidWith.length < 1) {
1266
- throw new Error(
1267
- 'Atleast one payment paid with item is required to identify how the payment was made.',
1268
- );
1269
- }
1270
- paymentMethodType = await PaymentMethodType.initMethodType(
1271
- dbTransaction,
1272
- paymentPaidWith[0].MethodTypeId,
1273
- );
1274
-
1275
- const paymentItems = await payment.PaymentItems;
1276
- if (paymentItems.length < 1) {
1277
- throw new Error(
1278
- 'Atleast one payment item is required to identify what payment is being paid for',
1279
- );
1280
- }
1281
-
1282
- payment.PaymentId = cuid();
1283
- payment.PaymentType = PaymentType.PAYOUT;
1284
- payment.ReceivedBy = loginUser.ObjectId;
1285
-
1286
- await FinanceCompany._PaymentRepository.create(
1287
- {
1288
- PaymentId: payment.PaymentId,
1289
- PaymentType: payment.PaymentType,
1290
- PaymentDate: payment.PaymentDate,
1291
- Description: payment.Description,
1292
- Currency: payment.Currency,
1293
- ReceivedBy: payment.ReceivedBy,
1294
- Amount: payment.Amount,
1295
- Status: PaymentStatus.SUCCESSFUL,
1296
- PostedToAccSystemYN: 'N',
1297
- UpdatedAt: new Date(),
1298
- UpdatedBy: loginUser.ObjectId,
1299
- CreatedAt: new Date(),
1300
- CreatedBy: loginUser.ObjectId,
1301
- },
1302
- { transaction: dbTransaction },
1303
- );
1304
-
1305
- paymentItems.forEach(async (paymentItem) => {
1306
- await FinanceCompany._PaymentItemRepository.create(
1307
- {
1308
- PaymentId: payment.PaymentId,
1309
- PayForObjectId: paymentItem.PayForObjectId,
1310
- PayForObjectType: paymentItem.PayForObjectType,
1311
- Currency: paymentItem.Currency,
1312
- Amount: paymentItem.Amount,
1313
- Name: paymentItem.Name,
1314
- Description: paymentItem.Description,
1315
- },
1316
- { transaction: dbTransaction },
1317
- );
1318
- });
1319
-
1320
- for (const paymentPaidWithItem of paymentPaidWith) {
1321
- await FinanceCompany._PaymentPaidWithRepository.create(
1322
- {
1323
- PaymentId: payment.PaymentId,
1324
- MethodTypeId: paymentPaidWithItem.MethodTypeId,
1325
- Currency: paymentPaidWithItem.Currency,
1326
- Amount: paymentPaidWithItem.Amount,
1327
- Status: paymentPaidWithItem.Status,
1328
- TransactionId: paymentPaidWithItem.TransactionId,
1329
- RefBank: paymentPaidWithItem.RefBank,
1330
- RefName: paymentPaidWithItem.RefName,
1331
- RefNo: paymentPaidWithItem.RefNo,
1332
- RefOther1: paymentPaidWithItem.RefOther1,
1333
- RefOther2: paymentPaidWithItem.RefOther2,
1334
- RefOther3: paymentPaidWithItem.RefOther3,
1335
- RefOther4: paymentPaidWithItem.RefOther4,
1336
- RefOther5: paymentPaidWithItem.RefOther5,
1337
- Remarks: paymentPaidWithItem.Remarks,
1338
- PaymentMediaId: paymentPaidWithItem.PaymentMediaId,
1339
- },
1340
- { transaction: dbTransaction },
1341
- );
1342
- }
1343
- const transactionDate = new Date();
1344
- for (const paymentPaidWithItem of paymentPaidWith) {
1345
- try {
1346
- paymentMethodType = await PaymentMethodType.initMethodType(
1347
- dbTransaction,
1348
- paymentPaidWithItem.MethodTypeId,
1349
- );
1350
-
1351
- const journalEntry = new JournalEntry(dbTransaction);
1352
- //Temporary fix to successfully save journal entry in PostJournal
1353
- journalEntry.init({
1354
- CompanyId: this.CompanyId,
1355
- Name: 'Make Payment for ' + payment.PaymentId,
1356
- });
1357
-
1358
- const creditLT = await journalEntry.newLedgerTransaction(
1359
- TransactionTypeOptions.CREDIT,
1360
- );
1361
- creditLT.AccountNo = paymentMethodType.AccountNo;
1362
- creditLT.Currency = paymentPaidWithItem.Currency;
1363
- creditLT.CreditAmount = paymentPaidWithItem.Amount;
1364
- creditLT.Date = transactionDate;
1365
- creditLT.Description = customer.FullName;
1366
- creditLT.Name = customer.FullName;
1367
-
1368
- const debitLT = await journalEntry.newLedgerTransaction(
1369
- TransactionTypeOptions.DEBIT,
1370
- );
1371
- if (dtAccountNo) {
1372
- debitLT.AccountNo = dtAccountNo;
1373
- } else {
1374
- debitLT.AccountNo = (await customer.AccountPayable).AccountNo;
1375
- }
1376
- debitLT.Currency = paymentPaidWithItem.Currency;
1377
- debitLT.DebitAmount = paymentPaidWithItem.Amount;
1378
- debitLT.Date = transactionDate;
1379
- debitLT.Description = paymentMethodType.Name;
1380
- debitLT.Name = paymentMethodType.Name;
1381
-
1382
- await this.postJournal(dbTransaction, journalEntry);
1383
- } catch (error) {
1384
- if (error instanceof RecordNotFoundError) {
1385
- throw new Error('Invalid Payment Method Type Id');
1386
- } else {
1387
- throw error;
1388
- }
1389
- }
1390
- }
1391
- /*todo: saving a record into the activity history table*/
1392
-
1393
- return payment;
1394
- } catch (error) {
1395
- if (error instanceof RecordNotFoundError) {
1396
- throw new Error('Invalid PaymentMethodType id');
1397
- } else {
1398
- throw error;
1399
- }
1400
- }
1401
- }
1402
-
1403
- get PaymentMethods(): Promise<PaymentMethod[]> {
1404
- return new Promise((resolve, reject) => {
1405
- if (this.CompanyId !== 'New') {
1406
- FinanceCompany._PaymentMethodRepository
1407
- .findAll({
1408
- where: {
1409
- CompanyId: this.CompanyId,
1410
- },
1411
- transaction: this._DbTransaction,
1412
- })
1413
- .then((paymentMethod) => {
1414
- const paymentMethodObjects = paymentMethod.map(
1415
- (paymentMethodData) => {
1416
- return new Promise((resolve, reject) => {
1417
- FinanceCompany._PaymentMethodTypeRepository
1418
- .findAll({
1419
- where: {
1420
- MethodId: paymentMethodData.MethodId,
1421
- },
1422
- transaction: this._DbTransaction,
1423
- raw: true,
1424
- })
1425
- .then((paymentMethodTypes) => {
1426
- const paymentMethodObjects = {
1427
- ...paymentMethodData.get({ plain: true }),
1428
- Types: paymentMethodTypes,
1429
- };
1430
- resolve(paymentMethodObjects);
1431
- })
1432
- .catch((err) => {
1433
- reject(err);
1434
- });
1435
- }).then((paymentMethods) => paymentMethods);
1436
- },
1437
- );
1438
- return Promise.all(paymentMethodObjects);
1439
- })
1440
- .then((paymentMethodObjects) => {
1441
- this._PaymentMethods = paymentMethodObjects;
1442
- resolve(this._PaymentMethods);
1443
- })
1444
- .catch((err) => {
1445
- reject(err);
1446
- });
1447
- } else {
1448
- resolve(this._PaymentMethods);
1449
- }
1450
- });
1451
- }
1452
-
1453
- /**
1454
- * Method to load / reload the payment methods based on the configuration file
1455
- */
1456
- async LoadPaymentMethods(): Promise<void> {
1457
- /*Retrieve the payment method from the config file */
1458
- const { companyId, paymentMethods } = config.financeCompanies['TXG-FS'];
1459
- const paymentMethod = new PaymentMethod();
1460
- for (const method in paymentMethods) {
1461
- const paymentMethodData =
1462
- await FinanceCompany._PaymentMethodRepository.findOne({
1463
- where: {
1464
- MethodId: paymentMethods[method].id,
1465
- Name: paymentMethods[method].name,
1466
- },
1467
- });
1468
-
1469
- if (!paymentMethodData) {
1470
- const newPaymentMethod =
1471
- await FinanceCompany._PaymentMethodRepository.create({
1472
- MethodId: paymentMethods[method].id,
1473
- Name: paymentMethods[method].name,
1474
- CompanyId: companyId,
1475
- });
1476
-
1477
- this._PaymentMethods.push(newPaymentMethod);
1478
- }
1479
- this._PaymentMethods.push(paymentMethodData);
1480
- }
1481
-
1482
- this._PaymentMethods.forEach(async (item) => {
1483
- const p = item?.get({ plain: true });
1484
-
1485
- for (const method in paymentMethods) {
1486
- if (!p) {
1487
- continue;
1488
- }
1489
-
1490
- if (p.MethodId === paymentMethods[method]?.id) {
1491
- const paymentMethodTypeData =
1492
- await FinanceCompany._PaymentMethodTypeRepository.findOne({
1493
- where: {
1494
- MethodId: p.MethodId,
1495
- },
1496
- });
1497
-
1498
- if (!paymentMethodTypeData) {
1499
- const configPaymentMethodTypes = paymentMethods[method]?.types;
1500
-
1501
- for (const methodType in configPaymentMethodTypes) {
1502
- // TODO: Create a seeder for payment method account
1503
- /*validate whether account data already exists*/
1504
- const accountData =
1505
- await FinanceCompany._AccountRepository.findOne({
1506
- where: {
1507
- AccountNo: configPaymentMethodTypes[methodType].accountNo,
1508
- },
1509
- });
1510
-
1511
- /*generating account data if not exist */
1512
- if (!accountData) {
1513
- const accountPayload = {
1514
- CompanyId: companyId,
1515
- Name: configPaymentMethodTypes[methodType].name,
1516
- AccountType: 'PaymentMethod',
1517
- CreatedAt: new Date(),
1518
- CreatedById: 'System',
1519
- AccSystemRefId: 'REF',
1520
- PostedToAccSystemYN: 'N',
1521
- };
1522
-
1523
- try {
1524
- await FinanceCompany._AccountRepository.create({
1525
- AccountNo: configPaymentMethodTypes[methodType].accountNo,
1526
- ...accountPayload,
1527
- });
1528
-
1529
- await FinanceCompany._AccountRepository.create({
1530
- AccountNo:
1531
- configPaymentMethodTypes[methodType]
1532
- .processingFeeAccountNo,
1533
- ...accountPayload,
1534
- });
1535
- } catch (err) {}
1536
- }
1537
-
1538
- const paymentMethodTypePayload = {
1539
- MethodId: p.MethodId,
1540
- MethodTypeId: configPaymentMethodTypes[methodType].id,
1541
- Name: configPaymentMethodTypes[methodType].name,
1542
- AccountNo: configPaymentMethodTypes[methodType].accountNo,
1543
- ProcessingFeeRate:
1544
- configPaymentMethodTypes[methodType].processingFeeRate,
1545
- ProcessingFeeAccountNo:
1546
- configPaymentMethodTypes[methodType].processingFeeAccountNo,
1547
- };
1548
-
1549
- try {
1550
- await paymentMethod.newPaymentMethodType(
1551
- paymentMethodTypePayload,
1552
- );
1553
- } catch (err) {}
1554
- }
1555
- }
1556
- }
1557
- }
1558
- });
1559
- }
1560
- }
1
+ import axios from 'axios';
2
+ import * as cuid from 'cuid';
3
+ import {
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
+ PaymentStatus,
20
+ PaymentType,
21
+ TransactionTypeOptions,
22
+ } from '../enum';
23
+ import PaymentMethodType from '../payment-method-type/payment-method-type';
24
+ import { PaymentRepository } from '../payment/payment.repository';
25
+ import { PaymentItemRepository } from '../payment-item/payment-item.repository';
26
+ import Payment from '../payment/payment';
27
+ import { DocumentRepository } from '../document/document.repository';
28
+ import { DocumentItemRepository } from '../document/document-item.repository';
29
+ import { PaymentMethodRepository } from '../payment-method/payment-method.repository';
30
+ import { PaymentMethodTypeRepository } from '../payment-method-type/payment-method-type.repository';
31
+ import PaymentMethod from '../payment-method/payment-method';
32
+ import { AccountRepository } from '../account/account.repository';
33
+ import { PaymentPaidWithRepository } from '../payment-paid-with/payment-paid-with.repository';
34
+
35
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
36
+ const getConfig = require('../config');
37
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
38
+ const config = getConfig();
39
+
40
+ export default class FinanceCompany extends ObjectBase {
41
+ private _CompanyId = 'New';
42
+ private _CompSystemCode = '';
43
+ private _CompSystemRefId = '';
44
+ private _AccSystemCode = '';
45
+ private static _htFinanceCompanyIds = new HashTable();
46
+ private static _htFinanceCompanies = new HashTable();
47
+ private static _financeCompanyRepository = new FinanceCompanyRepository();
48
+ private static _PaymentRepository = new PaymentRepository();
49
+ private static _PaymentItemRepository = new PaymentItemRepository();
50
+ private static _PaymentPaidWithRepository = new PaymentPaidWithRepository();
51
+ private static _PaymentMethodRepository = new PaymentMethodRepository();
52
+ private static _PaymentMethodTypeRepository =
53
+ new PaymentMethodTypeRepository();
54
+ private static _DocumentRepository = new DocumentRepository();
55
+ private static _DocumentItemRepository = new DocumentItemRepository();
56
+ private static _FinanceCustomerRepository = new FinanceCustomerRepository();
57
+ private static _LedgerTransactionRepository =
58
+ new LedgerTransactionRepository();
59
+
60
+ private static _AccountRepository = new AccountRepository();
61
+
62
+ private _AccountingSystem: IAccountSystem;
63
+
64
+ private _DbTransaction: any;
65
+ private _PaymentMethods = [];
66
+
67
+ get CompSystemCode(): string {
68
+ return this._CompSystemCode;
69
+ }
70
+
71
+ private set CompSystemCode(code: string) {
72
+ this._CompSystemCode = code;
73
+ }
74
+
75
+ get CompSystemRefId() {
76
+ return this._CompSystemRefId;
77
+ }
78
+
79
+ set CompSystemRefId(id: string) {
80
+ this._CompSystemRefId = id;
81
+ }
82
+
83
+ get AccSystemCode() {
84
+ return this._AccSystemCode;
85
+ }
86
+
87
+ private set AccSystemCode(code: string) {
88
+ this._AccSystemCode = code;
89
+ }
90
+
91
+ get CompanyId() {
92
+ return this._CompanyId;
93
+ }
94
+
95
+ get ObjectId() {
96
+ return this._CompanyId;
97
+ }
98
+
99
+ private set ObjectId(id: string) {
100
+ this._CompanyId = id;
101
+ }
102
+
103
+ get ObjectName() {
104
+ return `${this.CompSystemCode}-${this.CompSystemRefId}-${this.AccSystemCode}`;
105
+ }
106
+
107
+ get TableName() {
108
+ return 'finance_Company';
109
+ }
110
+
111
+ get AccountingSystem(): IAccountSystem {
112
+ return this._AccountingSystem;
113
+ }
114
+
115
+ set AccountingSystem(system: IAccountSystem) {
116
+ this._AccountingSystem = system;
117
+ }
118
+
119
+ constructor(
120
+ compSystemCode: string,
121
+ compSystemRefId: string,
122
+ accSystemCode: string,
123
+ ) {
124
+ super();
125
+ this.CompSystemCode = compSystemCode;
126
+ this.CompSystemRefId = compSystemRefId;
127
+ this.AccSystemCode = accSystemCode;
128
+ }
129
+
130
+ static async getFinanceCompanyId(
131
+ compSystemCode: string,
132
+ compSystemRefId: string,
133
+ accSystemCode: string,
134
+ ): Promise<string> {
135
+ let sCompanyId = '';
136
+ /*Assemble the hashtable key*/
137
+ const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
138
+ /*Check if the FinanceCompany has previously being loaded*/
139
+ if (!FinanceCompany._htFinanceCompanyIds.get(sKey)) {
140
+ /*Instantiate a new FinanceCompany*/
141
+ const financeCompany = new FinanceCompany(
142
+ compSystemCode,
143
+ compSystemRefId,
144
+ accSystemCode,
145
+ );
146
+
147
+ /*Retrieve the finance company from finance_Company table using compSystemCode,
148
+ * CompSystemRefId and accSystemCode */
149
+ const company = await FinanceCompany._financeCompanyRepository.findOne({
150
+ where: {
151
+ CompSystemCode: compSystemCode,
152
+ CompSystemRefId: compSystemRefId,
153
+ AccSystemCode: accSystemCode,
154
+ },
155
+ });
156
+
157
+ /*Retrieve and store the companyId from the result*/
158
+ financeCompany.ObjectId = company.CompanyId;
159
+ sCompanyId = financeCompany.ObjectId;
160
+
161
+ /*Add the details into the hashtable*/
162
+ FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
163
+ FinanceCompany._htFinanceCompanies.add(sCompanyId, financeCompany);
164
+ }
165
+
166
+ if (typeof FinanceCompany._htFinanceCompanyIds.get(sKey) === 'string') {
167
+ sCompanyId = FinanceCompany._htFinanceCompanyIds.get(sKey);
168
+ }
169
+
170
+ return sCompanyId;
171
+ }
172
+
173
+ static async getFinanceCompany(companyId: string): Promise<FinanceCompany> {
174
+ /*Check if the finance company is previously be loaded*/
175
+ if (!FinanceCompany._htFinanceCompanies.get(companyId)) {
176
+ /*Retrieve the finance company from finance_Company table using compSystemCode,
177
+ * CompSystemRefId and accSystemCode */
178
+ const company = await FinanceCompany._financeCompanyRepository.findOne({
179
+ where: { CompanyId: companyId },
180
+ });
181
+
182
+ if (!company) {
183
+ throw Error('No finance company found. Please create first.');
184
+ }
185
+
186
+ /*Using the result returned, instantiate a new FinanceCompany*/
187
+ const compSystemCode = company.CompSystemCode;
188
+ const compSystemRefId = company.CompSystemRefId;
189
+ const accSystemCode = company.AccSystemCode;
190
+
191
+ const financeCompany = new FinanceCompany(
192
+ compSystemCode,
193
+ compSystemRefId,
194
+ accSystemCode,
195
+ );
196
+
197
+ /*Retrieve and store the CompanyId from the result*/
198
+ financeCompany.ObjectId = company.CompanyId;
199
+ financeCompany._CompanyId = company.CompanyId;
200
+
201
+ /*Add the details into the hashtable*/
202
+ const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
203
+ FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
204
+ FinanceCompany._htFinanceCompanies.add(
205
+ financeCompany.ObjectId,
206
+ financeCompany,
207
+ );
208
+ }
209
+ // tslint:disable-next-line:no-console
210
+ console.log('return from hash table');
211
+ return FinanceCompany._htFinanceCompanies.get(companyId);
212
+ }
213
+
214
+ static async createFinanceCompany(
215
+ dbTransaction: any,
216
+ loginUser: LoginUserBase,
217
+ companyId: string,
218
+ compSystemCode: string,
219
+ compSystemRefId: string,
220
+ accSystemCode: string,
221
+ ) {
222
+ /*Instantiate a new FinanceCompany*/
223
+ const financeCompany = new FinanceCompany(
224
+ compSystemCode,
225
+ compSystemRefId,
226
+ accSystemCode,
227
+ );
228
+
229
+ /*Validating if the finance company already exists in finance_Company*/
230
+ const company = await FinanceCompany._financeCompanyRepository.findOne({
231
+ where: {
232
+ CompSystemCode: compSystemCode,
233
+ CompSystemRefId: compSystemRefId,
234
+ AccSystemCode: accSystemCode,
235
+ },
236
+ });
237
+
238
+ if (company) {
239
+ throw Error(
240
+ 'There is already another Finance Company with the compSystemCode, CompSystemRefId and accSystemCode specified.',
241
+ );
242
+ }
243
+
244
+ /*Generating the companyId*/
245
+ financeCompany.ObjectId = companyId;
246
+
247
+ /*Save the FinanceCompany to the finance_Company table*/
248
+ await FinanceCompany._financeCompanyRepository.create(
249
+ {
250
+ CompanyId: financeCompany.CompanyId,
251
+ CompSystemCode: financeCompany.CompSystemCode,
252
+ CompSystemRefId: financeCompany.CompSystemRefId,
253
+ AccSystemCode: financeCompany.AccSystemCode,
254
+ },
255
+ {
256
+ transaction: dbTransaction,
257
+ },
258
+ );
259
+
260
+ /*Add the details to hashtable*/
261
+ const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
262
+ FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
263
+ FinanceCompany._htFinanceCompanies.add(
264
+ financeCompany.ObjectId,
265
+ financeCompany,
266
+ );
267
+
268
+ // tslint:disable-next-line:no-console
269
+ console.log('return from hash table');
270
+ return FinanceCompany._htFinanceCompanies.get(companyId);
271
+ }
272
+
273
+ async createCustomer(
274
+ dbTransaction: any,
275
+ custSystemCode: string,
276
+ custSystemRefId: string,
277
+ customer: FinanceCustomerBase,
278
+ ) {
279
+ try {
280
+ if (!custSystemCode || !custSystemRefId) {
281
+ throw new Error(
282
+ 'CustSystemCode and CustomerRefId are required fields.',
283
+ );
284
+ }
285
+
286
+ const financeCustomerData =
287
+ await FinanceCompany._FinanceCustomerRepository.findOne({
288
+ where: {
289
+ CompanyId: this._CompanyId,
290
+ CustSystemCode: custSystemCode,
291
+ CustSystemRefId: custSystemRefId,
292
+ },
293
+ });
294
+
295
+ if (financeCustomerData) {
296
+ throw new Error('Customer already created previously.');
297
+ }
298
+
299
+ // Retrieve the type of accounting system, API Key, and API secret based on SysCode from the Config.ts
300
+ const customerId = await this.AccountingSystem.createCustomer({
301
+ customer,
302
+ });
303
+
304
+ customer.CompanyId = this._CompanyId;
305
+
306
+ const newCustomer = await customer.save(
307
+ customerId,
308
+ custSystemCode,
309
+ custSystemRefId,
310
+ dbTransaction,
311
+ );
312
+
313
+ const payload = {
314
+ Action: 'Create',
315
+ Activity: 'Finance Customer Created',
316
+ Description: `Customer (ID: ${newCustomer.CustomerId}) has been created for ${this.AccSystemCode}`,
317
+ EntityType: 'finance_Customer',
318
+ EntityValueBefore: JSON.stringify({}),
319
+ EntityValueAfter: JSON.stringify(newCustomer),
320
+ PerformedById: 'test',
321
+ PerformedAt: new Date(),
322
+ EntityId: newCustomer.CustomerId,
323
+ };
324
+
325
+ await axios.post(
326
+ `${process.env.COMMON_API_URL}/activity-histories`,
327
+ payload,
328
+ );
329
+
330
+ return customer;
331
+ } catch (error) {
332
+ throw error;
333
+ }
334
+ }
335
+
336
+ async postJournal(dbTransaction: any, journalEntry: JournalEntry) {
337
+ const debitTransactions = await journalEntry.DebitTransactions;
338
+ const creditTransactions = await journalEntry.CreditTransactions;
339
+ try {
340
+ if (creditTransactions.length < 1 || debitTransactions?.length < 1) {
341
+ throw new Error(
342
+ 'There should be at least 1 debit ledger transaction and 1 credit ledger transaction in the journal entry',
343
+ );
344
+ }
345
+
346
+ const totalCreditAmount = creditTransactions.reduce(
347
+ (accumulator, currentValue) => accumulator + currentValue.CreditAmount,
348
+ 0,
349
+ );
350
+ const totalDebitAmount = debitTransactions.reduce(
351
+ (accumulator, currentValue) => accumulator + currentValue.DebitAmount,
352
+ 0,
353
+ );
354
+
355
+ if (totalCreditAmount !== totalDebitAmount) {
356
+ throw new Error(
357
+ 'Credit ledger transaction and debit ledger transaction should the same amount',
358
+ );
359
+ }
360
+
361
+ const newJournalEntry = await journalEntry.save('test', dbTransaction);
362
+
363
+ for (const ledgerTransaction of debitTransactions) {
364
+ ledgerTransaction.TransactionId = cuid();
365
+ ledgerTransaction.JournalEntryId = newJournalEntry.JournalEntryId;
366
+
367
+ await ledgerTransaction.save(dbTransaction);
368
+ // const dt = await ledgerTransaction.newLedgerTransaction(
369
+ // TransactionTypeOptions.DEBIT,
370
+ // newJournalEntry.JournalEntryId,
371
+ // );
372
+ // await dt.save();
373
+ }
374
+
375
+ for (const ledgerTransaction of creditTransactions) {
376
+ ledgerTransaction.TransactionId = cuid();
377
+ ledgerTransaction.JournalEntryId = newJournalEntry.JournalEntryId;
378
+
379
+ await ledgerTransaction.save(dbTransaction);
380
+ // const ct = await ledgerTransaction.newLedgerTransaction(
381
+ // TransactionTypeOptions.CREDIT,
382
+ // newJournalEntry.JournalEntryId,
383
+ // );
384
+ // await ct.save();
385
+ }
386
+
387
+ await this.AccountingSystem.postJournalEntry(newJournalEntry);
388
+
389
+ const payload = {
390
+ Action: 'Create',
391
+ Activity: 'Post Journal Entry',
392
+ Description: `Journal Entry (ID: ${newJournalEntry.JournalEntryId}) has been created`,
393
+ EntityType: 'JournalEntry',
394
+ EntityValueBefore: JSON.stringify({}),
395
+ EntityValueAfter: JSON.stringify(newJournalEntry),
396
+ PerformedById: 'test',
397
+ PerformedAt: new Date(),
398
+ EntityId: newJournalEntry.JournalEntryId,
399
+ };
400
+
401
+ await axios.post(
402
+ `${process.env.COMMON_API_URL}/activity-histories`,
403
+ payload,
404
+ );
405
+ } catch (error) {
406
+ throw error;
407
+ }
408
+ }
409
+
410
+ async createAccount(dbTransaction: any, account: Account) {
411
+ try {
412
+ if (!account.AccountType) {
413
+ throw new Error('AccountType is required.');
414
+ }
415
+
416
+ let createAccountPayload: any = {
417
+ Name: account.Name,
418
+ AcctNum: account.AccountNo,
419
+ AccountType: account.AccountType,
420
+ AccountSubType: account.AccountSubtype,
421
+ };
422
+
423
+ if (account.isParentAccountExists()) {
424
+ createAccountPayload = {
425
+ ...createAccountPayload,
426
+ CurrencyRef: 'MYR',
427
+ ParentRef: account.ParentAccountNo,
428
+ SubAccount: true,
429
+ };
430
+ }
431
+
432
+ console.log(
433
+ 'Finance Company Create Account: Before accSystemAccountId Create',
434
+ );
435
+
436
+ const accSystemAccountId = await this.AccountingSystem.createAccount(
437
+ createAccountPayload,
438
+ );
439
+
440
+ console.log(
441
+ 'Finance Company Create Account: After accSystemAccountId Create',
442
+ );
443
+
444
+ account.PostedToAccSystemYN = 'Y';
445
+ console.log('Finance Company Create Account: Before new Account Create');
446
+ const newAccount = await account.save(
447
+ this.CompanyId,
448
+ accSystemAccountId,
449
+ 'test',
450
+ dbTransaction,
451
+ );
452
+
453
+ console.log('Finance Company Create Account: After new Account Create');
454
+
455
+ const payload = {
456
+ Action: 'Create',
457
+ Activity: 'Account Created',
458
+ Description: `Account (ID: ${newAccount.AccountNo}) has been created`,
459
+ EntityType: 'Account',
460
+ EntityValueBefore: JSON.stringify({}),
461
+ EntityValueAfter: JSON.stringify(newAccount),
462
+ PerformedById: 'test',
463
+ PerformedAt: new Date(),
464
+ EntityId: newAccount.AccountNo,
465
+ };
466
+
467
+ await axios.post(
468
+ `${process.env.COMMON_API_URL}/activity-histories`,
469
+ payload,
470
+ );
471
+
472
+ return account;
473
+ } catch (error) {
474
+ throw error;
475
+ }
476
+ }
477
+
478
+ /**
479
+ * Issue an invoice
480
+ *
481
+ * @param dbTransaction - The database transaction to be used
482
+ * @param loginUser - The user issuing the invoice
483
+ * @param invoice - The document containing the invoice details
484
+ * @param customer - The customer to be issued the invoice
485
+ * @param dtAccountNo - The account number of the Account Receivable (AR) account to debit. If not provided, will debit the customer's default AR account
486
+ *
487
+ * @returns {Document} - Document object representing the full details of the invoice issued
488
+ */
489
+ async issueInvoice(
490
+ /*todo: loginUser & customer is NOT supposed to be optional */
491
+ dbTransaction: any,
492
+ invoice: Document,
493
+ loginUser?: LoginUserBase,
494
+ customer?: FinanceCustomerBase,
495
+ dtAccountNo?: string,
496
+ ): Promise<Document> {
497
+ try {
498
+ /*Check if the invoice number already exists (should be unique)*/
499
+ const duplicateInvoice = await FinanceCompany._DocumentRepository.findOne(
500
+ {
501
+ where: {
502
+ DocNo: invoice.DocNo,
503
+ },
504
+ transaction: dbTransaction,
505
+ },
506
+ );
507
+
508
+ if (duplicateInvoice) {
509
+ throw new Error('Invoice number already exists');
510
+ }
511
+
512
+ const documentItems = await invoice.DocumentItems;
513
+
514
+ /*Check if the document has at least 1 document item*/
515
+ if (!documentItems.length) {
516
+ throw new Error('Document must have at least 1 document item');
517
+ }
518
+
519
+ /*Check if each document item has CtAccountNo provided*/
520
+ for (const invoiceItem of documentItems) {
521
+ if (!invoiceItem.CtAccountNo) {
522
+ throw new Error(
523
+ 'Each document item should have CtAccountNo provided',
524
+ );
525
+ }
526
+ }
527
+
528
+ /*Set up the document type*/
529
+ invoice.DocType = DocType.INVOICE;
530
+ // invoice.DocNo = cuid();
531
+
532
+ /*Saving the document and document items to the database*/
533
+ await FinanceCompany._DocumentRepository.create(
534
+ {
535
+ DocNo: invoice.DocNo,
536
+ DocType: invoice.DocType,
537
+ DocDate: invoice.DocDate,
538
+ CompanyId: invoice.CompanyId,
539
+ Currency: invoice.Currency,
540
+ Amount: invoice.Amount,
541
+ Description: invoice.Description,
542
+ Status: invoice.Status,
543
+ IssuedById: invoice.IssuedById,
544
+ IssuedToId: invoice.IssuedToId,
545
+ IssuedToType: invoice.IssuedToType,
546
+ RelatedObjectId: invoice.RelatedObjectId,
547
+ RelatedObjectType: invoice.RelatedObjectType,
548
+ CreatedById: invoice.CreatedById,
549
+ CreatedAt: new Date(),
550
+ UpdatedById: invoice.UpdatedById,
551
+ UpdatedAt: new Date(),
552
+ DocPDFFileMediaId: invoice.DocPDFFileMediaId,
553
+ DocHTMLFileMediaId: invoice.DocHTMLFileMediaId,
554
+ AccSystemRefId: invoice.AccSystemRefId,
555
+ PostedToAccSystemYN: invoice.PostedToAccSystemYN,
556
+ PostedById: invoice.PostedById,
557
+ PostedDateTime: new Date(),
558
+ UseAccSystemDocYN: invoice.UseAccSystemDocYN,
559
+ },
560
+ {
561
+ transaction: dbTransaction,
562
+ },
563
+ );
564
+
565
+ for (const documentItem of documentItems) {
566
+ await FinanceCompany._DocumentItemRepository.create(
567
+ {
568
+ DocumentItemId: cuid(),
569
+ DocNo: invoice.DocNo,
570
+ Name: documentItem.Name,
571
+ NameBM: documentItem.NameBM,
572
+ Description: documentItem.Description,
573
+ ItemId: documentItem.ItemId,
574
+ ItemType: documentItem.ItemType,
575
+ ItemSKU: documentItem.ItemSKU,
576
+ ItemSerialNo: documentItem.ItemSerialNo,
577
+ Currency: documentItem.Currency,
578
+ UnitPrice: documentItem.UnitPrice,
579
+ Quantity: documentItem.Quantity,
580
+ QuantityUOM: documentItem.QuantityUOM,
581
+ Amount: documentItem.Amount,
582
+ TaxCode: documentItem.TaxCode,
583
+ TaxAmount: documentItem.TaxAmount,
584
+ TaxRate: documentItem.TaxRate,
585
+ TaxInclusiveYN: documentItem.TaxInclusiveYN,
586
+ DtAccountNo: documentItem.DtAccountNo
587
+ ? documentItem.DtAccountNo
588
+ : null,
589
+ CtAccountNo: documentItem.CtAccountNo
590
+ ? documentItem.CtAccountNo
591
+ : null,
592
+ },
593
+ {
594
+ transaction: dbTransaction,
595
+ },
596
+ );
597
+ }
598
+
599
+ /*Generating the invoice*/
600
+ if (invoice.UseAccSystemDocYN === 'Y') {
601
+ /*todo: Posting to accounting system to generate invoice*/
602
+ await this.AccountingSystem.createInvoice(invoice);
603
+ } else {
604
+ /*todo: check config file to see which invoice template is to be used for specific project*/
605
+
606
+ /*Generating invoice based on template*/
607
+ invoice.generateInvoice(invoice.IssuedById, customer);
608
+ }
609
+
610
+ const transactionDate = new Date();
611
+ const htCreditAccountAmount = new HashTable();
612
+ const htCreditAccountCurrency = new HashTable();
613
+ const htCreditAccountPurpose = new HashTable();
614
+
615
+ documentItems.forEach((invoiceItem) => {
616
+ if (!htCreditAccountAmount.exists(invoiceItem.CtAccountNo)) {
617
+ //add the credit account to the hash table
618
+ htCreditAccountAmount.add(
619
+ invoiceItem.CtAccountNo,
620
+ invoiceItem.Amount,
621
+ );
622
+
623
+ htCreditAccountCurrency.add(
624
+ invoiceItem.CtAccountNo,
625
+ invoiceItem.Currency,
626
+ );
627
+
628
+ htCreditAccountPurpose.add(invoiceItem.CtAccountNo, invoiceItem.Name);
629
+ } else {
630
+ //update the credit account amount
631
+ const d = htCreditAccountAmount.get(invoiceItem.CtAccountNo);
632
+ htCreditAccountAmount.add(
633
+ invoiceItem.CtAccountNo,
634
+ d + invoiceItem.Amount,
635
+ );
636
+ }
637
+ });
638
+
639
+ const savedItems = htCreditAccountAmount.list();
640
+
641
+ for (let i = 0; i < savedItems.length; i++) {
642
+ const journalEntry = new JournalEntry(dbTransaction);
643
+ //Temporary fix to successfully save journal entry in PostJournal
644
+ journalEntry.init({
645
+ CompanyId: this.CompanyId,
646
+ Name: 'Issue Invoice ' + invoice.DocNo,
647
+ });
648
+ const account = await Account.initAccount(
649
+ dbTransaction,
650
+ savedItems[i].value[0],
651
+ );
652
+ const creditAmount = savedItems[i].value[1];
653
+ const currency = htCreditAccountCurrency.get(savedItems[i].value[0]);
654
+ const purpose = htCreditAccountPurpose.get(savedItems[i].value[0]);
655
+
656
+ const dt = await journalEntry.newLedgerTransaction(
657
+ TransactionTypeOptions.DEBIT,
658
+ );
659
+
660
+ if (dtAccountNo) {
661
+ /*Transacting using AR account provided*/
662
+ dt.AccountNo = dtAccountNo;
663
+ } else {
664
+ /*Transacting based on default customer AR account*/
665
+ dt.AccountNo = (await customer.AccountReceivable).AccountNo;
666
+ }
667
+
668
+ dt.Currency = currency ? currency : 'MYR';
669
+ dt.DebitAmount = creditAmount ? creditAmount : 0.0;
670
+ dt.Date = transactionDate;
671
+ dt.Description = `Transaction for ${purpose} - ${savedItems[i].value[0]}`;
672
+ dt.Name = `Transaction for ${purpose}`;
673
+ dt.RelatedDocNo = invoice.DocNo;
674
+
675
+ const ct = await journalEntry.newLedgerTransaction(
676
+ TransactionTypeOptions.CREDIT,
677
+ );
678
+ ct.AccountNo = savedItems[i].value[0];
679
+ ct.Currency = currency ? currency : 'MYR';
680
+ ct.CreditAmount = creditAmount ? creditAmount : 0.0;
681
+ ct.Date = transactionDate;
682
+ ct.Description = `Transaction for ${purpose}`;
683
+ ct.Name = account.Name;
684
+ ct.RelatedDocNo = invoice.DocNo;
685
+
686
+ await this.postJournal(dbTransaction, journalEntry);
687
+ }
688
+
689
+ return invoice;
690
+ } catch (err) {
691
+ // tslint:disable-next-line:no-console
692
+ console.log('Issue invoice err: ', err);
693
+ throw err;
694
+ }
695
+ }
696
+
697
+ /**
698
+ * Issue a debit note
699
+ *
700
+ * @param dbTransaction - The database transaction to be used
701
+ * @param loginUser - The user issuing the invoice
702
+ * @param invoice - The document containing the invoice details
703
+ * @param customer - The customer to be issued the invoice
704
+ * @param dtAccountNo - The account number of the Account Receivable (AR) account to debit. If not provided, will debit the customer's default AR account
705
+ *
706
+ * @returns {Document} - Document object representing the full details of the invoice issued
707
+ */
708
+ async issueDebitNote(
709
+ dbTransaction: any,
710
+ loginUser: LoginUserBase,
711
+ invoice: Document,
712
+ customer: FinanceCustomerBase,
713
+ dtAccountNo?: string,
714
+ ): Promise<Document> {
715
+ try {
716
+ /*Check if the invoice number already exists (should be unique)*/
717
+ const duplicateInvoice = await FinanceCompany._DocumentRepository.findOne(
718
+ {
719
+ where: {
720
+ DocNo: invoice.DocNo,
721
+ },
722
+ transaction: dbTransaction,
723
+ },
724
+ );
725
+
726
+ if (duplicateInvoice) {
727
+ throw new Error('Invoice number already exists');
728
+ }
729
+
730
+ const documentItems = await invoice.DocumentItems;
731
+
732
+ /*Check if the document has at least 1 document item*/
733
+ if (!documentItems.length) {
734
+ throw new Error('Document must have at least 1 document item');
735
+ }
736
+
737
+ /*Check if each document item has CtAccountNo provided*/
738
+ for (const invoiceItem of documentItems) {
739
+ if (!invoiceItem.CtAccountNo) {
740
+ throw new Error(
741
+ 'Each document item should have CtAccountNo provided',
742
+ );
743
+ }
744
+ }
745
+
746
+ /*Set up the document type*/
747
+ invoice.DocType = DocType.DEBIT_NOTE;
748
+ // invoice.DocNo = cuid();
749
+
750
+ /*Saving the document and document items to the database*/
751
+ await FinanceCompany._DocumentRepository.create(
752
+ {
753
+ DocNo: invoice.DocNo,
754
+ DocType: invoice.DocType,
755
+ DocDate: invoice.DocDate,
756
+ CompanyId: invoice.CompanyId,
757
+ Currency: invoice.Currency,
758
+ Amount: invoice.Amount,
759
+ Description: invoice.Description,
760
+ Status: invoice.Status,
761
+ IssuedById: loginUser.ObjectId,
762
+ IssuedToId: customer.ObjectId,
763
+ IssuedToType: invoice.IssuedToType,
764
+ RelatedObjectId: invoice.RelatedObjectId,
765
+ RelatedObjectType: invoice.RelatedObjectType,
766
+ CreatedById: loginUser.ObjectId,
767
+ CreatedAt: new Date(),
768
+ UpdatedById: loginUser.ObjectId,
769
+ UpdatedAt: new Date(),
770
+ DocPDFFileMediaId: invoice.DocPDFFileMediaId,
771
+ DocHTMLFileMediaId: invoice.DocHTMLFileMediaId,
772
+ AccSystemRefId: invoice.AccSystemRefId,
773
+ PostedToAccSystemYN: invoice.PostedToAccSystemYN,
774
+ PostedById: invoice.PostedById,
775
+ PostedDateTime: new Date(),
776
+ UseAccSystemDocYN: invoice.UseAccSystemDocYN,
777
+ },
778
+ {
779
+ transaction: dbTransaction,
780
+ },
781
+ );
782
+
783
+ documentItems.forEach(async (documentItem) => {
784
+ await FinanceCompany._DocumentItemRepository.create(
785
+ {
786
+ DocumentItemId: cuid(),
787
+ DocNo: documentItem.DocNo,
788
+ Name: documentItem.Name,
789
+ NameBM: documentItem.NameBM,
790
+ Description: documentItem.Description,
791
+ ItemId: documentItem.ItemId,
792
+ ItemType: documentItem.ItemType,
793
+ ItemSKU: documentItem.ItemSKU,
794
+ ItemSerialNo: documentItem.ItemSerialNo,
795
+ Currency: documentItem.Currency,
796
+ UnitPrice: documentItem.UnitPrice,
797
+ Quantity: documentItem.Quantity,
798
+ QuantityUOM: documentItem.QuantityUOM,
799
+ Amount: documentItem.Amount,
800
+ TaxCode: documentItem.TaxCode,
801
+ TaxAmount: documentItem.TaxAmount,
802
+ TaxRate: documentItem.TaxRate,
803
+ TaxInclusiveYN: documentItem.TaxInclusiveYN,
804
+ DtAccountNo: documentItem.DtAccountNo,
805
+ CtAccountNo: documentItem.CtAccountNo,
806
+ },
807
+ {
808
+ transaction: dbTransaction,
809
+ },
810
+ );
811
+ });
812
+
813
+ /*Generating the invoice*/
814
+ if (invoice.UseAccSystemDocYN === 'Y') {
815
+ /*todo: Posting to accounting system to generate invoice*/
816
+ await this.AccountingSystem.createInvoice(invoice);
817
+ } else {
818
+ /*todo: check config file to see which invoice template is to be used for specific project*/
819
+
820
+ /*Generating invoice based on template*/
821
+ invoice.generateInvoice(loginUser.IDNo, customer);
822
+ }
823
+
824
+ const transactionDate = new Date();
825
+ const htCreditAccountAmount = new HashTable();
826
+ const htCreditAccountCurrency = new HashTable();
827
+
828
+ documentItems.forEach((invoiceItem) => {
829
+ if (!htCreditAccountAmount.exists(invoiceItem.CtAccountNo)) {
830
+ //add the credit account to the hash table
831
+ htCreditAccountAmount.add(
832
+ invoiceItem.CtAccountNo,
833
+ invoiceItem.Amount,
834
+ );
835
+
836
+ htCreditAccountCurrency.add(
837
+ invoiceItem.CtAccountNo,
838
+ invoiceItem.Currency,
839
+ );
840
+ } else {
841
+ //update the credit account amount
842
+ const d = htCreditAccountAmount.get(invoiceItem.CtAccountNo);
843
+ htCreditAccountAmount.add(
844
+ invoiceItem.CtAccountNo,
845
+ d + invoiceItem.Amount,
846
+ );
847
+ }
848
+ });
849
+
850
+ const savedItems = htCreditAccountAmount.list();
851
+
852
+ for (let i = 0; i < savedItems.length; i++) {
853
+ const journalEntry = new JournalEntry(dbTransaction);
854
+ //Temporary fix to successfully save journal entry in PostJournal
855
+ journalEntry.init({
856
+ CompanyId: this.CompanyId,
857
+ Name: 'issue Invoice ' + invoice.DocNo,
858
+ });
859
+ const account = await Account.initAccount(
860
+ dbTransaction,
861
+ savedItems[i].value[0],
862
+ );
863
+ const creditAmount = savedItems[i].value[1];
864
+ const currency = htCreditAccountCurrency.get(savedItems[i].value[0]);
865
+
866
+ const dt = await journalEntry.newLedgerTransaction(
867
+ TransactionTypeOptions.DEBIT,
868
+ );
869
+
870
+ if (dtAccountNo) {
871
+ /*Transacting using AR account provided*/
872
+ dt.AccountNo = dtAccountNo;
873
+ } else {
874
+ /*Transacting based on default customer AR account*/
875
+ dt.AccountNo = (await customer.AccountReceivable).AccountNo;
876
+ }
877
+
878
+ dt.Currency = currency ? currency : 'MYR';
879
+ dt.DebitAmount = creditAmount ? creditAmount : 0.0;
880
+ dt.Date = transactionDate;
881
+ dt.Description = account.Name;
882
+ dt.Name = account.Name;
883
+ dt.RelatedDocNo = invoice.DocNo;
884
+
885
+ const ct = await journalEntry.newLedgerTransaction(
886
+ TransactionTypeOptions.CREDIT,
887
+ );
888
+ ct.AccountNo = savedItems[i].key;
889
+ ct.Currency = currency ? currency : 'MYR';
890
+ ct.CreditAmount = creditAmount ? creditAmount : 0.0;
891
+ ct.Date = transactionDate;
892
+ ct.Description = account.Name;
893
+ ct.Name = account.Name;
894
+ ct.RelatedDocNo = invoice.DocNo;
895
+
896
+ await this.postJournal(dbTransaction, journalEntry);
897
+ }
898
+
899
+ return invoice;
900
+ } catch (err) {
901
+ // tslint:disable-next-line:no-console
902
+ console.log('Issue debit note err: ', err);
903
+ throw err;
904
+ }
905
+ }
906
+
907
+ /**
908
+ * Issue a credit note
909
+ *
910
+ * @param dbTransaction - The database transaction to be used
911
+ * @param loginUser - The user issuing the credit note
912
+ * @param creditNote - The document containing the credit note details
913
+ * @param customer - The customer to be issued the credit note
914
+ * @param ctAccountNo - The account number of the Account Payable (AP) account to debit. If not provided, will debit the customer's default AR account
915
+ *
916
+ * @returns {Document} - Document object representing the full details of the invoice issued
917
+ */
918
+ async issueCreditNote(
919
+ dbTransaction: any,
920
+ loginUser: LoginUserBase,
921
+ creditNote: Document,
922
+ customer: FinanceCustomerBase,
923
+ ctAccountNo?: string,
924
+ ): Promise<Document> {
925
+ try {
926
+ /*Check if the invoice number already exists (should be unique)*/
927
+ const duplicateCreditNote =
928
+ await FinanceCompany._DocumentRepository.findOne({
929
+ where: {
930
+ DocNo: creditNote.DocNo,
931
+ },
932
+ transaction: dbTransaction,
933
+ });
934
+
935
+ if (duplicateCreditNote) {
936
+ throw new Error('Invoice number already exists');
937
+ }
938
+
939
+ const documentItems = await creditNote.DocumentItems;
940
+
941
+ /*Check if the document has at least 1 document item*/
942
+ if (!documentItems.length) {
943
+ throw new Error('Document must have at least 1 document item');
944
+ }
945
+
946
+ /*Check if each document item has CtAccountNo provided*/
947
+ for (const invoiceItem of documentItems) {
948
+ if (!invoiceItem.CtAccountNo) {
949
+ throw new Error(
950
+ 'Each document item should have CtAccountNo provided',
951
+ );
952
+ }
953
+ }
954
+
955
+ /*Set up the document type*/
956
+ creditNote.DocType = DocType.DEBIT_NOTE;
957
+
958
+ /*Saving the document and document items to the database*/
959
+ await FinanceCompany._DocumentRepository.create(
960
+ {
961
+ DocNo: creditNote.DocNo,
962
+ DocType: creditNote.DocType,
963
+ DocDate: creditNote.DocDate,
964
+ CompanyId: creditNote.CompanyId,
965
+ Currency: creditNote.Currency,
966
+ Amount: creditNote.Amount,
967
+ Description: creditNote.Description,
968
+ Status: creditNote.Status,
969
+ IssuedById: loginUser.ObjectId,
970
+ IssuedToId: customer.ObjectId,
971
+ IssuedToType: creditNote.IssuedToType,
972
+ RelatedObjectId: creditNote.RelatedObjectId,
973
+ RelatedObjectType: creditNote.RelatedObjectType,
974
+ CreatedById: loginUser.ObjectId,
975
+ CreatedAt: new Date(),
976
+ UpdatedById: loginUser.ObjectId,
977
+ UpdatedAt: new Date(),
978
+ DocPDFFileMediaId: creditNote.DocPDFFileMediaId,
979
+ DocHTMLFileMediaId: creditNote.DocHTMLFileMediaId,
980
+ AccSystemRefId: creditNote.AccSystemRefId,
981
+ PostedToAccSystemYN: creditNote.PostedToAccSystemYN,
982
+ PostedById: creditNote.PostedById,
983
+ PostedDateTime: new Date(),
984
+ UseAccSystemDocYN: creditNote.UseAccSystemDocYN,
985
+ },
986
+ {
987
+ transaction: dbTransaction,
988
+ },
989
+ );
990
+
991
+ documentItems.forEach(async (documentItem) => {
992
+ await FinanceCompany._DocumentItemRepository.create(
993
+ {
994
+ DocumentItemId: documentItem.DocumentItemId,
995
+ DocNo: documentItem.DocNo,
996
+ Name: documentItem.Name,
997
+ NameBM: documentItem.NameBM,
998
+ Description: documentItem.Description,
999
+ ItemId: documentItem.ItemId,
1000
+ ItemType: documentItem.ItemType,
1001
+ ItemSKU: documentItem.ItemSKU,
1002
+ ItemSerialNo: documentItem.ItemSerialNo,
1003
+ Currency: documentItem.Currency,
1004
+ UnitPrice: documentItem.UnitPrice,
1005
+ Quantity: documentItem.Quantity,
1006
+ QuantityUOM: documentItem.QuantityUOM,
1007
+ Amount: documentItem.Amount,
1008
+ TaxCode: documentItem.TaxCode,
1009
+ TaxAmount: documentItem.TaxAmount,
1010
+ TaxRate: documentItem.TaxRate,
1011
+ TaxInclusiveYN: documentItem.TaxInclusiveYN,
1012
+ DtAccountNo: documentItem.DtAccountNo,
1013
+ CtAccountNo: documentItem.CtAccountNo,
1014
+ },
1015
+ {
1016
+ transaction: dbTransaction,
1017
+ },
1018
+ );
1019
+ });
1020
+
1021
+ /*Generating the credit note*/
1022
+ if (creditNote.UseAccSystemDocYN === 'Y') {
1023
+ /*todo: Posting to accounting system to generate creditNote*/
1024
+ await this.AccountingSystem.createCreditNote(creditNote);
1025
+ } else {
1026
+ /*todo: check config file to see which invoice template is to be used for specific project*/
1027
+
1028
+ /*Generating credit note based on template*/
1029
+ creditNote.generateCreditNote(loginUser.IDNo, customer);
1030
+ }
1031
+
1032
+ const journalEntry = new JournalEntry(dbTransaction);
1033
+ //Temporary fix to successfully save journal entry in PostJournal
1034
+ journalEntry.init({
1035
+ CompanyId: this.CompanyId,
1036
+ Name: 'Issue Debit Note ' + creditNote.DocNo,
1037
+ });
1038
+
1039
+ const transactionDate = new Date();
1040
+
1041
+ const creditTransaction = await journalEntry.newLedgerTransaction(
1042
+ TransactionTypeOptions.CREDIT,
1043
+ );
1044
+
1045
+ if (ctAccountNo) {
1046
+ /*Transacting using AR account provided*/
1047
+ creditTransaction.AccountNo = ctAccountNo;
1048
+ } else {
1049
+ /*Transacting based on default customer AR account*/
1050
+ creditTransaction.AccountNo = customer.CustSystemCode + '-AP';
1051
+ }
1052
+
1053
+ creditTransaction.Currency = creditNote.Currency;
1054
+ creditTransaction.CreditAmount = creditNote.Amount;
1055
+ creditTransaction.Date = transactionDate;
1056
+ creditTransaction.Description = creditNote.DocNo;
1057
+ creditTransaction.Name = creditNote.DocNo;
1058
+
1059
+ for (const invoiceItem of documentItems) {
1060
+ const itemLedger = await journalEntry.newLedgerTransaction(
1061
+ TransactionTypeOptions.DEBIT,
1062
+ );
1063
+ itemLedger.AccountNo = invoiceItem.CtAccountNo;
1064
+ itemLedger.Currency = invoiceItem.Currency;
1065
+ itemLedger.DebitAmount = invoiceItem.Amount;
1066
+ itemLedger.Date = transactionDate;
1067
+ itemLedger.Description = invoiceItem.Description;
1068
+ itemLedger.Name = invoiceItem.Name;
1069
+ }
1070
+
1071
+ this.postJournal(dbTransaction, journalEntry);
1072
+
1073
+ const payload = {
1074
+ Action: 'Create',
1075
+ Activity: 'Issuing a Credit Note Transaction',
1076
+ Description: `Credit Transaction (ID: ${creditTransaction.TransactionId}) has been created`,
1077
+ EntityType: 'CreditTransaction',
1078
+ EntityValueBefore: JSON.stringify({}),
1079
+ EntityValueAfter: JSON.stringify(creditTransaction),
1080
+ PerformedById: 'test',
1081
+ PerformedAt: transactionDate,
1082
+ EntityId: creditTransaction.TransactionId,
1083
+ };
1084
+
1085
+ await axios.post(
1086
+ `${process.env.COMMON_API_URL}/activity-histories`,
1087
+ payload,
1088
+ );
1089
+
1090
+ return creditNote;
1091
+ } catch (err) {
1092
+ // tslint:disable-next-line:no-console
1093
+ console.log('Issue credit note err: ', err);
1094
+ throw err;
1095
+ }
1096
+ }
1097
+
1098
+ /**
1099
+ * Register a payment collected
1100
+ *
1101
+ * @param dbTransaction - The database transaction to be used
1102
+ * @param loginUser - The user collecting the payment
1103
+ * @param payment - The payment object containing payment details
1104
+ * @param customer - The customer making the payment
1105
+ * @param ctAccountNo - The account number of the customer's Account Receivable account to transact, else the default customer account payable receivable will be used
1106
+ *
1107
+ * @returns {Payment} - Payment object representing the full detals of the payment collection recorded
1108
+ */
1109
+ async collectPayment(
1110
+ dbTransaction: any,
1111
+ loginUser: LoginUserBase,
1112
+ payment: Payment,
1113
+ customer: FinanceCustomerBase,
1114
+ ctAccountNo?: string,
1115
+ ): Promise<Payment> {
1116
+ try {
1117
+ /*validation 1: Make sure that the payment has at least 1 payment item*/
1118
+ const paymentItems = await payment.PaymentItems;
1119
+ if (paymentItems.length < 1) {
1120
+ throw new Error(
1121
+ 'Atleast one payment item is required to identify what payment is being paid for.',
1122
+ );
1123
+ }
1124
+
1125
+ /*validation 2: Make sure that the payment has at least 1 payment method*/
1126
+ const paymentPaidWithItems = await payment.PaymentPaidWith;
1127
+ if (paymentPaidWithItems.length < 1) {
1128
+ throw new Error(
1129
+ 'Atleast one payment paid with item is required to identify how the payment was made.',
1130
+ );
1131
+ }
1132
+
1133
+ payment.PaymentId = cuid();
1134
+ payment.PaymentType = PaymentType.PAYMENT_RECEIVED;
1135
+ payment.ReceivedBy = loginUser.ObjectId;
1136
+
1137
+ await FinanceCompany._PaymentRepository.create(
1138
+ {
1139
+ PaymentId: payment.PaymentId,
1140
+ PaymentType: payment.PaymentType,
1141
+ PaymentDate: payment.PaymentDate,
1142
+ Description: payment.Description,
1143
+ Currency: payment.Currency,
1144
+ Amount: payment.Amount,
1145
+ Status: PaymentStatus.SUCCESSFUL,
1146
+ PostedToAccSystemYN: 'N',
1147
+ ReceivedBy: payment.ReceivedBy,
1148
+ UpdatedAt: new Date(),
1149
+ UpdatedBy: loginUser.ObjectId,
1150
+ CreatedAt: new Date(),
1151
+ CreatedBy: loginUser.ObjectId,
1152
+ },
1153
+ { transaction: dbTransaction },
1154
+ );
1155
+
1156
+ for (const paymentItem of paymentItems) {
1157
+ await FinanceCompany._PaymentItemRepository.create(
1158
+ {
1159
+ PaymentId: payment.PaymentId,
1160
+ PayForObjectId: paymentItem.PayForObjectId,
1161
+ PayForObjectType: paymentItem.PayForObjectType,
1162
+ Currency: paymentItem.Currency,
1163
+ Amount: paymentItem.Amount,
1164
+ Name: paymentItem.Name,
1165
+ Description: paymentItem.Description,
1166
+ },
1167
+ { transaction: dbTransaction },
1168
+ );
1169
+ }
1170
+
1171
+ for (const paymentPaidWithItem of paymentPaidWithItems) {
1172
+ await FinanceCompany._PaymentPaidWithRepository.create(
1173
+ {
1174
+ PaymentId: payment.PaymentId,
1175
+ MethodTypeId: paymentPaidWithItem.MethodTypeId,
1176
+ Currency: paymentPaidWithItem.Currency,
1177
+ Amount: paymentPaidWithItem.Amount,
1178
+ Status: paymentPaidWithItem.Status,
1179
+ TransactionId: paymentPaidWithItem.TransactionId,
1180
+ RefBank: paymentPaidWithItem.RefBank,
1181
+ RefName: paymentPaidWithItem.RefName,
1182
+ RefNo: paymentPaidWithItem.RefNo,
1183
+ RefOther1: paymentPaidWithItem.RefOther1,
1184
+ RefOther2: paymentPaidWithItem.RefOther2,
1185
+ RefOther3: paymentPaidWithItem.RefOther3,
1186
+ RefOther4: paymentPaidWithItem.RefOther4,
1187
+ RefOther5: paymentPaidWithItem.RefOther5,
1188
+ Remarks: paymentPaidWithItem.Remarks,
1189
+ PaymentMediaId: paymentPaidWithItem.PaymentMediaId,
1190
+ },
1191
+ { transaction: dbTransaction },
1192
+ );
1193
+ }
1194
+
1195
+ /*Registering the Journal Entries for the transaction*/
1196
+ const transactionDate = new Date();
1197
+
1198
+ for (const paymentPaidWith of paymentPaidWithItems) {
1199
+ let paymentMethodType: PaymentMethodType;
1200
+
1201
+ try {
1202
+ paymentMethodType = await PaymentMethodType.initMethodType(
1203
+ dbTransaction,
1204
+ paymentPaidWith.MethodTypeId,
1205
+ );
1206
+ } catch (error) {
1207
+ if (error instanceof RecordNotFoundError) {
1208
+ throw new Error('Invalid Payment Method Type Id');
1209
+ } else {
1210
+ throw error;
1211
+ }
1212
+ }
1213
+ const journalEntry = new JournalEntry(dbTransaction);
1214
+
1215
+ journalEntry.init({
1216
+ CompanyId: this.CompanyId,
1217
+ Name: 'Collect-payments for ' + payment.PaymentId,
1218
+ });
1219
+
1220
+ const debitLT = await journalEntry.newLedgerTransaction(
1221
+ TransactionTypeOptions.DEBIT,
1222
+ );
1223
+ debitLT.AccountNo = paymentMethodType.AccountNo;
1224
+ debitLT.Currency = paymentPaidWith.Currency;
1225
+ debitLT.DebitAmount = paymentPaidWith.Amount;
1226
+ debitLT.Date = transactionDate;
1227
+ debitLT.Description = customer.FullName;
1228
+ debitLT.Name = customer.FullName;
1229
+
1230
+ const creditLT = await journalEntry.newLedgerTransaction(
1231
+ TransactionTypeOptions.CREDIT,
1232
+ );
1233
+
1234
+ if (ctAccountNo) {
1235
+ creditLT.AccountNo = ctAccountNo;
1236
+ } else {
1237
+ creditLT.AccountNo = (await customer.AccountReceivable).AccountNo;
1238
+ }
1239
+ creditLT.Currency = paymentPaidWith.Currency;
1240
+ creditLT.CreditAmount = paymentPaidWith.Amount;
1241
+ creditLT.Date = transactionDate;
1242
+ creditLT.Description = paymentMethodType.Name;
1243
+ creditLT.Name = paymentMethodType.Name;
1244
+
1245
+ await this.postJournal(dbTransaction, journalEntry);
1246
+ }
1247
+
1248
+ /*todo: saving a record into the activity history table*/
1249
+
1250
+ return payment;
1251
+ } catch (error) {
1252
+ throw error;
1253
+ }
1254
+ }
1255
+
1256
+ /**
1257
+ * Method to make payment to a customer.
1258
+ *
1259
+ * @param dbTransaction - The database transaction to be used
1260
+ * @param loginUser - The user logging in and the user who collected the payment.
1261
+ * @param payment - The payment object containing payment details
1262
+ * @param customer - The customer who is receiving the payment.
1263
+ * @param ctAccountNo - The account number of the customer's Account Payable account to transact, else the default customer account payable will be used.
1264
+ *
1265
+ * @returns {Payment} - Payment object representing the full details of the payment collection recorded
1266
+ */
1267
+ async makePayment(
1268
+ dbTransaction: any,
1269
+ loginUser: LoginUserBase,
1270
+ payment: Payment,
1271
+ customer: FinanceCustomerBase,
1272
+ dtAccountNo?: string,
1273
+ ): Promise<Payment> {
1274
+ let paymentMethodType: PaymentMethodType;
1275
+ try {
1276
+ const paymentPaidWith = await payment.PaymentPaidWith;
1277
+ if (paymentPaidWith.length < 1) {
1278
+ throw new Error(
1279
+ 'Atleast one payment paid with item is required to identify how the payment was made.',
1280
+ );
1281
+ }
1282
+ paymentMethodType = await PaymentMethodType.initMethodType(
1283
+ dbTransaction,
1284
+ paymentPaidWith[0].MethodTypeId,
1285
+ );
1286
+
1287
+ const paymentItems = await payment.PaymentItems;
1288
+ if (paymentItems.length < 1) {
1289
+ throw new Error(
1290
+ 'Atleast one payment item is required to identify what payment is being paid for',
1291
+ );
1292
+ }
1293
+
1294
+ payment.PaymentId = cuid();
1295
+ payment.PaymentType = PaymentType.PAYOUT;
1296
+ payment.ReceivedBy = loginUser.ObjectId;
1297
+
1298
+ await FinanceCompany._PaymentRepository.create(
1299
+ {
1300
+ PaymentId: payment.PaymentId,
1301
+ PaymentType: payment.PaymentType,
1302
+ PaymentDate: payment.PaymentDate,
1303
+ Description: payment.Description,
1304
+ Currency: payment.Currency,
1305
+ ReceivedBy: payment.ReceivedBy,
1306
+ Amount: payment.Amount,
1307
+ Status: PaymentStatus.SUCCESSFUL,
1308
+ PostedToAccSystemYN: 'N',
1309
+ UpdatedAt: new Date(),
1310
+ UpdatedBy: loginUser.ObjectId,
1311
+ CreatedAt: new Date(),
1312
+ CreatedBy: loginUser.ObjectId,
1313
+ },
1314
+ { transaction: dbTransaction },
1315
+ );
1316
+
1317
+ paymentItems.forEach(async (paymentItem) => {
1318
+ await FinanceCompany._PaymentItemRepository.create(
1319
+ {
1320
+ PaymentId: payment.PaymentId,
1321
+ PayForObjectId: paymentItem.PayForObjectId,
1322
+ PayForObjectType: paymentItem.PayForObjectType,
1323
+ Currency: paymentItem.Currency,
1324
+ Amount: paymentItem.Amount,
1325
+ Name: paymentItem.Name,
1326
+ Description: paymentItem.Description,
1327
+ },
1328
+ { transaction: dbTransaction },
1329
+ );
1330
+ });
1331
+
1332
+ for (const paymentPaidWithItem of paymentPaidWith) {
1333
+ await FinanceCompany._PaymentPaidWithRepository.create(
1334
+ {
1335
+ PaymentId: payment.PaymentId,
1336
+ MethodTypeId: paymentPaidWithItem.MethodTypeId,
1337
+ Currency: paymentPaidWithItem.Currency,
1338
+ Amount: paymentPaidWithItem.Amount,
1339
+ Status: paymentPaidWithItem.Status,
1340
+ TransactionId: paymentPaidWithItem.TransactionId,
1341
+ RefBank: paymentPaidWithItem.RefBank,
1342
+ RefName: paymentPaidWithItem.RefName,
1343
+ RefNo: paymentPaidWithItem.RefNo,
1344
+ RefOther1: paymentPaidWithItem.RefOther1,
1345
+ RefOther2: paymentPaidWithItem.RefOther2,
1346
+ RefOther3: paymentPaidWithItem.RefOther3,
1347
+ RefOther4: paymentPaidWithItem.RefOther4,
1348
+ RefOther5: paymentPaidWithItem.RefOther5,
1349
+ Remarks: paymentPaidWithItem.Remarks,
1350
+ PaymentMediaId: paymentPaidWithItem.PaymentMediaId,
1351
+ },
1352
+ { transaction: dbTransaction },
1353
+ );
1354
+ }
1355
+ const transactionDate = new Date();
1356
+ for (const paymentPaidWithItem of paymentPaidWith) {
1357
+ try {
1358
+ paymentMethodType = await PaymentMethodType.initMethodType(
1359
+ dbTransaction,
1360
+ paymentPaidWithItem.MethodTypeId,
1361
+ );
1362
+
1363
+ const journalEntry = new JournalEntry(dbTransaction);
1364
+ //Temporary fix to successfully save journal entry in PostJournal
1365
+ journalEntry.init({
1366
+ CompanyId: this.CompanyId,
1367
+ Name: 'Make Payment for ' + payment.PaymentId,
1368
+ });
1369
+
1370
+ const creditLT = await journalEntry.newLedgerTransaction(
1371
+ TransactionTypeOptions.CREDIT,
1372
+ );
1373
+ creditLT.AccountNo = paymentMethodType.AccountNo;
1374
+ creditLT.Currency = paymentPaidWithItem.Currency;
1375
+ creditLT.CreditAmount = paymentPaidWithItem.Amount;
1376
+ creditLT.Date = transactionDate;
1377
+ creditLT.Description = customer.FullName;
1378
+ creditLT.Name = customer.FullName;
1379
+
1380
+ const debitLT = await journalEntry.newLedgerTransaction(
1381
+ TransactionTypeOptions.DEBIT,
1382
+ );
1383
+ if (dtAccountNo) {
1384
+ debitLT.AccountNo = dtAccountNo;
1385
+ } else {
1386
+ debitLT.AccountNo = (await customer.AccountPayable).AccountNo;
1387
+ }
1388
+ debitLT.Currency = paymentPaidWithItem.Currency;
1389
+ debitLT.DebitAmount = paymentPaidWithItem.Amount;
1390
+ debitLT.Date = transactionDate;
1391
+ debitLT.Description = paymentMethodType.Name;
1392
+ debitLT.Name = paymentMethodType.Name;
1393
+
1394
+ await this.postJournal(dbTransaction, journalEntry);
1395
+ } catch (error) {
1396
+ if (error instanceof RecordNotFoundError) {
1397
+ throw new Error('Invalid Payment Method Type Id');
1398
+ } else {
1399
+ throw error;
1400
+ }
1401
+ }
1402
+ }
1403
+ /*todo: saving a record into the activity history table*/
1404
+
1405
+ return payment;
1406
+ } catch (error) {
1407
+ if (error instanceof RecordNotFoundError) {
1408
+ throw new Error('Invalid PaymentMethodType id');
1409
+ } else {
1410
+ throw error;
1411
+ }
1412
+ }
1413
+ }
1414
+
1415
+ get PaymentMethods(): Promise<PaymentMethod[]> {
1416
+ return new Promise((resolve, reject) => {
1417
+ if (this.CompanyId !== 'New') {
1418
+ FinanceCompany._PaymentMethodRepository
1419
+ .findAll({
1420
+ where: {
1421
+ CompanyId: this.CompanyId,
1422
+ },
1423
+ transaction: this._DbTransaction,
1424
+ })
1425
+ .then((paymentMethod) => {
1426
+ const paymentMethodObjects = paymentMethod.map(
1427
+ (paymentMethodData) => {
1428
+ return new Promise((resolve, reject) => {
1429
+ FinanceCompany._PaymentMethodTypeRepository
1430
+ .findAll({
1431
+ where: {
1432
+ MethodId: paymentMethodData.MethodId,
1433
+ },
1434
+ transaction: this._DbTransaction,
1435
+ raw: true,
1436
+ })
1437
+ .then((paymentMethodTypes) => {
1438
+ const paymentMethodObjects = {
1439
+ ...paymentMethodData.get({ plain: true }),
1440
+ Types: paymentMethodTypes,
1441
+ };
1442
+ resolve(paymentMethodObjects);
1443
+ })
1444
+ .catch((err) => {
1445
+ reject(err);
1446
+ });
1447
+ }).then((paymentMethods) => paymentMethods);
1448
+ },
1449
+ );
1450
+ return Promise.all(paymentMethodObjects);
1451
+ })
1452
+ .then((paymentMethodObjects) => {
1453
+ this._PaymentMethods = paymentMethodObjects;
1454
+ resolve(this._PaymentMethods);
1455
+ })
1456
+ .catch((err) => {
1457
+ reject(err);
1458
+ });
1459
+ } else {
1460
+ resolve(this._PaymentMethods);
1461
+ }
1462
+ });
1463
+ }
1464
+
1465
+ /**
1466
+ * Method to load / reload the payment methods based on the configuration file
1467
+ */
1468
+ async LoadPaymentMethods(): Promise<void> {
1469
+ /*Retrieve the payment method from the config file */
1470
+ const { companyId, paymentMethods } = config.financeCompanies['TXG-FS'];
1471
+ const paymentMethod = new PaymentMethod();
1472
+ for (const method in paymentMethods) {
1473
+ const paymentMethodData =
1474
+ await FinanceCompany._PaymentMethodRepository.findOne({
1475
+ where: {
1476
+ MethodId: paymentMethods[method].id,
1477
+ Name: paymentMethods[method].name,
1478
+ },
1479
+ });
1480
+
1481
+ if (!paymentMethodData) {
1482
+ const newPaymentMethod =
1483
+ await FinanceCompany._PaymentMethodRepository.create({
1484
+ MethodId: paymentMethods[method].id,
1485
+ Name: paymentMethods[method].name,
1486
+ CompanyId: companyId,
1487
+ });
1488
+
1489
+ this._PaymentMethods.push(newPaymentMethod);
1490
+ }
1491
+ this._PaymentMethods.push(paymentMethodData);
1492
+ }
1493
+
1494
+ this._PaymentMethods.forEach(async (item) => {
1495
+ const p = item?.get({ plain: true });
1496
+
1497
+ for (const method in paymentMethods) {
1498
+ if (!p) {
1499
+ continue;
1500
+ }
1501
+
1502
+ if (p.MethodId === paymentMethods[method]?.id) {
1503
+ const paymentMethodTypeData =
1504
+ await FinanceCompany._PaymentMethodTypeRepository.findOne({
1505
+ where: {
1506
+ MethodId: p.MethodId,
1507
+ },
1508
+ });
1509
+
1510
+ if (!paymentMethodTypeData) {
1511
+ const configPaymentMethodTypes = paymentMethods[method]?.types;
1512
+
1513
+ for (const methodType in configPaymentMethodTypes) {
1514
+ // TODO: Create a seeder for payment method account
1515
+ /*validate whether account data already exists*/
1516
+ const accountData =
1517
+ await FinanceCompany._AccountRepository.findOne({
1518
+ where: {
1519
+ AccountNo: configPaymentMethodTypes[methodType].accountNo,
1520
+ },
1521
+ });
1522
+
1523
+ /*generating account data if not exist */
1524
+ if (!accountData) {
1525
+ const accountPayload = {
1526
+ CompanyId: companyId,
1527
+ Name: configPaymentMethodTypes[methodType].name,
1528
+ AccountType: 'PaymentMethod',
1529
+ CreatedAt: new Date(),
1530
+ CreatedById: 'System',
1531
+ AccSystemRefId: 'REF',
1532
+ PostedToAccSystemYN: 'N',
1533
+ };
1534
+
1535
+ try {
1536
+ await FinanceCompany._AccountRepository.create({
1537
+ AccountNo: configPaymentMethodTypes[methodType].accountNo,
1538
+ ...accountPayload,
1539
+ });
1540
+
1541
+ await FinanceCompany._AccountRepository.create({
1542
+ AccountNo:
1543
+ configPaymentMethodTypes[methodType]
1544
+ .processingFeeAccountNo,
1545
+ ...accountPayload,
1546
+ });
1547
+ } catch (err) {}
1548
+ }
1549
+
1550
+ const paymentMethodTypePayload = {
1551
+ MethodId: p.MethodId,
1552
+ MethodTypeId: configPaymentMethodTypes[methodType].id,
1553
+ Name: configPaymentMethodTypes[methodType].name,
1554
+ AccountNo: configPaymentMethodTypes[methodType].accountNo,
1555
+ ProcessingFeeRate:
1556
+ configPaymentMethodTypes[methodType].processingFeeRate,
1557
+ ProcessingFeeAccountNo:
1558
+ configPaymentMethodTypes[methodType].processingFeeAccountNo,
1559
+ };
1560
+
1561
+ try {
1562
+ await paymentMethod.newPaymentMethodType(
1563
+ paymentMethodTypePayload,
1564
+ );
1565
+ } catch (err) {}
1566
+ }
1567
+ }
1568
+ }
1569
+ }
1570
+ });
1571
+ }
1572
+ }