@churchsoln/dbms 1.0.56 → 1.0.58
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.
- package/constants/index.js +135 -21
- package/migrations/church/20260707170639-create-chartOfAccounts.js +56 -0
- package/migrations/church/20260707170640-update-accounts-add-chartOfAccountId.js +27 -0
- package/migrations/church/20260712170641-create-journalEntry.js +42 -0
- package/migrations/church/20260712170642-create-journalEntryLine.js +55 -0
- package/models/church/accountingAccountTypes.js +6 -0
- package/models/church/accounts.js +12 -1
- package/models/church/chartOfAccounts.js +49 -0
- package/models/church/contributionTransaction.js +4 -0
- package/models/church/journalEntry.js +37 -0
- package/models/church/journalEntryLine.js +37 -0
- package/package.json +1 -1
- package/seeders/church/20241005131420-accounts.js +8 -8
- package/seeders/church/20260707170639-chartOfAccounts.js +52 -0
- package/seeders/church/20260707170640-update-accounts-chartOfAccountId.js +37 -0
package/constants/index.js
CHANGED
|
@@ -64,6 +64,17 @@ const NORMAL_BALANCE = {
|
|
|
64
64
|
CREDIT: "Credit",
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
const JOURNAL_ENTRY_SOURCE_TYPE = {
|
|
68
|
+
CONTRIBUTION: "Contribution",
|
|
69
|
+
MANUAL: "Manual",
|
|
70
|
+
BILL: "Bill",
|
|
71
|
+
INVOICE: "Invoice",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const JOURNAL_ENTRY_STATUS = {
|
|
75
|
+
POSTED: "posted",
|
|
76
|
+
};
|
|
77
|
+
|
|
67
78
|
const ACCOUNTING_ACCOUNT_TYPES = {
|
|
68
79
|
ASSET: { name: "Asset", normalBalance: NORMAL_BALANCE.DEBIT },
|
|
69
80
|
LIABILITY: { name: "Liability", normalBalance: NORMAL_BALANCE.CREDIT },
|
|
@@ -72,28 +83,128 @@ const ACCOUNTING_ACCOUNT_TYPES = {
|
|
|
72
83
|
EXPENSE: { name: "Expense", normalBalance: NORMAL_BALANCE.DEBIT },
|
|
73
84
|
};
|
|
74
85
|
|
|
86
|
+
const CHART_OF_ACCOUNTS = [
|
|
87
|
+
{ code: "1000", name: "Assets", accountingAccountType: "Asset", parentCode: null, isPostingAccount: false },
|
|
88
|
+
{ code: "1010", name: "Checking Account", accountingAccountType: "Asset", parentCode: "1000", isPostingAccount: true },
|
|
89
|
+
{ code: "1020", name: "Cash on Hand", accountingAccountType: "Asset", parentCode: "1000", isPostingAccount: true },
|
|
90
|
+
{ code: "2000", name: "Liabilities", accountingAccountType: "Liability", parentCode: null, isPostingAccount: false },
|
|
91
|
+
{ code: "3000", name: "Equity", accountingAccountType: "Equity", parentCode: null, isPostingAccount: false },
|
|
92
|
+
{ code: "4000", name: "Income", accountingAccountType: "Income", parentCode: null, isPostingAccount: false },
|
|
93
|
+
{ code: "4010", name: "Members Subscription Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
|
|
94
|
+
{ code: "4020", name: "Capital Fund Donation Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
|
|
95
|
+
{ code: "4030", name: "Offering Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
|
|
96
|
+
{ code: "4040", name: "Wedding Fees Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
|
|
97
|
+
{ code: "4050", name: "Self Denial Cover Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
|
|
98
|
+
{ code: "4051", name: "Passion Week Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
|
|
99
|
+
{ code: "4060", name: "Special Donation Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
|
|
100
|
+
{ code: "5000", name: "Expenses", accountingAccountType: "Expense", parentCode: null, isPostingAccount: false },
|
|
101
|
+
{ code: "5010", name: "Utilities Expense", accountingAccountType: "Expense", parentCode: "5000", isPostingAccount: true },
|
|
102
|
+
{ code: "5020", name: "Mortgage Expense", accountingAccountType: "Expense", parentCode: "5000", isPostingAccount: true },
|
|
103
|
+
{ code: "5030", name: "Office Supplies Expense", accountingAccountType: "Expense", parentCode: "5000", isPostingAccount: true },
|
|
104
|
+
];
|
|
105
|
+
|
|
75
106
|
const ACCOUNTS = {
|
|
76
|
-
MEMBERS_SUBSCRIPTION:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
MEMBERS_SUBSCRIPTION: {
|
|
108
|
+
accountHead: "Members Subscription",
|
|
109
|
+
accountDescription: "Members Subscription",
|
|
110
|
+
chartOfAccountCode: "4010",
|
|
111
|
+
},
|
|
112
|
+
CAPITAL_FUND_DONATION: {
|
|
113
|
+
accountHead: "Capital Fund Donation",
|
|
114
|
+
accountDescription: "Capital Fund Donation",
|
|
115
|
+
chartOfAccountCode: "4020",
|
|
116
|
+
},
|
|
117
|
+
MONTHLY_MORTGAGE_CONTRIBUTION: {
|
|
118
|
+
accountHead: "Monthly Mortgage Contribution",
|
|
119
|
+
accountDescription: "Monthly Mortgage Contribution",
|
|
120
|
+
chartOfAccountCode: "4020",
|
|
121
|
+
},
|
|
122
|
+
OFFERTORY_PLATE: {
|
|
123
|
+
accountHead: "Offertory - Plate (Loose Plate)",
|
|
124
|
+
accountDescription: "Offertory - Plate (Loose Plate)",
|
|
125
|
+
chartOfAccountCode: "4030",
|
|
126
|
+
},
|
|
127
|
+
OFFERTORY_COVER: {
|
|
128
|
+
accountHead: "Offertory - Cover",
|
|
129
|
+
accountDescription: "Offertory - Cover",
|
|
130
|
+
chartOfAccountCode: "4030",
|
|
131
|
+
},
|
|
132
|
+
BIRTHDAY_COVER: {
|
|
133
|
+
accountHead: "Birthday Cover",
|
|
134
|
+
accountDescription: "Birthday Offering",
|
|
135
|
+
chartOfAccountCode: "4030",
|
|
136
|
+
},
|
|
137
|
+
WEDDING_ANNIVERSARY_COVER: {
|
|
138
|
+
accountHead: "Wedding Anniversary Cover",
|
|
139
|
+
accountDescription: "Wedding Anniversary Offering",
|
|
140
|
+
chartOfAccountCode: "4030",
|
|
141
|
+
},
|
|
142
|
+
REMEMBERANCE_PRAYER_DONATION: {
|
|
143
|
+
accountHead: "Rememberance Prayer Donation",
|
|
144
|
+
accountDescription: "Rememberance Prayer Offering",
|
|
145
|
+
chartOfAccountCode: "4030",
|
|
146
|
+
},
|
|
147
|
+
SELF_DENIAL_COVER: {
|
|
148
|
+
accountHead: "Self Denial Cover",
|
|
149
|
+
accountDescription: "Self Denial Cover",
|
|
150
|
+
chartOfAccountCode: "4050",
|
|
151
|
+
},
|
|
152
|
+
SPECIAL_DONATION: {
|
|
153
|
+
accountHead: "Special Donation",
|
|
154
|
+
accountDescription: "Special Donation",
|
|
155
|
+
chartOfAccountCode: "4060",
|
|
156
|
+
},
|
|
157
|
+
PASSION_WEEK: {
|
|
158
|
+
accountHead: "Passion Week",
|
|
159
|
+
accountDescription: "Passion Week",
|
|
160
|
+
chartOfAccountCode: "4051",
|
|
161
|
+
},
|
|
162
|
+
WEDDING_FEES: {
|
|
163
|
+
accountHead: "Wedding Fees",
|
|
164
|
+
accountDescription: "Wedding Fees",
|
|
165
|
+
chartOfAccountCode: "4040",
|
|
166
|
+
},
|
|
167
|
+
BAPTISM_FEES: {
|
|
168
|
+
accountHead: "Baptism Fees",
|
|
169
|
+
accountDescription: "Baptism Fees",
|
|
170
|
+
chartOfAccountCode: "4040",
|
|
171
|
+
},
|
|
172
|
+
CATHOLICATE_DAY_FUND: {
|
|
173
|
+
accountHead: "Catholicate Day Fund",
|
|
174
|
+
accountDescription: "Catholicate Day Fund",
|
|
175
|
+
chartOfAccountCode: null,
|
|
176
|
+
},
|
|
177
|
+
ARTICLE_DONATIONS: {
|
|
178
|
+
accountHead: "Article Donations",
|
|
179
|
+
accountDescription: "Article Donations",
|
|
180
|
+
chartOfAccountCode: null,
|
|
181
|
+
},
|
|
182
|
+
HARVEST_FESTIVAL: {
|
|
183
|
+
accountHead: "Harvest Festival",
|
|
184
|
+
accountDescription: "Harvest Festival",
|
|
185
|
+
chartOfAccountCode: null,
|
|
186
|
+
},
|
|
187
|
+
SUNDAY_SCHOOL_COLLECTION: {
|
|
188
|
+
accountHead: "Sunday School - Collection",
|
|
189
|
+
accountDescription: "Sunday School - Collection",
|
|
190
|
+
chartOfAccountCode: null,
|
|
191
|
+
},
|
|
192
|
+
MMVS_COLLECTION: {
|
|
193
|
+
accountHead: "MMVS Collection",
|
|
194
|
+
accountDescription: "MMVS Collection",
|
|
195
|
+
chartOfAccountCode: null,
|
|
196
|
+
},
|
|
197
|
+
CHRISTMAS_DONATIONS: {
|
|
198
|
+
accountHead: "Christmas Donations",
|
|
199
|
+
accountDescription: "Christmas Donations",
|
|
200
|
+
chartOfAccountCode: null,
|
|
201
|
+
},
|
|
202
|
+
CHARITY_FUNDS: {
|
|
203
|
+
accountHead: "Charity Funds",
|
|
204
|
+
accountDescription: "Charity Funds",
|
|
205
|
+
chartOfAccountCode: null,
|
|
206
|
+
},
|
|
207
|
+
};
|
|
97
208
|
|
|
98
209
|
const GENDER = {
|
|
99
210
|
MALE: "Male",
|
|
@@ -130,4 +241,7 @@ module.exports = {
|
|
|
130
241
|
ACCOUNTS,
|
|
131
242
|
NORMAL_BALANCE,
|
|
132
243
|
ACCOUNTING_ACCOUNT_TYPES,
|
|
244
|
+
CHART_OF_ACCOUNTS,
|
|
245
|
+
JOURNAL_ENTRY_SOURCE_TYPE,
|
|
246
|
+
JOURNAL_ENTRY_STATUS,
|
|
133
247
|
};
|
|
@@ -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,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
module.exports = {
|
|
3
|
+
up: async (queryInterface, DataTypes) => {
|
|
4
|
+
await queryInterface
|
|
5
|
+
.createTable(
|
|
6
|
+
"JournalEntry",
|
|
7
|
+
{
|
|
8
|
+
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
|
9
|
+
entryDate: { type: DataTypes.DATEONLY, allowNull: false },
|
|
10
|
+
sourceType: { type: DataTypes.STRING, allowNull: true },
|
|
11
|
+
contributionTransactionId: { type: DataTypes.INTEGER, allowNull: true },
|
|
12
|
+
description: { type: DataTypes.TEXT, allowNull: true },
|
|
13
|
+
status: { type: DataTypes.STRING, defaultValue: "posted" },
|
|
14
|
+
createdAt: {
|
|
15
|
+
type: DataTypes.DATE,
|
|
16
|
+
defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
|
|
17
|
+
},
|
|
18
|
+
updatedAt: {
|
|
19
|
+
type: DataTypes.DATE,
|
|
20
|
+
defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{ timestamps: false }
|
|
24
|
+
)
|
|
25
|
+
.then(() =>
|
|
26
|
+
queryInterface.addConstraint("JournalEntry", {
|
|
27
|
+
fields: ["contributionTransactionId"],
|
|
28
|
+
type: "foreign key",
|
|
29
|
+
name: "JournalEntry_ContributionTransactionId_Fkey",
|
|
30
|
+
references: {
|
|
31
|
+
table: "ContributionTransaction",
|
|
32
|
+
field: "id",
|
|
33
|
+
},
|
|
34
|
+
onDelete: "restrict",
|
|
35
|
+
onUpdate: "cascade",
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
down: async (queryInterface) => {
|
|
40
|
+
await queryInterface.dropTable("JournalEntry");
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
module.exports = {
|
|
3
|
+
up: async (queryInterface, DataTypes) => {
|
|
4
|
+
await queryInterface
|
|
5
|
+
.createTable(
|
|
6
|
+
"JournalEntryLine",
|
|
7
|
+
{
|
|
8
|
+
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
|
9
|
+
journalEntryId: { type: DataTypes.INTEGER, allowNull: false },
|
|
10
|
+
chartOfAccountId: { type: DataTypes.INTEGER, allowNull: false },
|
|
11
|
+
debit: { type: DataTypes.DECIMAL(10, 2), defaultValue: 0 },
|
|
12
|
+
credit: { type: DataTypes.DECIMAL(10, 2), defaultValue: 0 },
|
|
13
|
+
memo: { type: DataTypes.TEXT, allowNull: true },
|
|
14
|
+
createdAt: {
|
|
15
|
+
type: DataTypes.DATE,
|
|
16
|
+
defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
|
|
17
|
+
},
|
|
18
|
+
updatedAt: {
|
|
19
|
+
type: DataTypes.DATE,
|
|
20
|
+
defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{ timestamps: false }
|
|
24
|
+
)
|
|
25
|
+
.then(() =>
|
|
26
|
+
queryInterface.addConstraint("JournalEntryLine", {
|
|
27
|
+
fields: ["journalEntryId"],
|
|
28
|
+
type: "foreign key",
|
|
29
|
+
name: "JournalEntryLine_JournalEntryId_Fkey",
|
|
30
|
+
references: {
|
|
31
|
+
table: "JournalEntry",
|
|
32
|
+
field: "id",
|
|
33
|
+
},
|
|
34
|
+
onDelete: "cascade",
|
|
35
|
+
onUpdate: "cascade",
|
|
36
|
+
})
|
|
37
|
+
)
|
|
38
|
+
.then(() =>
|
|
39
|
+
queryInterface.addConstraint("JournalEntryLine", {
|
|
40
|
+
fields: ["chartOfAccountId"],
|
|
41
|
+
type: "foreign key",
|
|
42
|
+
name: "JournalEntryLine_ChartOfAccountId_Fkey",
|
|
43
|
+
references: {
|
|
44
|
+
table: "ChartOfAccounts",
|
|
45
|
+
field: "id",
|
|
46
|
+
},
|
|
47
|
+
onDelete: "restrict",
|
|
48
|
+
onUpdate: "cascade",
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
down: async (queryInterface) => {
|
|
53
|
+
await queryInterface.dropTable("JournalEntryLine");
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -12,5 +12,11 @@ module.exports = (sequelize, DataTypes) => {
|
|
|
12
12
|
{ freezeTableName: true, timestamps: false }
|
|
13
13
|
);
|
|
14
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
|
+
};
|
|
15
21
|
return accountingAccountTypes;
|
|
16
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 = [
|
|
30
|
+
accounts.selectedFields = [
|
|
31
|
+
"id",
|
|
32
|
+
"accountHead",
|
|
33
|
+
"accountDescription",
|
|
34
|
+
"isLinkedWithQB",
|
|
35
|
+
"chartOfAccountId",
|
|
36
|
+
];
|
|
26
37
|
return accounts;
|
|
27
38
|
};
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
chartOfAccounts.hasMany(models.journalEntryLine, {
|
|
35
|
+
foreignKey: "chartOfAccountId",
|
|
36
|
+
as: "journalEntryLines",
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
chartOfAccounts.selectedFields = [
|
|
40
|
+
"id",
|
|
41
|
+
"code",
|
|
42
|
+
"name",
|
|
43
|
+
"accountingAccountTypeId",
|
|
44
|
+
"parentAccountId",
|
|
45
|
+
"isPostingAccount",
|
|
46
|
+
"status",
|
|
47
|
+
];
|
|
48
|
+
return chartOfAccounts;
|
|
49
|
+
};
|
|
@@ -71,6 +71,10 @@ module.exports = (sequelize, DataTypes) => {
|
|
|
71
71
|
foreignKey: "contributionTransactionId",
|
|
72
72
|
as: "contributionTransactionLine",
|
|
73
73
|
});
|
|
74
|
+
contributionTransaction.hasMany(models.journalEntry, {
|
|
75
|
+
foreignKey: "contributionTransactionId",
|
|
76
|
+
as: "journalEntries",
|
|
77
|
+
});
|
|
74
78
|
}
|
|
75
79
|
contributionTransaction.selectedFields = [
|
|
76
80
|
"id",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module.exports = (sequelize, DataTypes) => {
|
|
2
|
+
const journalEntry = sequelize.define(
|
|
3
|
+
"JournalEntry",
|
|
4
|
+
{
|
|
5
|
+
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
|
6
|
+
entryDate: { type: DataTypes.DATEONLY, allowNull: false },
|
|
7
|
+
sourceType: { type: DataTypes.STRING, allowNull: true },
|
|
8
|
+
contributionTransactionId: { type: DataTypes.INTEGER, allowNull: true },
|
|
9
|
+
description: { type: DataTypes.TEXT, allowNull: true },
|
|
10
|
+
status: { type: DataTypes.STRING, defaultValue: "posted" },
|
|
11
|
+
createdAt: { type: DataTypes.DATE },
|
|
12
|
+
updatedAt: { type: DataTypes.DATE },
|
|
13
|
+
},
|
|
14
|
+
{ freezeTableName: true, timestamps: false }
|
|
15
|
+
);
|
|
16
|
+
journalEntry.associate = function (models) {
|
|
17
|
+
journalEntry.belongsTo(models.contributionTransaction, {
|
|
18
|
+
foreignKey: "contributionTransactionId",
|
|
19
|
+
as: "contributionTransaction",
|
|
20
|
+
});
|
|
21
|
+
journalEntry.hasMany(models.journalEntryLine, {
|
|
22
|
+
foreignKey: "journalEntryId",
|
|
23
|
+
as: "journalEntryLines",
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
journalEntry.selectedFields = [
|
|
27
|
+
"id",
|
|
28
|
+
"entryDate",
|
|
29
|
+
"sourceType",
|
|
30
|
+
"contributionTransactionId",
|
|
31
|
+
"description",
|
|
32
|
+
"status",
|
|
33
|
+
"createdAt",
|
|
34
|
+
"updatedAt",
|
|
35
|
+
];
|
|
36
|
+
return journalEntry;
|
|
37
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module.exports = (sequelize, DataTypes) => {
|
|
2
|
+
const journalEntryLine = sequelize.define(
|
|
3
|
+
"JournalEntryLine",
|
|
4
|
+
{
|
|
5
|
+
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
|
6
|
+
journalEntryId: { type: DataTypes.INTEGER, allowNull: false },
|
|
7
|
+
chartOfAccountId: { type: DataTypes.INTEGER, allowNull: false },
|
|
8
|
+
debit: { type: DataTypes.DECIMAL(10, 2), defaultValue: 0 },
|
|
9
|
+
credit: { type: DataTypes.DECIMAL(10, 2), defaultValue: 0 },
|
|
10
|
+
memo: { type: DataTypes.TEXT, allowNull: true },
|
|
11
|
+
createdAt: { type: DataTypes.DATE },
|
|
12
|
+
updatedAt: { type: DataTypes.DATE },
|
|
13
|
+
},
|
|
14
|
+
{ freezeTableName: true, timestamps: false }
|
|
15
|
+
);
|
|
16
|
+
journalEntryLine.associate = function (models) {
|
|
17
|
+
journalEntryLine.belongsTo(models.journalEntry, {
|
|
18
|
+
foreignKey: "journalEntryId",
|
|
19
|
+
as: "journalEntry",
|
|
20
|
+
});
|
|
21
|
+
journalEntryLine.belongsTo(models.chartOfAccounts, {
|
|
22
|
+
foreignKey: "chartOfAccountId",
|
|
23
|
+
as: "chartOfAccount",
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
journalEntryLine.selectedFields = [
|
|
27
|
+
"id",
|
|
28
|
+
"journalEntryId",
|
|
29
|
+
"chartOfAccountId",
|
|
30
|
+
"debit",
|
|
31
|
+
"credit",
|
|
32
|
+
"memo",
|
|
33
|
+
"createdAt",
|
|
34
|
+
"updatedAt",
|
|
35
|
+
];
|
|
36
|
+
return journalEntryLine;
|
|
37
|
+
};
|
package/package.json
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
const { ACCOUNTS } = require("../../constants");
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
|
-
up: async (queryInterface
|
|
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 (
|
|
39
|
+
down: async () => {},
|
|
40
40
|
};
|
|
@@ -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
|
+
};
|