@churchsoln/dbms 1.0.55 → 1.0.57

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.
@@ -59,28 +59,141 @@ const MEMBER_STATUS = {
59
59
  VISITOR: "Visitor",
60
60
  };
61
61
 
62
+ const NORMAL_BALANCE = {
63
+ DEBIT: "Debit",
64
+ CREDIT: "Credit",
65
+ };
66
+
67
+ const ACCOUNTING_ACCOUNT_TYPES = {
68
+ ASSET: { name: "Asset", normalBalance: NORMAL_BALANCE.DEBIT },
69
+ LIABILITY: { name: "Liability", normalBalance: NORMAL_BALANCE.CREDIT },
70
+ EQUITY: { name: "Equity", normalBalance: NORMAL_BALANCE.CREDIT },
71
+ INCOME: { name: "Income", normalBalance: NORMAL_BALANCE.CREDIT },
72
+ EXPENSE: { name: "Expense", normalBalance: NORMAL_BALANCE.DEBIT },
73
+ };
74
+
75
+ const CHART_OF_ACCOUNTS = [
76
+ { code: "1000", name: "Assets", accountingAccountType: "Asset", parentCode: null, isPostingAccount: false },
77
+ { code: "1010", name: "Checking Account", accountingAccountType: "Asset", parentCode: "1000", isPostingAccount: true },
78
+ { code: "1020", name: "Cash on Hand", accountingAccountType: "Asset", parentCode: "1000", isPostingAccount: true },
79
+ { code: "2000", name: "Liabilities", accountingAccountType: "Liability", parentCode: null, isPostingAccount: false },
80
+ { code: "3000", name: "Equity", accountingAccountType: "Equity", parentCode: null, isPostingAccount: false },
81
+ { code: "4000", name: "Income", accountingAccountType: "Income", parentCode: null, isPostingAccount: false },
82
+ { code: "4010", name: "Members Subscription Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
83
+ { code: "4020", name: "Capital Fund Donation Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
84
+ { code: "4030", name: "Offering Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
85
+ { code: "4040", name: "Wedding Fees Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
86
+ { code: "4050", name: "Self Denial Cover Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
87
+ { code: "4051", name: "Passion Week Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
88
+ { code: "4060", name: "Special Donation Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
89
+ { code: "5000", name: "Expenses", accountingAccountType: "Expense", parentCode: null, isPostingAccount: false },
90
+ { code: "5010", name: "Utilities Expense", accountingAccountType: "Expense", parentCode: "5000", isPostingAccount: true },
91
+ { code: "5020", name: "Mortgage Expense", accountingAccountType: "Expense", parentCode: "5000", isPostingAccount: true },
92
+ { code: "5030", name: "Office Supplies Expense", accountingAccountType: "Expense", parentCode: "5000", isPostingAccount: true },
93
+ ];
94
+
62
95
  const ACCOUNTS = {
63
- MEMBERS_SUBSCRIPTION: "Members Subscription",
64
- CAPITAL_FUND_DONATION: "Capital Fund Donation",
65
- MONTHLY_MORTGAGE_CONTRIBUTION: "Monthly Mortgage Contribution",
66
- OFFERTORY_PLATE: "Offertory - Plate (Loose Plate)",
67
- OFFERTORY_COVER: "Offertory - Cover",
68
- BIRTHDAY_COVER: "Birthday Cover",
69
- WEDDING_ANNIVERSARY_COVER: "Wedding Anniversary Cover",
70
- REMEMBERANCE_PRAYER_DONATION: "Rememberance Prayer Donation",
71
- SELF_DENIAL_COVER: "Self Denial Cover",
72
- SPECIAL_DONATION: "Special Donation",
73
- PASSION_WEEK: "Passion Week",
74
- WEDDING_FEES: "Wedding Fees",
75
- BAPTISM_FEES: "Baptism Fees",
76
- CATHOLICATE_DAY_FUND: "Catholicate Day Fund",
77
- ARTICLE_DONATIONS: "Article Donations",
78
- HARVEST_FESTIVAL: "Harvest Festival",
79
- SUNDAY_SCHOOL_COLLECTION: "Sunday School - Collection",
80
- MMVS_COLLECTION: "MMVS Collection",
81
- CHRISTMAS_DONATIONS: "Christmas Donations",
82
- CHARITY_FUNDS: "Charity Funds",
83
- }
96
+ MEMBERS_SUBSCRIPTION: {
97
+ accountHead: "Members Subscription",
98
+ accountDescription: "Members Subscription",
99
+ chartOfAccountCode: "4010",
100
+ },
101
+ CAPITAL_FUND_DONATION: {
102
+ accountHead: "Capital Fund Donation",
103
+ accountDescription: "Capital Fund Donation",
104
+ chartOfAccountCode: "4020",
105
+ },
106
+ MONTHLY_MORTGAGE_CONTRIBUTION: {
107
+ accountHead: "Monthly Mortgage Contribution",
108
+ accountDescription: "Monthly Mortgage Contribution",
109
+ chartOfAccountCode: "4020",
110
+ },
111
+ OFFERTORY_PLATE: {
112
+ accountHead: "Offertory - Plate (Loose Plate)",
113
+ accountDescription: "Offertory - Plate (Loose Plate)",
114
+ chartOfAccountCode: "4030",
115
+ },
116
+ OFFERTORY_COVER: {
117
+ accountHead: "Offertory - Cover",
118
+ accountDescription: "Offertory - Cover",
119
+ chartOfAccountCode: "4030",
120
+ },
121
+ BIRTHDAY_COVER: {
122
+ accountHead: "Birthday Cover",
123
+ accountDescription: "Birthday Offering",
124
+ chartOfAccountCode: "4030",
125
+ },
126
+ WEDDING_ANNIVERSARY_COVER: {
127
+ accountHead: "Wedding Anniversary Cover",
128
+ accountDescription: "Wedding Anniversary Offering",
129
+ chartOfAccountCode: "4030",
130
+ },
131
+ REMEMBERANCE_PRAYER_DONATION: {
132
+ accountHead: "Rememberance Prayer Donation",
133
+ accountDescription: "Rememberance Prayer Offering",
134
+ chartOfAccountCode: "4030",
135
+ },
136
+ SELF_DENIAL_COVER: {
137
+ accountHead: "Self Denial Cover",
138
+ accountDescription: "Self Denial Cover",
139
+ chartOfAccountCode: "4050",
140
+ },
141
+ SPECIAL_DONATION: {
142
+ accountHead: "Special Donation",
143
+ accountDescription: "Special Donation",
144
+ chartOfAccountCode: "4060",
145
+ },
146
+ PASSION_WEEK: {
147
+ accountHead: "Passion Week",
148
+ accountDescription: "Passion Week",
149
+ chartOfAccountCode: "4051",
150
+ },
151
+ WEDDING_FEES: {
152
+ accountHead: "Wedding Fees",
153
+ accountDescription: "Wedding Fees",
154
+ chartOfAccountCode: "4040",
155
+ },
156
+ BAPTISM_FEES: {
157
+ accountHead: "Baptism Fees",
158
+ accountDescription: "Baptism Fees",
159
+ chartOfAccountCode: "4040",
160
+ },
161
+ CATHOLICATE_DAY_FUND: {
162
+ accountHead: "Catholicate Day Fund",
163
+ accountDescription: "Catholicate Day Fund",
164
+ chartOfAccountCode: null,
165
+ },
166
+ ARTICLE_DONATIONS: {
167
+ accountHead: "Article Donations",
168
+ accountDescription: "Article Donations",
169
+ chartOfAccountCode: null,
170
+ },
171
+ HARVEST_FESTIVAL: {
172
+ accountHead: "Harvest Festival",
173
+ accountDescription: "Harvest Festival",
174
+ chartOfAccountCode: null,
175
+ },
176
+ SUNDAY_SCHOOL_COLLECTION: {
177
+ accountHead: "Sunday School - Collection",
178
+ accountDescription: "Sunday School - Collection",
179
+ chartOfAccountCode: null,
180
+ },
181
+ MMVS_COLLECTION: {
182
+ accountHead: "MMVS Collection",
183
+ accountDescription: "MMVS Collection",
184
+ chartOfAccountCode: null,
185
+ },
186
+ CHRISTMAS_DONATIONS: {
187
+ accountHead: "Christmas Donations",
188
+ accountDescription: "Christmas Donations",
189
+ chartOfAccountCode: null,
190
+ },
191
+ CHARITY_FUNDS: {
192
+ accountHead: "Charity Funds",
193
+ accountDescription: "Charity Funds",
194
+ chartOfAccountCode: null,
195
+ },
196
+ };
84
197
 
85
198
  const GENDER = {
86
199
  MALE: "Male",
@@ -114,5 +227,8 @@ module.exports = {
114
227
  GENDER,
115
228
  RELATIONSHIP,
116
229
  CODE_LENGTH,
117
- ACCOUNTS
230
+ ACCOUNTS,
231
+ NORMAL_BALANCE,
232
+ ACCOUNTING_ACCOUNT_TYPES,
233
+ CHART_OF_ACCOUNTS,
118
234
  };
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface.createTable(
5
+ "AccountingAccountTypes",
6
+ {
7
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
8
+ name: { type: DataTypes.STRING, allowNull: false, unique: true },
9
+ normalBalance: { type: DataTypes.STRING, allowNull: false },
10
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
11
+ createdAt: {
12
+ type: DataTypes.DATE,
13
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
14
+ },
15
+ updatedAt: {
16
+ type: DataTypes.DATE,
17
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
18
+ },
19
+ },
20
+ { timestamps: false }
21
+ );
22
+ },
23
+ down: async (queryInterface) => {
24
+ await queryInterface.dropTable("AccountingAccountTypes");
25
+ },
26
+ };
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .createTable(
6
+ "ChartOfAccounts",
7
+ {
8
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
9
+ code: { type: DataTypes.STRING, allowNull: false, unique: true },
10
+ name: { type: DataTypes.STRING, allowNull: false },
11
+ accountingAccountTypeId: { type: DataTypes.INTEGER, allowNull: false },
12
+ parentAccountId: { type: DataTypes.INTEGER, allowNull: true },
13
+ isPostingAccount: { type: DataTypes.BOOLEAN, defaultValue: true },
14
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
15
+ createdAt: {
16
+ type: DataTypes.DATE,
17
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
18
+ },
19
+ updatedAt: {
20
+ type: DataTypes.DATE,
21
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
22
+ },
23
+ },
24
+ { timestamps: false }
25
+ )
26
+ .then(() =>
27
+ queryInterface.addConstraint("ChartOfAccounts", {
28
+ fields: ["accountingAccountTypeId"],
29
+ type: "foreign key",
30
+ name: "ChartOfAccounts_AccountingAccountTypeId_Fkey",
31
+ references: {
32
+ table: "AccountingAccountTypes",
33
+ field: "id",
34
+ },
35
+ onDelete: "restrict",
36
+ onUpdate: "cascade",
37
+ })
38
+ )
39
+ .then(() =>
40
+ queryInterface.addConstraint("ChartOfAccounts", {
41
+ fields: ["parentAccountId"],
42
+ type: "foreign key",
43
+ name: "ChartOfAccounts_ParentAccountId_Fkey",
44
+ references: {
45
+ table: "ChartOfAccounts",
46
+ field: "id",
47
+ },
48
+ onDelete: "restrict",
49
+ onUpdate: "cascade",
50
+ })
51
+ );
52
+ },
53
+ down: async (queryInterface) => {
54
+ await queryInterface.dropTable("ChartOfAccounts");
55
+ },
56
+ };
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .addColumn("Accounts", "chartOfAccountId", {
6
+ type: DataTypes.INTEGER,
7
+ allowNull: true,
8
+ })
9
+ .then(() =>
10
+ queryInterface.addConstraint("Accounts", {
11
+ fields: ["chartOfAccountId"],
12
+ type: "foreign key",
13
+ name: "Accounts_ChartOfAccountId_Fkey",
14
+ references: {
15
+ table: "ChartOfAccounts",
16
+ field: "id",
17
+ },
18
+ onDelete: "restrict",
19
+ onUpdate: "cascade",
20
+ })
21
+ );
22
+ },
23
+ down: async (queryInterface) => {
24
+ await queryInterface.removeConstraint("Accounts", "Accounts_ChartOfAccountId_Fkey");
25
+ await queryInterface.removeColumn("Accounts", "chartOfAccountId");
26
+ },
27
+ };
@@ -0,0 +1,22 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const accountingAccountTypes = sequelize.define(
3
+ "AccountingAccountTypes",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ name: { type: DataTypes.STRING, allowNull: false, unique: true },
7
+ normalBalance: { type: DataTypes.STRING, allowNull: false },
8
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
9
+ createdAt: { type: DataTypes.DATE },
10
+ updatedAt: { type: DataTypes.DATE },
11
+ },
12
+ { freezeTableName: true, timestamps: false }
13
+ );
14
+ accountingAccountTypes.selectedFields = ["id", "name", "normalBalance", "status"];
15
+ accountingAccountTypes.associate = function (models) {
16
+ accountingAccountTypes.hasMany(models.chartOfAccounts, {
17
+ foreignKey: "accountingAccountTypeId",
18
+ as: "chartOfAccounts",
19
+ });
20
+ };
21
+ return accountingAccountTypes;
22
+ };
@@ -6,6 +6,7 @@ module.exports = (sequelize, DataTypes) => {
6
6
  accountHead: { type: DataTypes.STRING },
7
7
  accountDescription: { type: DataTypes.STRING },
8
8
  isLinkedWithQB: { type: DataTypes.BOOLEAN },
9
+ chartOfAccountId: { type: DataTypes.INTEGER, allowNull: true },
9
10
  status: { type: DataTypes.BOOLEAN, defaultValue: true },
10
11
  createdAt: { type: DataTypes.DATE },
11
12
  updatedAt: { type: DataTypes.DATE },
@@ -13,6 +14,10 @@ module.exports = (sequelize, DataTypes) => {
13
14
  { freezeTableName: true, timestamps: false }
14
15
  );
15
16
  accounts.associate = function (models) {
17
+ accounts.belongsTo(models.chartOfAccounts, {
18
+ foreignKey: "chartOfAccountId",
19
+ as: "chartOfAccount",
20
+ });
16
21
  accounts.hasMany(models.pledgeAccount, {
17
22
  foreignKey: "accountId",
18
23
  as: "pledgeAccount",
@@ -22,6 +27,12 @@ module.exports = (sequelize, DataTypes) => {
22
27
  as: "contributionTransactionLine",
23
28
  });
24
29
  }
25
- accounts.selectedFields = ["id", "accountHead", "accountDescription", "isLinkedWithQB"];
30
+ accounts.selectedFields = [
31
+ "id",
32
+ "accountHead",
33
+ "accountDescription",
34
+ "isLinkedWithQB",
35
+ "chartOfAccountId",
36
+ ];
26
37
  return accounts;
27
38
  };
@@ -0,0 +1,45 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const chartOfAccounts = sequelize.define(
3
+ "ChartOfAccounts",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ code: { type: DataTypes.STRING, allowNull: false, unique: true },
7
+ name: { type: DataTypes.STRING, allowNull: false },
8
+ accountingAccountTypeId: { type: DataTypes.INTEGER, allowNull: false },
9
+ parentAccountId: { type: DataTypes.INTEGER, allowNull: true },
10
+ isPostingAccount: { type: DataTypes.BOOLEAN, defaultValue: true },
11
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
12
+ createdAt: { type: DataTypes.DATE },
13
+ updatedAt: { type: DataTypes.DATE },
14
+ },
15
+ { freezeTableName: true, timestamps: false }
16
+ );
17
+ chartOfAccounts.associate = function (models) {
18
+ chartOfAccounts.belongsTo(models.accountingAccountTypes, {
19
+ foreignKey: "accountingAccountTypeId",
20
+ as: "accountingAccountType",
21
+ });
22
+ chartOfAccounts.belongsTo(models.chartOfAccounts, {
23
+ foreignKey: "parentAccountId",
24
+ as: "parentAccount",
25
+ });
26
+ chartOfAccounts.hasMany(models.chartOfAccounts, {
27
+ foreignKey: "parentAccountId",
28
+ as: "childAccounts",
29
+ });
30
+ chartOfAccounts.hasMany(models.accounts, {
31
+ foreignKey: "chartOfAccountId",
32
+ as: "accounts",
33
+ });
34
+ };
35
+ chartOfAccounts.selectedFields = [
36
+ "id",
37
+ "code",
38
+ "name",
39
+ "accountingAccountTypeId",
40
+ "parentAccountId",
41
+ "isPostingAccount",
42
+ "status",
43
+ ];
44
+ return chartOfAccounts;
45
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@churchsoln/dbms",
3
- "version": "1.0.55",
3
+ "version": "1.0.57",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "prestart": "node db-setup.js",
@@ -3,10 +3,10 @@
3
3
  const { ACCOUNTS } = require("../../constants");
4
4
 
5
5
  module.exports = {
6
- up: async (queryInterface, Sequelize) => {
6
+ up: async (queryInterface) => {
7
7
  const accounts = Object.values(ACCOUNTS).map((account) => ({
8
- accountHead: account,
9
- accountDescription: account,
8
+ accountHead: account.accountHead,
9
+ accountDescription: account.accountDescription,
10
10
  isLinkedWithQB: false,
11
11
  status: true,
12
12
  }));
@@ -23,10 +23,10 @@ module.exports = {
23
23
  if (existingAccount) {
24
24
  await queryInterface.bulkUpdate(
25
25
  "Accounts",
26
- {
27
- status: account.status,
28
- isLinkedWithQB: account.isLinkedWithQB,
29
- accountDescription: account.accountDescription
26
+ {
27
+ status: account.status,
28
+ isLinkedWithQB: account.isLinkedWithQB,
29
+ accountDescription: account.accountDescription,
30
30
  },
31
31
  { accountHead: account.accountHead }
32
32
  );
@@ -36,5 +36,5 @@ module.exports = {
36
36
  }
37
37
  },
38
38
 
39
- down: async (queryInterface, Sequelize) => {},
39
+ down: async () => {},
40
40
  };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ const { ACCOUNTING_ACCOUNT_TYPES } = require("../../constants");
4
+
5
+ module.exports = {
6
+ up: async (queryInterface) => {
7
+ const accountTypes = Object.values(ACCOUNTING_ACCOUNT_TYPES).map((accountType) => ({
8
+ name: accountType.name,
9
+ normalBalance: accountType.normalBalance,
10
+ status: true,
11
+ }));
12
+
13
+ for (const accountType of accountTypes) {
14
+ const existingAccountType = await queryInterface.rawSelect(
15
+ "AccountingAccountTypes",
16
+ {
17
+ where: { name: accountType.name },
18
+ },
19
+ ["id"]
20
+ );
21
+
22
+ if (existingAccountType) {
23
+ await queryInterface.bulkUpdate(
24
+ "AccountingAccountTypes",
25
+ {
26
+ normalBalance: accountType.normalBalance,
27
+ status: accountType.status,
28
+ },
29
+ { name: accountType.name }
30
+ );
31
+ } else {
32
+ await queryInterface.bulkInsert("AccountingAccountTypes", [accountType], {});
33
+ }
34
+ }
35
+ },
36
+
37
+ down: async () => {},
38
+ };
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+
3
+ const { CHART_OF_ACCOUNTS } = require("../../constants");
4
+
5
+ module.exports = {
6
+ up: async (queryInterface) => {
7
+ const codeToId = {};
8
+
9
+ const getAccountTypeId = async (accountTypeName) =>
10
+ queryInterface.rawSelect(
11
+ "AccountingAccountTypes",
12
+ { where: { name: accountTypeName } },
13
+ ["id"]
14
+ );
15
+
16
+ for (const account of CHART_OF_ACCOUNTS) {
17
+ const accountingAccountTypeId = await getAccountTypeId(account.accountingAccountType);
18
+ if (!accountingAccountTypeId) continue;
19
+
20
+ const parentAccountId = account.parentCode ? codeToId[account.parentCode] || null : null;
21
+
22
+ const record = {
23
+ code: account.code,
24
+ name: account.name,
25
+ accountingAccountTypeId,
26
+ parentAccountId,
27
+ isPostingAccount: account.isPostingAccount,
28
+ status: true,
29
+ };
30
+
31
+ const existingId = await queryInterface.rawSelect(
32
+ "ChartOfAccounts",
33
+ { where: { code: account.code } },
34
+ ["id"]
35
+ );
36
+
37
+ if (existingId) {
38
+ await queryInterface.bulkUpdate("ChartOfAccounts", record, { code: account.code });
39
+ codeToId[account.code] = existingId;
40
+ } else {
41
+ await queryInterface.bulkInsert("ChartOfAccounts", [record], {});
42
+ codeToId[account.code] = await queryInterface.rawSelect(
43
+ "ChartOfAccounts",
44
+ { where: { code: account.code } },
45
+ ["id"]
46
+ );
47
+ }
48
+ }
49
+ },
50
+
51
+ down: async () => {},
52
+ };
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+
3
+ const { ACCOUNTS } = require("../../constants");
4
+
5
+ module.exports = {
6
+ up: async (queryInterface) => {
7
+ for (const account of Object.values(ACCOUNTS)) {
8
+ if (!account.chartOfAccountCode) continue;
9
+
10
+ const chartOfAccountId = await queryInterface.rawSelect(
11
+ "ChartOfAccounts",
12
+ { where: { code: account.chartOfAccountCode } },
13
+ ["id"]
14
+ );
15
+ if (!chartOfAccountId) continue;
16
+
17
+ const existingAccountId = await queryInterface.rawSelect(
18
+ "Accounts",
19
+ { where: { accountHead: account.accountHead } },
20
+ ["id"]
21
+ );
22
+
23
+ if (existingAccountId) {
24
+ await queryInterface.bulkUpdate(
25
+ "Accounts",
26
+ {
27
+ accountDescription: account.accountDescription,
28
+ chartOfAccountId,
29
+ },
30
+ { accountHead: account.accountHead }
31
+ );
32
+ }
33
+ }
34
+ },
35
+
36
+ down: async () => {},
37
+ };