@churchsoln/dbms 1.0.62 → 1.0.64

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.
@@ -84,6 +84,39 @@ const JOURNAL_ENTRY_STATUS = {
84
84
  DRAFT: "draft",
85
85
  };
86
86
 
87
+ const BILL_STATUS = {
88
+ DRAFT: "Draft",
89
+ UNPAID: "Unpaid",
90
+ PARTIALLY_PAID: "PartiallyPaid",
91
+ PAID: "Paid",
92
+ };
93
+
94
+ const BILL_TAX_MODE = {
95
+ EXCLUSIVE: "exclusive",
96
+ INCLUSIVE: "inclusive",
97
+ };
98
+
99
+ const BILL_PAYMENT_METHOD = {
100
+ CASH: "Cash",
101
+ CHECK: "Check",
102
+ BANK_TRANSFER: "BankTransfer",
103
+ CARD: "Card",
104
+ OTHER: "Other",
105
+ };
106
+
107
+ const ACCOUNTS_PAYABLE_CODE = "2010";
108
+
109
+ // Invoices share the bill lifecycle (Draft -> Unpaid -> PartiallyPaid -> Paid; Overdue is derived).
110
+ const INVOICE_STATUS = {
111
+ DRAFT: "Draft",
112
+ UNPAID: "Unpaid",
113
+ PARTIALLY_PAID: "PartiallyPaid",
114
+ PAID: "Paid",
115
+ };
116
+
117
+ // An asset account, but not cash: excluded wherever "posting asset" means a bank/cash account.
118
+ const ACCOUNTS_RECEIVABLE_CODE = "1100";
119
+
87
120
  const ACCOUNTING_ACCOUNT_TYPES = {
88
121
  ASSET: { name: "Asset", normalBalance: NORMAL_BALANCE.DEBIT },
89
122
  LIABILITY: { name: "Liability", normalBalance: NORMAL_BALANCE.CREDIT },
@@ -96,7 +129,9 @@ const CHART_OF_ACCOUNTS = [
96
129
  { code: "1000", name: "Assets", accountingAccountType: "Asset", parentCode: null, isPostingAccount: false },
97
130
  { code: "1010", name: "Checking Account", accountingAccountType: "Asset", parentCode: "1000", isPostingAccount: true },
98
131
  { code: "1020", name: "Cash on Hand", accountingAccountType: "Asset", parentCode: "1000", isPostingAccount: true },
132
+ { code: "1100", name: "Accounts Receivable", accountingAccountType: "Asset", parentCode: "1000", isPostingAccount: true },
99
133
  { code: "2000", name: "Liabilities", accountingAccountType: "Liability", parentCode: null, isPostingAccount: false },
134
+ { code: "2010", name: "Accounts Payable", accountingAccountType: "Liability", parentCode: "2000", isPostingAccount: true },
100
135
  { code: "3000", name: "Equity", accountingAccountType: "Equity", parentCode: null, isPostingAccount: false },
101
136
  { code: "4000", name: "Income", accountingAccountType: "Income", parentCode: null, isPostingAccount: false },
102
137
  { code: "4010", name: "Members Subscription Income", accountingAccountType: "Income", parentCode: "4000", isPostingAccount: true },
@@ -253,4 +288,10 @@ module.exports = {
253
288
  CHART_OF_ACCOUNTS,
254
289
  JOURNAL_ENTRY_SOURCE_TYPE,
255
290
  JOURNAL_ENTRY_STATUS,
291
+ BILL_STATUS,
292
+ BILL_TAX_MODE,
293
+ BILL_PAYMENT_METHOD,
294
+ ACCOUNTS_PAYABLE_CODE,
295
+ INVOICE_STATUS,
296
+ ACCOUNTS_RECEIVABLE_CODE,
256
297
  };
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .createTable(
6
+ "Vendor",
7
+ {
8
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
9
+ name: { type: DataTypes.STRING, allowNull: false, unique: true },
10
+ email: { type: DataTypes.STRING, allowNull: true },
11
+ phone: { type: DataTypes.STRING, allowNull: true },
12
+ addressLine1: { type: DataTypes.STRING, allowNull: true },
13
+ addressLine2: { type: DataTypes.STRING, allowNull: true },
14
+ defaultTerms: { type: DataTypes.STRING, allowNull: true },
15
+ defaultChartOfAccountId: { type: DataTypes.INTEGER, allowNull: true },
16
+ notes: { type: DataTypes.TEXT, allowNull: true },
17
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
18
+ createdAt: {
19
+ type: DataTypes.DATE,
20
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
21
+ },
22
+ updatedAt: {
23
+ type: DataTypes.DATE,
24
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
25
+ },
26
+ },
27
+ { timestamps: false }
28
+ )
29
+ .then(() =>
30
+ queryInterface.addConstraint("Vendor", {
31
+ fields: ["defaultChartOfAccountId"],
32
+ type: "foreign key",
33
+ name: "Vendor_DefaultChartOfAccountId_Fkey",
34
+ references: {
35
+ table: "ChartOfAccounts",
36
+ field: "id",
37
+ },
38
+ onDelete: "set null",
39
+ onUpdate: "cascade",
40
+ })
41
+ );
42
+ },
43
+ down: async (queryInterface) => {
44
+ await queryInterface.dropTable("Vendor");
45
+ },
46
+ };
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .createTable(
6
+ "Bill",
7
+ {
8
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
9
+ billNo: { type: DataTypes.STRING, allowNull: false, unique: true },
10
+ vendorId: { type: DataTypes.INTEGER, allowNull: false },
11
+ reference: { type: DataTypes.STRING, allowNull: true },
12
+ description: { type: DataTypes.TEXT, allowNull: true },
13
+ billDate: { type: DataTypes.DATEONLY, allowNull: true },
14
+ dueDate: { type: DataTypes.DATEONLY, allowNull: true },
15
+ terms: { type: DataTypes.STRING, allowNull: true },
16
+ currency: { type: DataTypes.STRING, defaultValue: "USD" },
17
+ memo: { type: DataTypes.TEXT, allowNull: true },
18
+ notes: { type: DataTypes.TEXT, allowNull: true },
19
+ status: { type: DataTypes.STRING, defaultValue: "Draft" },
20
+ taxMode: { type: DataTypes.STRING, defaultValue: "exclusive" },
21
+ subtotal: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
22
+ tax: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
23
+ discount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
24
+ amount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
25
+ amountPaid: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
26
+ amountDue: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
27
+ paidDate: { type: DataTypes.DATEONLY, allowNull: true },
28
+ journalEntryId: { type: DataTypes.INTEGER, allowNull: true },
29
+ createdBy: { type: DataTypes.INTEGER, allowNull: true },
30
+ createdAt: {
31
+ type: DataTypes.DATE,
32
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
33
+ },
34
+ updatedAt: {
35
+ type: DataTypes.DATE,
36
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
37
+ },
38
+ },
39
+ { timestamps: false }
40
+ )
41
+ .then(() =>
42
+ queryInterface.addConstraint("Bill", {
43
+ fields: ["vendorId"],
44
+ type: "foreign key",
45
+ name: "Bill_VendorId_Fkey",
46
+ references: {
47
+ table: "Vendor",
48
+ field: "id",
49
+ },
50
+ onDelete: "restrict",
51
+ onUpdate: "cascade",
52
+ })
53
+ )
54
+ .then(() =>
55
+ queryInterface.addConstraint("Bill", {
56
+ fields: ["journalEntryId"],
57
+ type: "foreign key",
58
+ name: "Bill_JournalEntryId_Fkey",
59
+ references: {
60
+ table: "JournalEntry",
61
+ field: "id",
62
+ },
63
+ onDelete: "set null",
64
+ onUpdate: "cascade",
65
+ })
66
+ )
67
+ .then(() => queryInterface.addIndex("Bill", ["status"]))
68
+ .then(() => queryInterface.addIndex("Bill", ["dueDate"]));
69
+ },
70
+ down: async (queryInterface) => {
71
+ await queryInterface.dropTable("Bill");
72
+ },
73
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .createTable(
6
+ "BillLine",
7
+ {
8
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
9
+ billId: { type: DataTypes.INTEGER, allowNull: false },
10
+ chartOfAccountId: { type: DataTypes.INTEGER, allowNull: true },
11
+ description: { type: DataTypes.TEXT, allowNull: true },
12
+ className: { type: DataTypes.STRING, allowNull: true },
13
+ amount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
14
+ taxRate: { type: DataTypes.DECIMAL(6, 4), defaultValue: 0 },
15
+ taxLabel: { type: DataTypes.STRING, allowNull: true },
16
+ netAmount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
17
+ taxAmount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
18
+ total: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
19
+ sortOrder: { type: DataTypes.INTEGER, defaultValue: 0 },
20
+ createdAt: {
21
+ type: DataTypes.DATE,
22
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
23
+ },
24
+ updatedAt: {
25
+ type: DataTypes.DATE,
26
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
27
+ },
28
+ },
29
+ { timestamps: false }
30
+ )
31
+ .then(() =>
32
+ queryInterface.addConstraint("BillLine", {
33
+ fields: ["billId"],
34
+ type: "foreign key",
35
+ name: "BillLine_BillId_Fkey",
36
+ references: {
37
+ table: "Bill",
38
+ field: "id",
39
+ },
40
+ onDelete: "cascade",
41
+ onUpdate: "cascade",
42
+ })
43
+ )
44
+ .then(() =>
45
+ queryInterface.addConstraint("BillLine", {
46
+ fields: ["chartOfAccountId"],
47
+ type: "foreign key",
48
+ name: "BillLine_ChartOfAccountId_Fkey",
49
+ references: {
50
+ table: "ChartOfAccounts",
51
+ field: "id",
52
+ },
53
+ onDelete: "restrict",
54
+ onUpdate: "cascade",
55
+ })
56
+ );
57
+ },
58
+ down: async (queryInterface) => {
59
+ await queryInterface.dropTable("BillLine");
60
+ },
61
+ };
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .createTable(
6
+ "BillAttachment",
7
+ {
8
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
9
+ billId: { type: DataTypes.INTEGER, allowNull: false },
10
+ name: { type: DataTypes.STRING, allowNull: false },
11
+ fileName: { type: DataTypes.STRING, allowNull: false },
12
+ url: { type: DataTypes.STRING, allowNull: false },
13
+ mimeType: { type: DataTypes.STRING, allowNull: true },
14
+ size: { type: DataTypes.INTEGER, allowNull: 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("BillAttachment", {
28
+ fields: ["billId"],
29
+ type: "foreign key",
30
+ name: "BillAttachment_BillId_Fkey",
31
+ references: {
32
+ table: "Bill",
33
+ field: "id",
34
+ },
35
+ onDelete: "cascade",
36
+ onUpdate: "cascade",
37
+ })
38
+ );
39
+ },
40
+ down: async (queryInterface) => {
41
+ await queryInterface.dropTable("BillAttachment");
42
+ },
43
+ };
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .createTable(
6
+ "BillPayment",
7
+ {
8
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
9
+ billId: { type: DataTypes.INTEGER, allowNull: false },
10
+ paymentDate: { type: DataTypes.DATEONLY, allowNull: false },
11
+ amount: { type: DataTypes.DECIMAL(12, 2), allowNull: false },
12
+ paidFromAccountId: { type: DataTypes.INTEGER, allowNull: false },
13
+ method: { type: DataTypes.STRING, allowNull: true },
14
+ reference: { type: DataTypes.STRING, allowNull: true },
15
+ memo: { type: DataTypes.TEXT, allowNull: true },
16
+ journalEntryId: { type: DataTypes.INTEGER, allowNull: true },
17
+ createdBy: { type: DataTypes.INTEGER, allowNull: true },
18
+ createdAt: {
19
+ type: DataTypes.DATE,
20
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
21
+ },
22
+ updatedAt: {
23
+ type: DataTypes.DATE,
24
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
25
+ },
26
+ },
27
+ { timestamps: false }
28
+ )
29
+ .then(() =>
30
+ queryInterface.addConstraint("BillPayment", {
31
+ fields: ["billId"],
32
+ type: "foreign key",
33
+ name: "BillPayment_BillId_Fkey",
34
+ references: {
35
+ table: "Bill",
36
+ field: "id",
37
+ },
38
+ onDelete: "restrict",
39
+ onUpdate: "cascade",
40
+ })
41
+ )
42
+ .then(() =>
43
+ queryInterface.addConstraint("BillPayment", {
44
+ fields: ["paidFromAccountId"],
45
+ type: "foreign key",
46
+ name: "BillPayment_PaidFromAccountId_Fkey",
47
+ references: {
48
+ table: "ChartOfAccounts",
49
+ field: "id",
50
+ },
51
+ onDelete: "restrict",
52
+ onUpdate: "cascade",
53
+ })
54
+ )
55
+ .then(() =>
56
+ queryInterface.addConstraint("BillPayment", {
57
+ fields: ["journalEntryId"],
58
+ type: "foreign key",
59
+ name: "BillPayment_JournalEntryId_Fkey",
60
+ references: {
61
+ table: "JournalEntry",
62
+ field: "id",
63
+ },
64
+ onDelete: "set null",
65
+ onUpdate: "cascade",
66
+ })
67
+ );
68
+ },
69
+ down: async (queryInterface) => {
70
+ await queryInterface.dropTable("BillPayment");
71
+ },
72
+ };
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .createTable(
6
+ "Customer",
7
+ {
8
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
9
+ name: { type: DataTypes.STRING, allowNull: false, unique: true },
10
+ email: { type: DataTypes.STRING, allowNull: true },
11
+ phone: { type: DataTypes.STRING, allowNull: true },
12
+ addressLine1: { type: DataTypes.STRING, allowNull: true },
13
+ addressLine2: { type: DataTypes.STRING, allowNull: true },
14
+ defaultTerms: { type: DataTypes.STRING, allowNull: true },
15
+ defaultChartOfAccountId: { type: DataTypes.INTEGER, allowNull: true },
16
+ notes: { type: DataTypes.TEXT, allowNull: true },
17
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
18
+ createdAt: {
19
+ type: DataTypes.DATE,
20
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
21
+ },
22
+ updatedAt: {
23
+ type: DataTypes.DATE,
24
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
25
+ },
26
+ },
27
+ { timestamps: false }
28
+ )
29
+ .then(() =>
30
+ queryInterface.addConstraint("Customer", {
31
+ fields: ["defaultChartOfAccountId"],
32
+ type: "foreign key",
33
+ name: "Customer_DefaultChartOfAccountId_Fkey",
34
+ references: {
35
+ table: "ChartOfAccounts",
36
+ field: "id",
37
+ },
38
+ onDelete: "set null",
39
+ onUpdate: "cascade",
40
+ })
41
+ );
42
+ },
43
+ down: async (queryInterface) => {
44
+ await queryInterface.dropTable("Customer");
45
+ },
46
+ };
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .createTable(
6
+ "Invoice",
7
+ {
8
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
9
+ invoiceNo: { type: DataTypes.STRING, allowNull: false, unique: true },
10
+ customerId: { type: DataTypes.INTEGER, allowNull: false },
11
+ reference: { type: DataTypes.STRING, allowNull: true },
12
+ description: { type: DataTypes.TEXT, allowNull: true },
13
+ invoiceDate: { type: DataTypes.DATEONLY, allowNull: true },
14
+ dueDate: { type: DataTypes.DATEONLY, allowNull: true },
15
+ terms: { type: DataTypes.STRING, allowNull: true },
16
+ currency: { type: DataTypes.STRING, defaultValue: "USD" },
17
+ memo: { type: DataTypes.TEXT, allowNull: true },
18
+ notes: { type: DataTypes.TEXT, allowNull: true },
19
+ status: { type: DataTypes.STRING, defaultValue: "Draft" },
20
+ subtotal: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
21
+ discount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
22
+ amount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
23
+ amountPaid: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
24
+ amountDue: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
25
+ paidDate: { type: DataTypes.DATEONLY, allowNull: true },
26
+ journalEntryId: { type: DataTypes.INTEGER, allowNull: true },
27
+ createdBy: { type: DataTypes.INTEGER, allowNull: true },
28
+ createdAt: {
29
+ type: DataTypes.DATE,
30
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
31
+ },
32
+ updatedAt: {
33
+ type: DataTypes.DATE,
34
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
35
+ },
36
+ },
37
+ { timestamps: false }
38
+ )
39
+ .then(() =>
40
+ queryInterface.addConstraint("Invoice", {
41
+ fields: ["customerId"],
42
+ type: "foreign key",
43
+ name: "Invoice_CustomerId_Fkey",
44
+ references: {
45
+ table: "Customer",
46
+ field: "id",
47
+ },
48
+ onDelete: "restrict",
49
+ onUpdate: "cascade",
50
+ })
51
+ )
52
+ .then(() =>
53
+ queryInterface.addConstraint("Invoice", {
54
+ fields: ["journalEntryId"],
55
+ type: "foreign key",
56
+ name: "Invoice_JournalEntryId_Fkey",
57
+ references: {
58
+ table: "JournalEntry",
59
+ field: "id",
60
+ },
61
+ onDelete: "set null",
62
+ onUpdate: "cascade",
63
+ })
64
+ )
65
+ .then(() => queryInterface.addIndex("Invoice", ["status"]))
66
+ .then(() => queryInterface.addIndex("Invoice", ["dueDate"]));
67
+ },
68
+ down: async (queryInterface) => {
69
+ await queryInterface.dropTable("Invoice");
70
+ },
71
+ };
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .createTable(
6
+ "InvoiceLine",
7
+ {
8
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
9
+ invoiceId: { type: DataTypes.INTEGER, allowNull: false },
10
+ chartOfAccountId: { type: DataTypes.INTEGER, allowNull: true },
11
+ description: { type: DataTypes.TEXT, allowNull: true },
12
+ quantity: { type: DataTypes.DECIMAL(12, 2), defaultValue: 1 },
13
+ rate: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
14
+ amount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
15
+ sortOrder: { type: DataTypes.INTEGER, defaultValue: 0 },
16
+ createdAt: {
17
+ type: DataTypes.DATE,
18
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
19
+ },
20
+ updatedAt: {
21
+ type: DataTypes.DATE,
22
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
23
+ },
24
+ },
25
+ { timestamps: false }
26
+ )
27
+ .then(() =>
28
+ queryInterface.addConstraint("InvoiceLine", {
29
+ fields: ["invoiceId"],
30
+ type: "foreign key",
31
+ name: "InvoiceLine_InvoiceId_Fkey",
32
+ references: {
33
+ table: "Invoice",
34
+ field: "id",
35
+ },
36
+ onDelete: "cascade",
37
+ onUpdate: "cascade",
38
+ })
39
+ )
40
+ .then(() =>
41
+ queryInterface.addConstraint("InvoiceLine", {
42
+ fields: ["chartOfAccountId"],
43
+ type: "foreign key",
44
+ name: "InvoiceLine_ChartOfAccountId_Fkey",
45
+ references: {
46
+ table: "ChartOfAccounts",
47
+ field: "id",
48
+ },
49
+ onDelete: "restrict",
50
+ onUpdate: "cascade",
51
+ })
52
+ );
53
+ },
54
+ down: async (queryInterface) => {
55
+ await queryInterface.dropTable("InvoiceLine");
56
+ },
57
+ };
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ module.exports = {
3
+ up: async (queryInterface, DataTypes) => {
4
+ await queryInterface
5
+ .createTable(
6
+ "InvoicePayment",
7
+ {
8
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
9
+ invoiceId: { type: DataTypes.INTEGER, allowNull: false },
10
+ paymentDate: { type: DataTypes.DATEONLY, allowNull: false },
11
+ amount: { type: DataTypes.DECIMAL(12, 2), allowNull: false },
12
+ depositToAccountId: { type: DataTypes.INTEGER, allowNull: false },
13
+ method: { type: DataTypes.STRING, allowNull: true },
14
+ reference: { type: DataTypes.STRING, allowNull: true },
15
+ memo: { type: DataTypes.TEXT, allowNull: true },
16
+ journalEntryId: { type: DataTypes.INTEGER, allowNull: true },
17
+ createdBy: { type: DataTypes.INTEGER, allowNull: true },
18
+ createdAt: {
19
+ type: DataTypes.DATE,
20
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
21
+ },
22
+ updatedAt: {
23
+ type: DataTypes.DATE,
24
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
25
+ },
26
+ },
27
+ { timestamps: false }
28
+ )
29
+ .then(() =>
30
+ queryInterface.addConstraint("InvoicePayment", {
31
+ fields: ["invoiceId"],
32
+ type: "foreign key",
33
+ name: "InvoicePayment_InvoiceId_Fkey",
34
+ references: {
35
+ table: "Invoice",
36
+ field: "id",
37
+ },
38
+ onDelete: "restrict",
39
+ onUpdate: "cascade",
40
+ })
41
+ )
42
+ .then(() =>
43
+ queryInterface.addConstraint("InvoicePayment", {
44
+ fields: ["depositToAccountId"],
45
+ type: "foreign key",
46
+ name: "InvoicePayment_DepositToAccountId_Fkey",
47
+ references: {
48
+ table: "ChartOfAccounts",
49
+ field: "id",
50
+ },
51
+ onDelete: "restrict",
52
+ onUpdate: "cascade",
53
+ })
54
+ )
55
+ .then(() =>
56
+ queryInterface.addConstraint("InvoicePayment", {
57
+ fields: ["journalEntryId"],
58
+ type: "foreign key",
59
+ name: "InvoicePayment_JournalEntryId_Fkey",
60
+ references: {
61
+ table: "JournalEntry",
62
+ field: "id",
63
+ },
64
+ onDelete: "set null",
65
+ onUpdate: "cascade",
66
+ })
67
+ );
68
+ },
69
+ down: async (queryInterface) => {
70
+ await queryInterface.dropTable("InvoicePayment");
71
+ },
72
+ };
@@ -0,0 +1,83 @@
1
+ const { BILL_STATUS, BILL_TAX_MODE } = require("../../constants");
2
+
3
+ module.exports = (sequelize, DataTypes) => {
4
+ const bill = sequelize.define(
5
+ "Bill",
6
+ {
7
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
8
+ billNo: { type: DataTypes.STRING, allowNull: false, unique: true },
9
+ vendorId: { type: DataTypes.INTEGER, allowNull: false },
10
+ reference: { type: DataTypes.STRING, allowNull: true },
11
+ description: { type: DataTypes.TEXT, allowNull: true },
12
+ billDate: { type: DataTypes.DATEONLY, allowNull: true },
13
+ dueDate: { type: DataTypes.DATEONLY, allowNull: true },
14
+ terms: { type: DataTypes.STRING, allowNull: true },
15
+ currency: { type: DataTypes.STRING, defaultValue: "USD" },
16
+ memo: { type: DataTypes.TEXT, allowNull: true },
17
+ notes: { type: DataTypes.TEXT, allowNull: true },
18
+ status: { type: DataTypes.STRING, defaultValue: BILL_STATUS.DRAFT },
19
+ taxMode: { type: DataTypes.STRING, defaultValue: BILL_TAX_MODE.EXCLUSIVE },
20
+ subtotal: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
21
+ tax: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
22
+ discount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
23
+ amount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
24
+ amountPaid: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
25
+ amountDue: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
26
+ paidDate: { type: DataTypes.DATEONLY, allowNull: true },
27
+ journalEntryId: { type: DataTypes.INTEGER, allowNull: true },
28
+ createdBy: { type: DataTypes.INTEGER, allowNull: true },
29
+ createdAt: { type: DataTypes.DATE },
30
+ updatedAt: { type: DataTypes.DATE },
31
+ },
32
+ { freezeTableName: true, timestamps: false }
33
+ );
34
+ bill.associate = function (models) {
35
+ bill.belongsTo(models.vendor, {
36
+ foreignKey: "vendorId",
37
+ as: "vendor",
38
+ });
39
+ bill.belongsTo(models.journalEntry, {
40
+ foreignKey: "journalEntryId",
41
+ as: "journalEntry",
42
+ });
43
+ bill.hasMany(models.billLine, {
44
+ foreignKey: "billId",
45
+ as: "billLines",
46
+ });
47
+ bill.hasMany(models.billAttachment, {
48
+ foreignKey: "billId",
49
+ as: "billAttachments",
50
+ });
51
+ bill.hasMany(models.billPayment, {
52
+ foreignKey: "billId",
53
+ as: "billPayments",
54
+ });
55
+ };
56
+ bill.selectedFields = [
57
+ "id",
58
+ "billNo",
59
+ "vendorId",
60
+ "reference",
61
+ "description",
62
+ "billDate",
63
+ "dueDate",
64
+ "terms",
65
+ "currency",
66
+ "memo",
67
+ "notes",
68
+ "status",
69
+ "taxMode",
70
+ "subtotal",
71
+ "tax",
72
+ "discount",
73
+ "amount",
74
+ "amountPaid",
75
+ "amountDue",
76
+ "paidDate",
77
+ "journalEntryId",
78
+ "createdBy",
79
+ "createdAt",
80
+ "updatedAt",
81
+ ];
82
+ return bill;
83
+ };
@@ -0,0 +1,34 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const billAttachment = sequelize.define(
3
+ "BillAttachment",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ billId: { type: DataTypes.INTEGER, allowNull: false },
7
+ name: { type: DataTypes.STRING, allowNull: false },
8
+ fileName: { type: DataTypes.STRING, allowNull: false },
9
+ url: { type: DataTypes.STRING, allowNull: false },
10
+ mimeType: { type: DataTypes.STRING, allowNull: true },
11
+ size: { type: DataTypes.INTEGER, allowNull: true },
12
+ createdAt: { type: DataTypes.DATE },
13
+ updatedAt: { type: DataTypes.DATE },
14
+ },
15
+ { freezeTableName: true, timestamps: false }
16
+ );
17
+ billAttachment.associate = function (models) {
18
+ billAttachment.belongsTo(models.bill, {
19
+ foreignKey: "billId",
20
+ as: "bill",
21
+ });
22
+ };
23
+ billAttachment.selectedFields = [
24
+ "id",
25
+ "billId",
26
+ "name",
27
+ "fileName",
28
+ "url",
29
+ "mimeType",
30
+ "size",
31
+ "createdAt",
32
+ ];
33
+ return billAttachment;
34
+ };
@@ -0,0 +1,47 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const billLine = sequelize.define(
3
+ "BillLine",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ billId: { type: DataTypes.INTEGER, allowNull: false },
7
+ chartOfAccountId: { type: DataTypes.INTEGER, allowNull: true },
8
+ description: { type: DataTypes.TEXT, allowNull: true },
9
+ className: { type: DataTypes.STRING, allowNull: true },
10
+ amount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
11
+ taxRate: { type: DataTypes.DECIMAL(6, 4), defaultValue: 0 },
12
+ taxLabel: { type: DataTypes.STRING, allowNull: true },
13
+ netAmount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
14
+ taxAmount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
15
+ total: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
16
+ sortOrder: { type: DataTypes.INTEGER, defaultValue: 0 },
17
+ createdAt: { type: DataTypes.DATE },
18
+ updatedAt: { type: DataTypes.DATE },
19
+ },
20
+ { freezeTableName: true, timestamps: false }
21
+ );
22
+ billLine.associate = function (models) {
23
+ billLine.belongsTo(models.bill, {
24
+ foreignKey: "billId",
25
+ as: "bill",
26
+ });
27
+ billLine.belongsTo(models.chartOfAccounts, {
28
+ foreignKey: "chartOfAccountId",
29
+ as: "chartOfAccount",
30
+ });
31
+ };
32
+ billLine.selectedFields = [
33
+ "id",
34
+ "billId",
35
+ "chartOfAccountId",
36
+ "description",
37
+ "className",
38
+ "amount",
39
+ "taxRate",
40
+ "taxLabel",
41
+ "netAmount",
42
+ "taxAmount",
43
+ "total",
44
+ "sortOrder",
45
+ ];
46
+ return billLine;
47
+ };
@@ -0,0 +1,48 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const billPayment = sequelize.define(
3
+ "BillPayment",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ billId: { type: DataTypes.INTEGER, allowNull: false },
7
+ paymentDate: { type: DataTypes.DATEONLY, allowNull: false },
8
+ amount: { type: DataTypes.DECIMAL(12, 2), allowNull: false },
9
+ paidFromAccountId: { type: DataTypes.INTEGER, allowNull: false },
10
+ method: { type: DataTypes.STRING, allowNull: true },
11
+ reference: { type: DataTypes.STRING, allowNull: true },
12
+ memo: { type: DataTypes.TEXT, allowNull: true },
13
+ journalEntryId: { type: DataTypes.INTEGER, allowNull: true },
14
+ createdBy: { type: DataTypes.INTEGER, allowNull: true },
15
+ createdAt: { type: DataTypes.DATE },
16
+ updatedAt: { type: DataTypes.DATE },
17
+ },
18
+ { freezeTableName: true, timestamps: false }
19
+ );
20
+ billPayment.associate = function (models) {
21
+ billPayment.belongsTo(models.bill, {
22
+ foreignKey: "billId",
23
+ as: "bill",
24
+ });
25
+ billPayment.belongsTo(models.chartOfAccounts, {
26
+ foreignKey: "paidFromAccountId",
27
+ as: "paidFromAccount",
28
+ });
29
+ billPayment.belongsTo(models.journalEntry, {
30
+ foreignKey: "journalEntryId",
31
+ as: "journalEntry",
32
+ });
33
+ };
34
+ billPayment.selectedFields = [
35
+ "id",
36
+ "billId",
37
+ "paymentDate",
38
+ "amount",
39
+ "paidFromAccountId",
40
+ "method",
41
+ "reference",
42
+ "memo",
43
+ "journalEntryId",
44
+ "createdBy",
45
+ "createdAt",
46
+ ];
47
+ return billPayment;
48
+ };
@@ -0,0 +1,45 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const customer = sequelize.define(
3
+ "Customer",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ name: { type: DataTypes.STRING, allowNull: false, unique: true },
7
+ email: { type: DataTypes.STRING, allowNull: true },
8
+ phone: { type: DataTypes.STRING, allowNull: true },
9
+ addressLine1: { type: DataTypes.STRING, allowNull: true },
10
+ addressLine2: { type: DataTypes.STRING, allowNull: true },
11
+ defaultTerms: { type: DataTypes.STRING, allowNull: true },
12
+ defaultChartOfAccountId: { type: DataTypes.INTEGER, allowNull: true },
13
+ notes: { type: DataTypes.TEXT, allowNull: true },
14
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
15
+ createdAt: { type: DataTypes.DATE },
16
+ updatedAt: { type: DataTypes.DATE },
17
+ },
18
+ { freezeTableName: true, timestamps: false }
19
+ );
20
+ customer.associate = function (models) {
21
+ customer.belongsTo(models.chartOfAccounts, {
22
+ foreignKey: "defaultChartOfAccountId",
23
+ as: "defaultChartOfAccount",
24
+ });
25
+ customer.hasMany(models.invoice, {
26
+ foreignKey: "customerId",
27
+ as: "invoices",
28
+ });
29
+ };
30
+ customer.selectedFields = [
31
+ "id",
32
+ "name",
33
+ "email",
34
+ "phone",
35
+ "addressLine1",
36
+ "addressLine2",
37
+ "defaultTerms",
38
+ "defaultChartOfAccountId",
39
+ "notes",
40
+ "status",
41
+ "createdAt",
42
+ "updatedAt",
43
+ ];
44
+ return customer;
45
+ };
@@ -0,0 +1,75 @@
1
+ const { INVOICE_STATUS } = require("../../constants");
2
+
3
+ module.exports = (sequelize, DataTypes) => {
4
+ const invoice = sequelize.define(
5
+ "Invoice",
6
+ {
7
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
8
+ invoiceNo: { type: DataTypes.STRING, allowNull: false, unique: true },
9
+ customerId: { type: DataTypes.INTEGER, allowNull: false },
10
+ reference: { type: DataTypes.STRING, allowNull: true },
11
+ description: { type: DataTypes.TEXT, allowNull: true },
12
+ invoiceDate: { type: DataTypes.DATEONLY, allowNull: true },
13
+ dueDate: { type: DataTypes.DATEONLY, allowNull: true },
14
+ terms: { type: DataTypes.STRING, allowNull: true },
15
+ currency: { type: DataTypes.STRING, defaultValue: "USD" },
16
+ memo: { type: DataTypes.TEXT, allowNull: true },
17
+ notes: { type: DataTypes.TEXT, allowNull: true },
18
+ status: { type: DataTypes.STRING, defaultValue: INVOICE_STATUS.DRAFT },
19
+ subtotal: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
20
+ discount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
21
+ amount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
22
+ amountPaid: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
23
+ amountDue: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
24
+ paidDate: { type: DataTypes.DATEONLY, allowNull: true },
25
+ journalEntryId: { type: DataTypes.INTEGER, allowNull: true },
26
+ createdBy: { type: DataTypes.INTEGER, allowNull: true },
27
+ createdAt: { type: DataTypes.DATE },
28
+ updatedAt: { type: DataTypes.DATE },
29
+ },
30
+ { freezeTableName: true, timestamps: false }
31
+ );
32
+ invoice.associate = function (models) {
33
+ invoice.belongsTo(models.customer, {
34
+ foreignKey: "customerId",
35
+ as: "customer",
36
+ });
37
+ invoice.belongsTo(models.journalEntry, {
38
+ foreignKey: "journalEntryId",
39
+ as: "journalEntry",
40
+ });
41
+ invoice.hasMany(models.invoiceLine, {
42
+ foreignKey: "invoiceId",
43
+ as: "invoiceLines",
44
+ });
45
+ invoice.hasMany(models.invoicePayment, {
46
+ foreignKey: "invoiceId",
47
+ as: "invoicePayments",
48
+ });
49
+ };
50
+ invoice.selectedFields = [
51
+ "id",
52
+ "invoiceNo",
53
+ "customerId",
54
+ "reference",
55
+ "description",
56
+ "invoiceDate",
57
+ "dueDate",
58
+ "terms",
59
+ "currency",
60
+ "memo",
61
+ "notes",
62
+ "status",
63
+ "subtotal",
64
+ "discount",
65
+ "amount",
66
+ "amountPaid",
67
+ "amountDue",
68
+ "paidDate",
69
+ "journalEntryId",
70
+ "createdBy",
71
+ "createdAt",
72
+ "updatedAt",
73
+ ];
74
+ return invoice;
75
+ };
@@ -0,0 +1,39 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const invoiceLine = sequelize.define(
3
+ "InvoiceLine",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ invoiceId: { type: DataTypes.INTEGER, allowNull: false },
7
+ chartOfAccountId: { type: DataTypes.INTEGER, allowNull: true },
8
+ description: { type: DataTypes.TEXT, allowNull: true },
9
+ quantity: { type: DataTypes.DECIMAL(12, 2), defaultValue: 1 },
10
+ rate: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
11
+ amount: { type: DataTypes.DECIMAL(12, 2), defaultValue: 0 },
12
+ sortOrder: { type: DataTypes.INTEGER, defaultValue: 0 },
13
+ createdAt: { type: DataTypes.DATE },
14
+ updatedAt: { type: DataTypes.DATE },
15
+ },
16
+ { freezeTableName: true, timestamps: false }
17
+ );
18
+ invoiceLine.associate = function (models) {
19
+ invoiceLine.belongsTo(models.invoice, {
20
+ foreignKey: "invoiceId",
21
+ as: "invoice",
22
+ });
23
+ invoiceLine.belongsTo(models.chartOfAccounts, {
24
+ foreignKey: "chartOfAccountId",
25
+ as: "chartOfAccount",
26
+ });
27
+ };
28
+ invoiceLine.selectedFields = [
29
+ "id",
30
+ "invoiceId",
31
+ "chartOfAccountId",
32
+ "description",
33
+ "quantity",
34
+ "rate",
35
+ "amount",
36
+ "sortOrder",
37
+ ];
38
+ return invoiceLine;
39
+ };
@@ -0,0 +1,48 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const invoicePayment = sequelize.define(
3
+ "InvoicePayment",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ invoiceId: { type: DataTypes.INTEGER, allowNull: false },
7
+ paymentDate: { type: DataTypes.DATEONLY, allowNull: false },
8
+ amount: { type: DataTypes.DECIMAL(12, 2), allowNull: false },
9
+ depositToAccountId: { type: DataTypes.INTEGER, allowNull: false },
10
+ method: { type: DataTypes.STRING, allowNull: true },
11
+ reference: { type: DataTypes.STRING, allowNull: true },
12
+ memo: { type: DataTypes.TEXT, allowNull: true },
13
+ journalEntryId: { type: DataTypes.INTEGER, allowNull: true },
14
+ createdBy: { type: DataTypes.INTEGER, allowNull: true },
15
+ createdAt: { type: DataTypes.DATE },
16
+ updatedAt: { type: DataTypes.DATE },
17
+ },
18
+ { freezeTableName: true, timestamps: false }
19
+ );
20
+ invoicePayment.associate = function (models) {
21
+ invoicePayment.belongsTo(models.invoice, {
22
+ foreignKey: "invoiceId",
23
+ as: "invoice",
24
+ });
25
+ invoicePayment.belongsTo(models.chartOfAccounts, {
26
+ foreignKey: "depositToAccountId",
27
+ as: "depositToAccount",
28
+ });
29
+ invoicePayment.belongsTo(models.journalEntry, {
30
+ foreignKey: "journalEntryId",
31
+ as: "journalEntry",
32
+ });
33
+ };
34
+ invoicePayment.selectedFields = [
35
+ "id",
36
+ "invoiceId",
37
+ "paymentDate",
38
+ "amount",
39
+ "depositToAccountId",
40
+ "method",
41
+ "reference",
42
+ "memo",
43
+ "journalEntryId",
44
+ "createdBy",
45
+ "createdAt",
46
+ ];
47
+ return invoicePayment;
48
+ };
@@ -0,0 +1,45 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const vendor = sequelize.define(
3
+ "Vendor",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ name: { type: DataTypes.STRING, allowNull: false, unique: true },
7
+ email: { type: DataTypes.STRING, allowNull: true },
8
+ phone: { type: DataTypes.STRING, allowNull: true },
9
+ addressLine1: { type: DataTypes.STRING, allowNull: true },
10
+ addressLine2: { type: DataTypes.STRING, allowNull: true },
11
+ defaultTerms: { type: DataTypes.STRING, allowNull: true },
12
+ defaultChartOfAccountId: { type: DataTypes.INTEGER, allowNull: true },
13
+ notes: { type: DataTypes.TEXT, allowNull: true },
14
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
15
+ createdAt: { type: DataTypes.DATE },
16
+ updatedAt: { type: DataTypes.DATE },
17
+ },
18
+ { freezeTableName: true, timestamps: false }
19
+ );
20
+ vendor.associate = function (models) {
21
+ vendor.belongsTo(models.chartOfAccounts, {
22
+ foreignKey: "defaultChartOfAccountId",
23
+ as: "defaultChartOfAccount",
24
+ });
25
+ vendor.hasMany(models.bill, {
26
+ foreignKey: "vendorId",
27
+ as: "bills",
28
+ });
29
+ };
30
+ vendor.selectedFields = [
31
+ "id",
32
+ "name",
33
+ "email",
34
+ "phone",
35
+ "addressLine1",
36
+ "addressLine2",
37
+ "defaultTerms",
38
+ "defaultChartOfAccountId",
39
+ "notes",
40
+ "status",
41
+ "createdAt",
42
+ "updatedAt",
43
+ ];
44
+ return vendor;
45
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@churchsoln/dbms",
3
- "version": "1.0.62",
3
+ "version": "1.0.64",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "prestart": "node db-setup.js",
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ const { ACCOUNTS_PAYABLE_CODE, CHART_OF_ACCOUNTS } = require("../../constants");
4
+
5
+ // The chartOfAccounts seeder is tracked in SequelizeData, so existing churches never re-run it.
6
+ // This seeder adds the Accounts Payable account that bills post to.
7
+ module.exports = {
8
+ up: async (queryInterface) => {
9
+ const account = CHART_OF_ACCOUNTS.find((a) => a.code === ACCOUNTS_PAYABLE_CODE);
10
+ const existingId = await queryInterface.rawSelect(
11
+ "ChartOfAccounts",
12
+ { where: { code: account.code } },
13
+ ["id"]
14
+ );
15
+ if (existingId) return;
16
+
17
+ const accountingAccountTypeId = await queryInterface.rawSelect(
18
+ "AccountingAccountTypes",
19
+ { where: { name: account.accountingAccountType } },
20
+ ["id"]
21
+ );
22
+ // Account types are seeded by 20260706170638-accountingAccountTypes; without them there is no chart to extend.
23
+ if (!accountingAccountTypeId) return;
24
+ const parentAccountId = await queryInterface.rawSelect(
25
+ "ChartOfAccounts",
26
+ { where: { code: account.parentCode } },
27
+ ["id"]
28
+ );
29
+ await queryInterface.bulkInsert("ChartOfAccounts", [
30
+ {
31
+ code: account.code,
32
+ name: account.name,
33
+ accountingAccountTypeId,
34
+ parentAccountId: parentAccountId || null,
35
+ isPostingAccount: account.isPostingAccount,
36
+ status: true,
37
+ },
38
+ ]);
39
+ },
40
+
41
+ down: async (queryInterface) => {
42
+ await queryInterface.bulkDelete("ChartOfAccounts", { code: ACCOUNTS_PAYABLE_CODE });
43
+ },
44
+ };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ const { ACCOUNTS_RECEIVABLE_CODE, CHART_OF_ACCOUNTS } = require("../../constants");
4
+
5
+ // The chartOfAccounts seeder is tracked in SequelizeData, so existing churches never re-run it.
6
+ // This seeder adds the Accounts Receivable account that invoices post to.
7
+ module.exports = {
8
+ up: async (queryInterface) => {
9
+ const account = CHART_OF_ACCOUNTS.find((a) => a.code === ACCOUNTS_RECEIVABLE_CODE);
10
+ const existingId = await queryInterface.rawSelect(
11
+ "ChartOfAccounts",
12
+ { where: { code: account.code } },
13
+ ["id"]
14
+ );
15
+ if (existingId) return;
16
+
17
+ const accountingAccountTypeId = await queryInterface.rawSelect(
18
+ "AccountingAccountTypes",
19
+ { where: { name: account.accountingAccountType } },
20
+ ["id"]
21
+ );
22
+ // Account types are seeded by 20260706170638-accountingAccountTypes; without them there is no chart to extend.
23
+ if (!accountingAccountTypeId) return;
24
+ const parentAccountId = await queryInterface.rawSelect(
25
+ "ChartOfAccounts",
26
+ { where: { code: account.parentCode } },
27
+ ["id"]
28
+ );
29
+ await queryInterface.bulkInsert("ChartOfAccounts", [
30
+ {
31
+ code: account.code,
32
+ name: account.name,
33
+ accountingAccountTypeId,
34
+ parentAccountId: parentAccountId || null,
35
+ isPostingAccount: account.isPostingAccount,
36
+ status: true,
37
+ },
38
+ ]);
39
+ },
40
+
41
+ down: async (queryInterface) => {
42
+ await queryInterface.bulkDelete("ChartOfAccounts", { code: ACCOUNTS_RECEIVABLE_CODE });
43
+ },
44
+ };