diff --git a/launch.json b/launch.json new file mode 100644 index 000000000..45e2d5c3f --- /dev/null +++ b/launch.json @@ -0,0 +1,24 @@ + +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug Nest Framework", + "runtimeExecutable": "npm", + "runtimeArgs": [ + "run", + "start:debug", + "--", + "--inspect-brk" + ], + "autoAttachChildProcesses": true, + "restart": true, + "sourceMaps": true, + "stopOnEntry": false, + "console": "integratedTerminal" + } + ] +} + diff --git a/package.json b/package.json index 69f5dd546..ac8bd27bb 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "server2:start": "lerna run start:dev --scope \"@bigcapital/server2\"", "test:watch": "lerna run test:watch", "test:e2e": "lerna run test:e2e", + "start:debug": "lerna run start:debug", "prepare": "husky install" }, "devDependencies": { diff --git a/packages/server-nest/src/common/config/tenant-database.ts b/packages/server-nest/src/common/config/tenant-database.ts index 0885f2383..d20fac41a 100644 --- a/packages/server-nest/src/common/config/tenant-database.ts +++ b/packages/server-nest/src/common/config/tenant-database.ts @@ -1,3 +1,4 @@ +import * as path from 'path'; import { registerAs } from '@nestjs/config'; export default registerAs('tenantDatabase', () => ({ @@ -6,4 +7,7 @@ export default registerAs('tenantDatabase', () => ({ port: process.env.TENANT_DB_PORT || process.env.DB_PORT || 5432, user: process.env.TENANT_DB_USER || process.env.DB_USER, password: process.env.TENANT_DB_PASSWORD || process.env.DB_PASSWORD, + dbNamePrefix: process.env.TENANT_DB_NAME_PERFIX || 'bigcapital_tenant_', + migrationsDir: path.join(__dirname, '../../database/migrations'), + seedsDir: path.join(__dirname, '../../database/migrations'), })); diff --git a/packages/server-nest/src/database/migrations/20190822214303_create_accounts_table.ts b/packages/server-nest/src/database/migrations/20190822214303_create_accounts_table.ts new file mode 100644 index 000000000..81abcaf26 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20190822214303_create_accounts_table.ts @@ -0,0 +1,19 @@ +exports.up = function (knex) { + return knex.schema.createTable('accounts', (table) => { + table.increments('id').comment('Auto-generated id'); + table.string('name').index(); + table.string('slug'); + table.string('account_type').index(); + table.integer('parent_account_id').unsigned().references('id').inTable('accounts'); + table.string('code', 10).index(); + table.text('description'); + table.boolean('active').defaultTo(true).index(); + table.integer('index').unsigned(); + table.boolean('predefined').defaultTo(false).index(); + table.decimal('amount', 15, 5); + table.string('currency_code', 3).index(); + table.timestamps(); + }).raw('ALTER TABLE `ACCOUNTS` AUTO_INCREMENT = 1000'); +}; + +exports.down = (knex) => knex.schema.dropTableIfExists('accounts'); diff --git a/packages/server-nest/src/database/migrations/20190822214304_create_items_categories_table.ts b/packages/server-nest/src/database/migrations/20190822214304_create_items_categories_table.ts new file mode 100644 index 000000000..2fe1ec9ef --- /dev/null +++ b/packages/server-nest/src/database/migrations/20190822214304_create_items_categories_table.ts @@ -0,0 +1,19 @@ + +exports.up = function (knex) { + return knex.schema.createTable('items_categories', (table) => { + table.increments(); + table.string('name').index(); + + table.text('description'); + table.integer('user_id').unsigned().index(); + + table.integer('cost_account_id').unsigned().references('id').inTable('accounts'); + table.integer('sell_account_id').unsigned().references('id').inTable('accounts'); + table.integer('inventory_account_id').unsigned().references('id').inTable('accounts'); + + table.string('cost_method'); + table.timestamps(); + }); +}; + +exports.down = (knex) => knex.schema.dropTableIfExists('items_categories'); diff --git a/packages/server-nest/src/database/migrations/20190822214306_create_items_table.ts b/packages/server-nest/src/database/migrations/20190822214306_create_items_table.ts new file mode 100644 index 000000000..16ac1ed66 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20190822214306_create_items_table.ts @@ -0,0 +1,30 @@ + +exports.up = function (knex) { + return knex.schema.createTable('items', (table) => { + table.increments(); + table.string('name').index(); + table.string('type').index(); + table.string('code'); + table.boolean('sellable').index(); + table.boolean('purchasable').index(); + table.decimal('sell_price', 13, 3).unsigned(); + table.decimal('cost_price', 13, 3).unsigned(); + table.string('currency_code', 3); + table.string('picture_uri'); + table.integer('cost_account_id').nullable().unsigned().references('id').inTable('accounts'); + table.integer('sell_account_id').nullable().unsigned().references('id').inTable('accounts'); + table.integer('inventory_account_id').unsigned().references('id').inTable('accounts'); + table.text('sell_description').nullable(); + table.text('purchase_description').nullable(); + table.integer('quantity_on_hand'); + table.boolean('landed_cost').nullable(); + + table.text('note').nullable(); + table.boolean('active'); + table.integer('category_id').unsigned().index().references('id').inTable('items_categories'); + table.integer('user_id').unsigned().index(); + table.timestamps(); + }).raw('ALTER TABLE `ITEMS` AUTO_INCREMENT = 1000'); +}; + +exports.down = (knex) => knex.schema.dropTableIfExists('items'); diff --git a/packages/server-nest/src/database/migrations/20190822214903_create_views_table.ts b/packages/server-nest/src/database/migrations/20190822214903_create_views_table.ts new file mode 100644 index 000000000..eb3929c47 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20190822214903_create_views_table.ts @@ -0,0 +1,15 @@ + +exports.up = function (knex) { + return knex.schema.createTable('views', (table) => { + table.increments(); + table.string('name').index(); + table.string('slug').index(); + table.boolean('predefined'); + table.string('resource_model').index(); + table.boolean('favourite'); + table.string('roles_logic_expression'); + table.timestamps(); + }).raw('ALTER TABLE `VIEWS` AUTO_INCREMENT = 1000'); +}; + +exports.down = (knex) => knex.schema.dropTableIfExists('views'); diff --git a/packages/server-nest/src/database/migrations/20190822214904_create_settings_table.ts b/packages/server-nest/src/database/migrations/20190822214904_create_settings_table.ts new file mode 100644 index 000000000..65f3f4fdc --- /dev/null +++ b/packages/server-nest/src/database/migrations/20190822214904_create_settings_table.ts @@ -0,0 +1,13 @@ + +exports.up = function (knex) { + return knex.schema.createTable('settings', (table) => { + table.increments(); + table.integer('user_id').unsigned().index(); + table.string('group').index(); + table.string('type'); + table.string('key').index(); + table.string('value'); + }).raw('ALTER TABLE `SETTINGS` AUTO_INCREMENT = 2000'); +}; + +exports.down = (knex) => knex.schema.dropTableIfExists('settings'); diff --git a/packages/server-nest/src/database/migrations/20190822214905_create_views_columns.ts b/packages/server-nest/src/database/migrations/20190822214905_create_views_columns.ts new file mode 100644 index 000000000..4fc76e399 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20190822214905_create_views_columns.ts @@ -0,0 +1,11 @@ + +exports.up = function (knex) { + return knex.schema.createTable('view_has_columns', (table) => { + table.increments(); + table.integer('view_id').unsigned().index().references('id').inTable('views'); + table.string('field_key'); + table.integer('index').unsigned(); + }).raw('ALTER TABLE `ITEMS_CATEGORIES` AUTO_INCREMENT = 1000'); +}; + +exports.down = (knex) => knex.schema.dropTableIfExists('view_has_columns'); diff --git a/packages/server-nest/src/database/migrations/20190822214905_create_views_roles_table.ts b/packages/server-nest/src/database/migrations/20190822214905_create_views_roles_table.ts new file mode 100644 index 000000000..4167514c3 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20190822214905_create_views_roles_table.ts @@ -0,0 +1,19 @@ +exports.up = function (knex) { + return knex.schema + .createTable('view_roles', (table) => { + table.increments(); + table.integer('index'); + table.string('field_key').index(); + table.string('comparator'); + table.string('value'); + table + .integer('view_id') + .unsigned() + .index() + .references('id') + .inTable('views'); + }) + .raw('ALTER TABLE `VIEW_ROLES` AUTO_INCREMENT = 1000'); +}; + +exports.down = (knex) => knex.schema.dropTableIfExists('view_roles'); diff --git a/packages/server-nest/src/database/migrations/20200104232644_create_contacts_table.ts b/packages/server-nest/src/database/migrations/20200104232644_create_contacts_table.ts new file mode 100644 index 000000000..09e0accde --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200104232644_create_contacts_table.ts @@ -0,0 +1,54 @@ + +exports.up = function(knex) { + return knex.schema.createTable('contacts', table => { + table.increments(); + + table.string('contact_service'); + table.string('contact_type'); + + table.decimal('balance', 13, 3).defaultTo(0); + table.string('currency_code', 3); + + table.decimal('opening_balance', 13, 3).defaultTo(0); + table.date('opening_balance_at'); + + table.string('salutation').nullable(); + table.string('first_name').nullable(); + table.string('last_name').nullable(); + table.string('company_name').nullable(); + + table.string('display_name'); + + table.string('email').nullable(); + table.string('work_phone').nullable(); + table.string('personal_phone').nullable(); + table.string('website').nullable(); + + table.string('billing_address_1').nullable(); + table.string('billing_address_2').nullable(); + table.string('billing_address_city').nullable(); + table.string('billing_address_country').nullable(); + table.string('billing_address_email').nullable(); + table.string('billing_address_postcode').nullable(); + table.string('billing_address_phone').nullable(); + table.string('billing_address_state').nullable(), + + table.string('shipping_address_1').nullable(); + table.string('shipping_address_2').nullable(); + table.string('shipping_address_city').nullable(); + table.string('shipping_address_country').nullable(); + table.string('shipping_address_email').nullable(); + table.string('shipping_address_postcode').nullable(); + table.string('shipping_address_phone').nullable(); + table.string('shipping_address_state').nullable(); + + table.text('note'); + table.boolean('active').defaultTo(true); + + table.timestamps(); + }); +}; + +exports.down = function(knex) { + return knex.schema.dropTableIfExists('contacts'); +}; diff --git a/packages/server-nest/src/database/migrations/20200104232647_create_accounts_transactions_table.ts b/packages/server-nest/src/database/migrations/20200104232647_create_accounts_transactions_table.ts new file mode 100644 index 000000000..50fe6d396 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200104232647_create_accounts_transactions_table.ts @@ -0,0 +1,36 @@ +exports.up = function (knex) { + return knex.schema + .createTable('accounts_transactions', (table) => { + table.increments(); + table.decimal('credit', 13, 3); + table.decimal('debit', 13, 3); + table.string('transaction_type').index(); + table.string('reference_type').index(); + table.integer('reference_id').index(); + table + .integer('account_id') + .unsigned() + .index() + .references('id') + .inTable('accounts'); + table.string('contact_type').nullable().index(); + table.integer('contact_id').unsigned().nullable().index(); + table.string('transaction_number').nullable().index(); + table.string('reference_number').nullable().index(); + table.integer('item_id').unsigned().nullable().index(); + table.integer('item_quantity').unsigned().nullable().index(), + table.string('note'); + table.integer('user_id').unsigned().index(); + + table.integer('index_group').unsigned().index(); + table.integer('index').unsigned().index(); + + table.date('date').index(); + table.datetime('created_at').index(); + }) + .raw('ALTER TABLE `ACCOUNTS_TRANSACTIONS` AUTO_INCREMENT = 1000'); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('accounts_transactions'); +}; diff --git a/packages/server-nest/src/database/migrations/20200105014405_create_expenses_table.ts b/packages/server-nest/src/database/migrations/20200105014405_create_expenses_table.ts new file mode 100644 index 000000000..169856f33 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200105014405_create_expenses_table.ts @@ -0,0 +1,29 @@ +exports.up = function (knex) { + return knex.schema + .createTable('expenses_transactions', (table) => { + table.increments(); + table.string('currency_code', 3); + table.text('description'); + table + .integer('payment_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table.integer('payee_id').unsigned().references('id').inTable('contacts'); + table.string('reference_no'); + + table.decimal('total_amount', 13, 3); + table.decimal('landed_cost_amount', 13, 3).defaultTo(0); + table.decimal('allocated_cost_amount', 13, 3).defaultTo(0); + + table.date('published_at').index(); + table.integer('user_id').unsigned().index(); + table.date('payment_date').index(); + table.timestamps(); + }) + .raw('ALTER TABLE `EXPENSES_TRANSACTIONS` AUTO_INCREMENT = 1000'); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('expenses'); +}; diff --git a/packages/server-nest/src/database/migrations/20200105195823_create_manual_journals_table.ts b/packages/server-nest/src/database/migrations/20200105195823_create_manual_journals_table.ts new file mode 100644 index 000000000..8c2714648 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200105195823_create_manual_journals_table.ts @@ -0,0 +1,21 @@ + +exports.up = function(knex) { + return knex.schema.createTable('manual_journals', (table) => { + table.increments(); + table.string('journal_number').index(); + table.string('reference').index(); + table.string('journal_type').index(); + table.decimal('amount', 13, 3); + table.string('currency_code', 3); + table.date('date').index(); + table.string('description'); + table.date('published_at').index(); + table.string('attachment_file'); + table.integer('user_id').unsigned().index(); + table.timestamps(); + }).raw('ALTER TABLE `MANUAL_JOURNALS` AUTO_INCREMENT = 1000'); +}; + +exports.down = function(knex) { + return knex.schema.dropTableIfExists('manual_journals'); +}; diff --git a/packages/server-nest/src/database/migrations/20200105195825_create_manual_journals_entries_table.ts b/packages/server-nest/src/database/migrations/20200105195825_create_manual_journals_entries_table.ts new file mode 100644 index 000000000..ecf22cf4c --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200105195825_create_manual_journals_entries_table.ts @@ -0,0 +1,17 @@ + +exports.up = function(knex) { + return knex.schema.createTable('manual_journals_entries', (table) => { + table.increments(); + table.decimal('credit', 13, 3); + table.decimal('debit', 13, 3); + table.integer('index').unsigned(); + table.integer('account_id').unsigned().index().references('id').inTable('accounts'); + table.integer('contact_id').unsigned().nullable().index(); + table.string('note'); + table.integer('manual_journal_id').unsigned().index().references('id').inTable('manual_journals'); + }).raw('ALTER TABLE `MANUAL_JOURNALS_ENTRIES` AUTO_INCREMENT = 1000'); +}; + +exports.down = function(knex) { + return knex.schema.dropTableIfExists('manual_journals_entries'); +}; diff --git a/packages/server-nest/src/database/migrations/20200419171451_create_currencies_table.ts b/packages/server-nest/src/database/migrations/20200419171451_create_currencies_table.ts new file mode 100644 index 000000000..4d06717b9 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200419171451_create_currencies_table.ts @@ -0,0 +1,14 @@ + +exports.up = function(knex) { + return knex.schema.createTable('currencies', table => { + table.increments(); + table.string('currency_name').index(); + table.string('currency_code', 4).index(); + table.string('currency_sign').index(); + table.timestamps(); + }).raw('ALTER TABLE `CURRENCIES` AUTO_INCREMENT = 1000'); +}; + +exports.down = function(knex) { + return knex.schema.dropTableIfExists('currencies'); +}; diff --git a/packages/server-nest/src/database/migrations/20200419191832_create_exchange_rates_table.ts b/packages/server-nest/src/database/migrations/20200419191832_create_exchange_rates_table.ts new file mode 100644 index 000000000..99db76530 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200419191832_create_exchange_rates_table.ts @@ -0,0 +1,14 @@ + +exports.up = function(knex) { + return knex.schema.createTable('exchange_rates', table => { + table.increments(); + table.string('currency_code', 4).index(); + table.decimal('exchange_rate'); + table.date('date').index(); + table.timestamps(); + }).raw('ALTER TABLE `EXCHANGE_RATES` AUTO_INCREMENT = 1000'); +}; + +exports.down = function(knex) { + return knex.schema.dropTableIfExists('exchange_rates'); +}; diff --git a/packages/server-nest/src/database/migrations/20200423201600_create_media_table.ts b/packages/server-nest/src/database/migrations/20200423201600_create_media_table.ts new file mode 100644 index 000000000..64ffc3940 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200423201600_create_media_table.ts @@ -0,0 +1,12 @@ + +exports.up = function(knex) { + return knex.schema.createTable('media', (table) => { + table.increments(); + table.string('attachment_file'); + table.timestamps(); + }); +}; + +exports.down = function(knex) { + return knex.schema.dropTableIfExists('media'); +}; diff --git a/packages/server-nest/src/database/migrations/20200503032011_create_media_links_table.ts b/packages/server-nest/src/database/migrations/20200503032011_create_media_links_table.ts new file mode 100644 index 000000000..31d26be4b --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200503032011_create_media_links_table.ts @@ -0,0 +1,13 @@ + +exports.up = function(knex) { + return knex.schema.createTable('media_links', table => { + table.increments(); + table.string('model_name').index(); + table.integer('media_id').unsigned().references('id').inTable('media'); + table.integer('model_id').unsigned().index(); + }) +}; + +exports.down = function(knex) { + return knex.schema.dropTableIfExists('media_links'); +}; diff --git a/packages/server-nest/src/database/migrations/20200606113848_create_expense_transactions_categories_table.ts b/packages/server-nest/src/database/migrations/20200606113848_create_expense_transactions_categories_table.ts new file mode 100644 index 000000000..a1bc88052 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200606113848_create_expense_transactions_categories_table.ts @@ -0,0 +1,29 @@ +exports.up = function (knex) { + return knex.schema + .createTable('expense_transaction_categories', (table) => { + table.increments(); + table + .integer('expense_account_id') + .unsigned() + .index() + .references('id') + .inTable('accounts'); + table.integer('index').unsigned(); + table.text('description'); + table.decimal('amount', 13, 3); + table.decimal('allocated_cost_amount', 13, 3).defaultTo(0); + table.boolean('landed_cost').defaultTo(false); + table + .integer('expense_id') + .unsigned() + .index() + .references('id') + .inTable('expenses_transactions'); + table.timestamps(); + }) + .raw('ALTER TABLE `EXPENSE_TRANSACTION_CATEGORIES` AUTO_INCREMENT = 1000'); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('expense_transaction_categories'); +}; diff --git a/packages/server-nest/src/database/migrations/20200713192127_create_sales_estimates_table.ts b/packages/server-nest/src/database/migrations/20200713192127_create_sales_estimates_table.ts new file mode 100644 index 000000000..06f8f1c91 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200713192127_create_sales_estimates_table.ts @@ -0,0 +1,35 @@ +exports.up = function (knex) { + return knex.schema.createTable('sales_estimates', (table) => { + table.increments(); + table.decimal('amount', 13, 3); + table.string('currency_code', 3); + table + .integer('customer_id') + .unsigned() + .index() + .references('id') + .inTable('contacts'); + table.date('estimate_date').index(); + table.date('expiration_date').index(); + table.string('reference'); + table.string('estimate_number').index(); + table.text('note'); + table.text('terms_conditions'); + table.text('send_to_email'); + + table.date('delivered_at').index(); + table.date('approved_at').index(); + table.date('rejected_at').index(); + + table.integer('user_id').unsigned().index(); + + table.integer('converted_to_invoice_id').unsigned(); + table.date('converted_to_invoice_at'); + + table.timestamps(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('sales_estimates'); +}; diff --git a/packages/server-nest/src/database/migrations/20200713213303_create_sales_receipt_table.ts b/packages/server-nest/src/database/migrations/20200713213303_create_sales_receipt_table.ts new file mode 100644 index 000000000..c1f093312 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200713213303_create_sales_receipt_table.ts @@ -0,0 +1,22 @@ + +exports.up = function(knex) { + return knex.schema.createTable('sales_receipts', table => { + table.increments(); + table.decimal('amount', 13, 3); + table.string('currency_code', 3); + table.integer('deposit_account_id').unsigned().index().references('id').inTable('accounts'); + table.integer('customer_id').unsigned().index().references('id').inTable('contacts'); + table.date('receipt_date').index(); + table.string('receipt_number').index(); + table.string('reference_no').index(); + table.string('send_to_email'); + table.text('receipt_message'); + table.text('statement'); + table.date('closed_at').index(); + table.timestamps(); + }) +}; + +exports.down = function(knex) { + return knex.schema.dropTableIfExists('sales_receipts'); +}; diff --git a/packages/server-nest/src/database/migrations/20200715193633_create_sale_invoices_table.ts b/packages/server-nest/src/database/migrations/20200715193633_create_sale_invoices_table.ts new file mode 100644 index 000000000..87c26f58c --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200715193633_create_sale_invoices_table.ts @@ -0,0 +1,34 @@ +exports.up = function (knex) { + return knex.schema.createTable('sales_invoices', (table) => { + table.increments(); + table + .integer('customer_id') + .unsigned() + .index() + .references('id') + .inTable('contacts'); + + table.date('invoice_date').index(); + table.date('due_date'); + table.string('invoice_no').index(); + table.string('reference_no'); + + table.text('invoice_message'); + table.text('terms_conditions'); + + table.decimal('balance', 13, 3); + table.decimal('payment_amount', 13, 3); + table.string('currency_code', 3); + table.decimal('credited_amount', 13, 3).defaultTo(0); + + table.string('inv_lot_number').index(); + + table.date('delivered_at').index(); + table.integer('user_id').unsigned(); + table.timestamps(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('sales_invoices'); +}; diff --git a/packages/server-nest/src/database/migrations/20200715194514_create_payment_receives_table.ts b/packages/server-nest/src/database/migrations/20200715194514_create_payment_receives_table.ts new file mode 100644 index 000000000..c9a73ba13 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200715194514_create_payment_receives_table.ts @@ -0,0 +1,30 @@ +const { knexSnakeCaseMappers } = require('objection'); + +exports.up = function (knex) { + return knex.schema.createTable('payment_receives', (table) => { + table.increments(); + table + .integer('customer_id') + .unsigned() + .index() + .references('id') + .inTable('contacts'); + table.date('payment_date').index(); + table.decimal('amount', 13, 3).defaultTo(0); + table.string('currency_code', 3); + table.string('reference_no').index(); + table + .integer('deposit_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table.string('payment_receive_no').nullable(); + table.text('statement'); + table.integer('user_id').unsigned().index(); + table.timestamps(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('payment_receives'); +}; diff --git a/packages/server-nest/src/database/migrations/20200718161031_create_payment_receives_entries_table.ts b/packages/server-nest/src/database/migrations/20200718161031_create_payment_receives_entries_table.ts new file mode 100644 index 000000000..d4c68a270 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200718161031_create_payment_receives_entries_table.ts @@ -0,0 +1,23 @@ +exports.up = function (knex) { + return knex.schema.createTable('payment_receives_entries', (table) => { + table.increments(); + table + .integer('payment_receive_id') + .unsigned() + .index() + .references('id') + .inTable('payment_receives'); + table + .integer('invoice_id') + .unsigned() + .index() + .references('id') + .inTable('sales_invoices'); + table.decimal('payment_amount', 13, 3).unsigned(); + table.integer('index').unsigned(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('payment_receives_entries'); +}; diff --git a/packages/server-nest/src/database/migrations/20200719152005_create_bills_table.ts b/packages/server-nest/src/database/migrations/20200719152005_create_bills_table.ts new file mode 100644 index 000000000..cac0e04cd --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200719152005_create_bills_table.ts @@ -0,0 +1,31 @@ +exports.up = function (knex) { + return knex.schema.createTable('bills', (table) => { + table.increments(); + table + .integer('vendor_id') + .unsigned() + .index() + .references('id') + .inTable('contacts'); + table.string('bill_number'); + table.date('bill_date').index(); + table.date('due_date').index(); + table.string('reference_no').index(); + table.string('status').index(); + table.text('note'); + table.decimal('amount', 13, 3).defaultTo(0); + table.string('currency_code'); + table.decimal('payment_amount', 13, 3).defaultTo(0); + table.decimal('landed_cost_amount', 13, 3).defaultTo(0); + table.decimal('allocated_cost_amount', 13, 3).defaultTo(0); + table.decimal('credited_amount', 13, 3).defaultTo(0); + table.string('inv_lot_number').index(); + table.date('opened_at').index(); + table.integer('user_id').unsigned(); + table.timestamps(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('bills'); +}; diff --git a/packages/server-nest/src/database/migrations/20200719153909_create_bills_payments_table.ts b/packages/server-nest/src/database/migrations/20200719153909_create_bills_payments_table.ts new file mode 100644 index 000000000..568302f3e --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200719153909_create_bills_payments_table.ts @@ -0,0 +1,21 @@ + +exports.up = function(knex) { + return knex.schema.createTable('bills_payments', table => { + table.increments(); + table.integer('vendor_id').unsigned().index().references('id').inTable('contacts'); + table.decimal('amount', 13, 3).defaultTo(0); + table.string('currency_code'); + table.integer('payment_account_id').unsigned().references('id').inTable('accounts'); + table.string('payment_number').nullable().index(); + table.date('payment_date').index(); + table.string('payment_method'); + table.string('reference'); + table.integer('user_id').unsigned().index(); + table.text('statement'); + table.timestamps(); + }); +}; + +exports.down = function(knex) { + +}; diff --git a/packages/server-nest/src/database/migrations/20200722164251_create_inventory_transactions_table.ts b/packages/server-nest/src/database/migrations/20200722164251_create_inventory_transactions_table.ts new file mode 100644 index 000000000..bf8de6184 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200722164251_create_inventory_transactions_table.ts @@ -0,0 +1,24 @@ +exports.up = function (knex) { + return knex.schema.createTable('inventory_transactions', (table) => { + table.increments('id'); + table.date('date').index(); + table.string('direction').index(); + table + .integer('item_id') + .unsigned() + .index() + .references('id') + .inTable('items'); + table.integer('quantity').unsigned(); + table.decimal('rate', 13, 3).unsigned(); + + table.string('transaction_type').index(); + table.integer('transaction_id').unsigned().index(); + + table.integer('entry_id').unsigned().index(); + table.integer('cost_account_id').unsigned(); + table.timestamps(); + }); +}; + +exports.down = function (knex) {}; diff --git a/packages/server-nest/src/database/migrations/20200722164252_create_landed_cost_table.ts b/packages/server-nest/src/database/migrations/20200722164252_create_landed_cost_table.ts new file mode 100644 index 000000000..f315e1bde --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200722164252_create_landed_cost_table.ts @@ -0,0 +1,21 @@ +exports.up = function (knex) { + return knex.schema.createTable('bill_located_costs', (table) => { + table.increments(); + + table.decimal('amount', 13, 3).unsigned(); + + table.integer('fromTransactionId').unsigned(); + table.string('fromTransactionType'); + table.integer('fromTransactionEntryId').unsigned(); + + table.string('allocationMethod'); + table.integer('costAccountId').unsigned(); + table.text('description'); + + table.integer('billId').unsigned(); + + table.timestamps(); + }); +}; + +exports.down = function (knex) {}; diff --git a/packages/server-nest/src/database/migrations/20200722164253_create_landed_cost_entries_table.ts b/packages/server-nest/src/database/migrations/20200722164253_create_landed_cost_entries_table.ts new file mode 100644 index 000000000..96cdc5d77 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200722164253_create_landed_cost_entries_table.ts @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.createTable('bill_located_cost_entries', (table) => { + table.increments(); + + table.decimal('cost', 13, 3).unsigned(); + table.integer('entry_id').unsigned(); + table.integer('bill_located_cost_id').unsigned(); + }); +}; + +exports.down = function (knex) {}; diff --git a/packages/server-nest/src/database/migrations/20200722164255_create_inventory_transaction_meta_table.ts b/packages/server-nest/src/database/migrations/20200722164255_create_inventory_transaction_meta_table.ts new file mode 100644 index 000000000..15f348a17 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200722164255_create_inventory_transaction_meta_table.ts @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.createTable('inventory_transaction_meta', (table) => { + table.increments('id'); + table.string('transaction_number'); + table.text('description'); + table.integer('inventory_transaction_id').unsigned(); + }); + }; + + exports.down = function (knex) {}; + \ No newline at end of file diff --git a/packages/server-nest/src/database/migrations/20200722173423_create_items_entries_table.ts b/packages/server-nest/src/database/migrations/20200722173423_create_items_entries_table.ts new file mode 100644 index 000000000..b480540de --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200722173423_create_items_entries_table.ts @@ -0,0 +1,39 @@ +exports.up = function (knex) { + return knex.schema.createTable('items_entries', (table) => { + table.increments(); + table.string('reference_type').index(); + table.string('reference_id').index(); + + table.integer('index').unsigned(); + table + .integer('item_id') + .unsigned() + .index() + .references('id') + .inTable('items'); + table.text('description'); + table.integer('discount').unsigned(); + table.integer('quantity').unsigned(); + table.integer('rate').unsigned(); + + table + .integer('sell_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table + .integer('cost_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + + table.boolean('landed_cost').defaultTo(false); + table.decimal('allocated_cost_amount', 13, 3).defaultTo(0); + + table.timestamps(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('items_entries'); +}; diff --git a/packages/server-nest/src/database/migrations/20200728161617_create_bill_payments_entries.ts b/packages/server-nest/src/database/migrations/20200728161617_create_bill_payments_entries.ts new file mode 100644 index 000000000..d08ac23bb --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200728161617_create_bill_payments_entries.ts @@ -0,0 +1,23 @@ +exports.up = function (knex) { + return knex.schema.createTable('bills_payments_entries', (table) => { + table.increments(); + table + .integer('bill_payment_id') + .unsigned() + .index() + .references('id') + .inTable('bills_payments'); + table + .integer('bill_id') + .unsigned() + .index() + .references('id') + .inTable('bills'); + table.decimal('payment_amount', 13, 3).unsigned(); + table.integer('index').unsigned(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('bills_payments_entries'); +}; diff --git a/packages/server-nest/src/database/migrations/20200810121807_create_inventory_cost_lot_tracker_table.ts b/packages/server-nest/src/database/migrations/20200810121807_create_inventory_cost_lot_tracker_table.ts new file mode 100644 index 000000000..d490cbcc7 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200810121807_create_inventory_cost_lot_tracker_table.ts @@ -0,0 +1,26 @@ +exports.up = function (knex) { + return knex.schema.createTable('inventory_cost_lot_tracker', (table) => { + table.increments(); + table.date('date').index(); + table.string('direction').index(); + + table.integer('item_id').unsigned().index(); + table.integer('quantity').unsigned().index(); + table.decimal('rate', 13, 3); + table.integer('remaining'); + table.decimal('cost', 13, 3); + + table.string('transaction_type').index(); + table.integer('transaction_id').unsigned().index(); + + table.integer('entry_id').unsigned().index(); + table.integer('cost_account_id').unsigned(); + table.integer('inventory_transaction_id').unsigned().index(); + + table.datetime('created_at').index(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('inventory_cost_lot_tracker'); +}; diff --git a/packages/server-nest/src/database/migrations/20200810121809_create_inventory_adjustments_table.ts b/packages/server-nest/src/database/migrations/20200810121809_create_inventory_adjustments_table.ts new file mode 100644 index 000000000..4774fcd23 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200810121809_create_inventory_adjustments_table.ts @@ -0,0 +1,19 @@ + +exports.up = function(knex) { + return knex.schema.createTable('inventory_adjustments', table => { + table.increments(); + table.date('date').index(); + table.string('type').index(); + table.integer('adjustment_account_id').unsigned().references('id').inTable('accounts'); + table.string('reason'); + table.string('reference_no').index(); + table.string('description'); + table.integer('user_id').unsigned(); + table.date('published_at'); + table.timestamps(); + }); +}; + +exports.down = function(knex) { + return knex.schema.dropTableIfExists('inventory_adjustments'); +}; diff --git a/packages/server-nest/src/database/migrations/20200810121810_create_inventory_adjustments_entries_table.ts b/packages/server-nest/src/database/migrations/20200810121810_create_inventory_adjustments_entries_table.ts new file mode 100644 index 000000000..c40f877e2 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200810121810_create_inventory_adjustments_entries_table.ts @@ -0,0 +1,25 @@ +exports.up = function (knex) { + return knex.schema.createTable('inventory_adjustments_entries', (table) => { + table.increments(); + table + .integer('adjustment_id') + .unsigned() + .index() + .references('id') + .inTable('inventory_adjustments'); + table.integer('index').unsigned(); + table + .integer('item_id') + .unsigned() + .index() + .references('id') + .inTable('items'); + table.integer('quantity'); + table.decimal('cost', 13, 3).unsigned(); + table.decimal('value', 13, 3).unsigned(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('inventory_adjustments_entries'); +}; diff --git a/packages/server-nest/src/database/migrations/20200810121910_create_cashflow_transactions_table.ts b/packages/server-nest/src/database/migrations/20200810121910_create_cashflow_transactions_table.ts new file mode 100644 index 000000000..22914346a --- /dev/null +++ b/packages/server-nest/src/database/migrations/20200810121910_create_cashflow_transactions_table.ts @@ -0,0 +1,18 @@ +exports.up = (knex) => { + return knex.schema.createTable('cashflow_transactions', (table) => { + table.increments(); + table.date('date').index(); + table.decimal('amount', 13, 3); + table.string('reference_no').index(); + table.string('transaction_type').index(); + table.string('transaction_number').index(); + table.string('description'); + table.date('published_at').index(); + table.integer('user_id').unsigned().index(); + table.timestamps(); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTableIfExists('cashflow_transactions'); +}; diff --git a/packages/server-nest/src/database/migrations/20210810121910_create_cashflow_transaction_lines_table.ts b/packages/server-nest/src/database/migrations/20210810121910_create_cashflow_transaction_lines_table.ts new file mode 100644 index 000000000..79a47e690 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20210810121910_create_cashflow_transaction_lines_table.ts @@ -0,0 +1,23 @@ +exports.up = (knex) => { + return knex.schema.createTable('cashflow_transaction_lines', (table) => { + table.increments(); + table.integer('cashflow_transaction_id').unsigned(); + table + .integer('cashflow_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table + .integer('credit_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table.decimal('amount', 13, 3); + table.integer('index').unsigned(); + table.timestamps(); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTableIfExists('cashflow_transaction_lines'); +}; diff --git a/packages/server-nest/src/database/migrations/20210910121910_add_invoices_writtenoff_columns.ts b/packages/server-nest/src/database/migrations/20210910121910_add_invoices_writtenoff_columns.ts new file mode 100644 index 000000000..8ce84a105 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20210910121910_add_invoices_writtenoff_columns.ts @@ -0,0 +1,13 @@ +exports.up = (knex) => { + return knex.schema.table('sales_invoices', (table) => { + table.decimal('writtenoff_amount', 13, 3); + table.date('writtenoff_at').index(); + }); +}; + +exports.down = (knex) => { + return knex.schema.table('sales_invoices', (table) => { + table.dropColumn('writtenoff_amount'); + table.dropColumn('writtenoff_at'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20211012121910_add_costable_column_to_account_transactions.ts b/packages/server-nest/src/database/migrations/20211012121910_add_costable_column_to_account_transactions.ts new file mode 100644 index 000000000..7594fae97 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20211012121910_add_costable_column_to_account_transactions.ts @@ -0,0 +1,11 @@ +exports.up = (knex) => { + return knex.schema.table('accounts_transactions', (table) => { + table.boolean('costable'); + }); +}; + +exports.down = (knex) => { + return knex.schema.table('accounts_transactions', (table) => { + table.dropColumn('costable'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20211014121910_add_roles_table.ts b/packages/server-nest/src/database/migrations/20211014121910_add_roles_table.ts new file mode 100644 index 000000000..e9d8a0775 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20211014121910_add_roles_table.ts @@ -0,0 +1,21 @@ +exports.up = (knex) => { + return knex.schema + .createTable('roles', (table) => { + table.increments('id'); + table.string('name', 255).notNullable(); + table.string('slug'); + table.text('description'); + table.boolean('predefined'); + }) + .createTable('role_permissions', (table) => { + table.increments('id'); + table.integer('role_id').unsigned().references('id').inTable('roles'); + table.string('subject'); + table.string('ability'); + table.boolean('value'); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTable('roles').dropTable('role_permissions'); +}; diff --git a/packages/server-nest/src/database/migrations/20211112121920_create_users_table.ts b/packages/server-nest/src/database/migrations/20211112121920_create_users_table.ts new file mode 100644 index 000000000..a5df01941 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20211112121920_create_users_table.ts @@ -0,0 +1,19 @@ +exports.up = (knex) => { + return knex.schema.createTable('users', (table) => { + table.increments(); + table.string('first_name'); + table.string('last_name'); + table.string('email').index(); + table.string('phone_number').index(); + table.boolean('active').index(); + table.integer('role_id').unsigned().references('id').inTable('roles'); + table.integer('system_user_id').unsigned(); + table.dateTime('invited_at').index(); + table.dateTime('invite_accepted_at').index(); + table.timestamps(); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTableIfExists('users'); +}; diff --git a/packages/server-nest/src/database/migrations/20211122121920_create_credit_notes_table.ts b/packages/server-nest/src/database/migrations/20211122121920_create_credit_notes_table.ts new file mode 100644 index 000000000..07aa28a65 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20211122121920_create_credit_notes_table.ts @@ -0,0 +1,28 @@ +exports.up = (knex) => { + return knex.schema.createTable('credit_notes', (table) => { + table.increments(); + table + .integer('customer_id') + .unsigned() + .references('id') + .inTable('contacts'); + table.date('credit_note_date'); + table.string('credit_note_number'); + table.string('reference_no'); + table.decimal('amount', 13, 3); + + table.decimal('refunded_amount', 13, 3).defaultTo(0); + table.decimal('invoices_amount', 13, 3).defaultTo(0); + + table.string('currency_code', 3); + table.text('note'); + table.text('terms_conditions'); + table.date('opened_at').index(); + table.integer('user_id').unsigned().references('id').inTable('users'); + table.timestamps(); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTableIfExists('credit_notescredit_notes'); +}; diff --git a/packages/server-nest/src/database/migrations/20211122121920_create_vendor_credits_table.ts b/packages/server-nest/src/database/migrations/20211122121920_create_vendor_credits_table.ts new file mode 100644 index 000000000..0e4e361f8 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20211122121920_create_vendor_credits_table.ts @@ -0,0 +1,23 @@ +exports.up = (knex) => { + return knex.schema.createTable('vendor_credits', (table) => { + table.increments(); + table.integer('vendor_id').unsigned().references('id').inTable('contacts'); + table.decimal('amount', 13, 3); + table.string('currency_code', 3); + table.date('vendor_credit_date'); + table.string('vendor_credit_number'); + table.string('reference_no'); + + table.decimal('refunded_amount', 13, 3).defaultTo(0); + table.decimal('invoiced_amount', 13, 3).defaultTo(0); + + table.text('note'); + table.date('opened_at').index(); + table.integer('user_id').unsigned().references('id').inTable('users'); + table.timestamps(); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTableIfExists('vendor_credits'); +}; diff --git a/packages/server-nest/src/database/migrations/20211123121920_create_refund_transactions_table.ts b/packages/server-nest/src/database/migrations/20211123121920_create_refund_transactions_table.ts new file mode 100644 index 000000000..233657be2 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20211123121920_create_refund_transactions_table.ts @@ -0,0 +1,45 @@ +exports.up = (knex) => { + return knex.schema + .createTable('refund_credit_note_transactions', (table) => { + table.increments(); + table.date('date'); + table + .integer('credit_note_id') + .unsigned() + .references('id') + .inTable('credit_notes'); + table.decimal('amount', 13, 3); + table.string('currency_code', 3); + table.string('reference_no'); + table + .integer('from_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table.text('description'); + table.timestamps(); + }) + .createTable('refund_vendor_credit_transactions', (table) => { + table.increments(); + table.date('date'); + table + .integer('vendor_credit_id') + .unsigned() + .references('id') + .inTable('vendor_credits'); + table.decimal('amount', 13, 3); + table.string('currency_code', 3); + table.string('reference_no'); + table + .integer('deposit_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table.text('description'); + table.timestamps(); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTableIfExists('refund_transactions'); +}; diff --git a/packages/server-nest/src/database/migrations/20211124121920_create_credit_note_applies_invoices.ts b/packages/server-nest/src/database/migrations/20211124121920_create_credit_note_applies_invoices.ts new file mode 100644 index 000000000..678a52bf2 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20211124121920_create_credit_note_applies_invoices.ts @@ -0,0 +1,35 @@ +exports.up = (knex) => { + return knex.schema + .createTable('credit_note_applied_invoice', (table) => { + table.increments(); + table.decimal('amount', 13, 3); + table + .integer('credit_note_id') + .unsigned() + .references('id') + .inTable('credit_notes'); + table + .integer('invoice_id') + .unsigned() + .references('id') + .inTable('sales_invoices'); + table.timestamps(); + }) + .createTable('vendor_credit_applied_bill', (table) => { + table.increments(); + table.decimal('amount', 13, 3); + table + .integer('vendor_credit_id') + .unsigned() + .references('id') + .inTable('vendor_credits'); + table.integer('bill_id').unsigned().references('id').inTable('bills'); + table.timestamps(); + }); +}; + +exports.down = (knex) => { + return knex.schema + .dropTableIfExists('vendor_credit_applied_bill') + .dropTableIfExists('credit_note_applied_invoice'); +}; diff --git a/packages/server-nest/src/database/migrations/20220124121920_create_branches_table.ts b/packages/server-nest/src/database/migrations/20220124121920_create_branches_table.ts new file mode 100644 index 000000000..de2b75261 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220124121920_create_branches_table.ts @@ -0,0 +1,24 @@ +exports.up = (knex) => { + return knex.schema.createTable('branches', (table) => { + table.increments(); + + table.string('name'); + table.string('code'); + + table.string('address'); + table.string('city'); + table.string('country'); + + table.string('phone_number'); + table.string('email'); + table.string('website'); + + table.boolean('primary'); + + table.timestamps(); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTableIfExists('branches'); +}; diff --git a/packages/server-nest/src/database/migrations/20220124121920_create_warehouses_table.ts b/packages/server-nest/src/database/migrations/20220124121920_create_warehouses_table.ts new file mode 100644 index 000000000..b6617857e --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220124121920_create_warehouses_table.ts @@ -0,0 +1,59 @@ +exports.up = (knex) => { + return knex.schema + .createTable('warehouses', (table) => { + table.increments(); + table.string('name'); + table.string('code'); + + table.string('address'); + table.string('city'); + table.string('country'); + + table.string('phone_number'); + table.string('email'); + table.string('website'); + + table.boolean('primary'); + + table.timestamps(); + }) + .createTable('warehouses_transfers', (table) => { + table.increments(); + table.date('date'); + table + .integer('to_warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + table + .integer('from_warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + table.string('transaction_number'); + + table.date('transfer_initiated_at'); + table.date('transfer_delivered_at'); + + table.timestamps(); + }) + .createTable('warehouses_transfers_entries', (table) => { + table.increments(); + table.integer('index'); + table + .integer('warehouse_transfer_id') + .unsigned() + .references('id') + .inTable('warehouses_transfers'); + table.integer('item_id').unsigned().references('id').inTable('items'); + table.string('description'); + table.integer('quantity'); + table.decimal('cost'); + }); +}; + +exports.down = (knex) => { + return knex.schema + .dropTableIfExists('vendor_credit_applied_bill') + .dropTableIfExists('credit_note_applied_invoice'); +}; diff --git a/packages/server-nest/src/database/migrations/20220125021920_create_items_warehouses_quantity.ts b/packages/server-nest/src/database/migrations/20220125021920_create_items_warehouses_quantity.ts new file mode 100644 index 000000000..899db5e2b --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220125021920_create_items_warehouses_quantity.ts @@ -0,0 +1,16 @@ +exports.up = (knex) => { + return knex.schema.createTable('items_warehouses_quantity', (table) => { + table.integer('item_id').unsigned().references('id').inTable('items'); + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + + table.integer('quantity_on_hand'); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTableIfExists('items_warehouses_quantity'); +}; diff --git a/packages/server-nest/src/database/migrations/20220125121920_add_branch_column_to_accounts_transactions.ts b/packages/server-nest/src/database/migrations/20220125121920_add_branch_column_to_accounts_transactions.ts new file mode 100644 index 000000000..ba7ab26c1 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220125121920_add_branch_column_to_accounts_transactions.ts @@ -0,0 +1,86 @@ +exports.up = (knex) => { + return knex.schema + .table('accounts_transactions', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }) + .table('manual_journals', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }) + .table('manual_journals_entries', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }) + .table('expenses_transactions', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches') + .after('user_id'); + }) + .table('cashflow_transactions', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches') + .after('user_id'); + }) + .table('contacts', (table) => { + table + .integer('opening_balance_branch_id') + .unsigned() + .references('id') + .inTable('branches') + .after('opening_balance_at'); + }) + .table('refund_credit_note_transactions', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches') + .after('description'); + }) + .table('refund_vendor_credit_transactions', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches') + .after('description'); + }); +}; + +exports.down = (knex) => { + return knex.schema + .table('accounts_transactions', (table) => { + table.dropColumn('branch_id'); + }) + .table('manual_journals', (table) => { + table.dropColumn('branch_id'); + }) + .table('manual_journals_entries', (table) => { + table.dropColumn('branch_id'); + }) + .table('cashflow_transactions', (table) => { + table.dropColumn('branch_id'); + }) + .table('refund_credit_note_transactions', (table) => { + table.dropColumn('branch_id'); + }) + .table('refund_vendor_credit_transactions', (table) => { + table.dropColumn('branch_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20220125121920_add_branch_warehouse_columns_to_purchases.ts b/packages/server-nest/src/database/migrations/20220125121920_add_branch_warehouse_columns_to_purchases.ts new file mode 100644 index 000000000..7ce87115c --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220125121920_add_branch_warehouse_columns_to_purchases.ts @@ -0,0 +1,65 @@ +exports.up = (knex) => { + return knex.schema + .table('bills', (table) => { + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }) + .table('bills_payments', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }) + .table('vendor_credits', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + }) + .table('inventory_adjustments', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + }); +}; + +exports.down = (knex) => { + return knex.schema + .table('bills', (table) => { + table.dropColumn('warehouse_id'); + table.dropColumn('branch_id'); + }) + .table('bills_payments', (table) => { + table.dropColumn('branch_id'); + }) + .table('vendor_credits', (table) => { + table.dropColumn('branch_id'); + table.dropColumn('warehouse_id'); + }) + .table('inventory_adjustments', (table) => { + table.dropColumn('branch_id'); + table.dropColumn('warehouse_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20220125121920_add_branch_warehouse_columns_to_sales.ts b/packages/server-nest/src/database/migrations/20220125121920_add_branch_warehouse_columns_to_sales.ts new file mode 100644 index 000000000..b209040e4 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220125121920_add_branch_warehouse_columns_to_sales.ts @@ -0,0 +1,84 @@ +exports.up = (knex) => { + return knex.schema + .table('sales_invoices', (table) => { + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }) + .table('sales_estimates', (table) => { + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }) + .table('sales_receipts', (table) => { + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }) + .table('payment_receives', (table) => { + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }) + .table('credit_notes', (table) => { + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }); +}; + +exports.down = (knex) => { + return knex.schema + .table('sales_invoices', (table) => { + table.dropColumn('warehouse_id'); + table.dropColumn('branch_id'); + }) + .table('sales_estimates', (table) => { + table.dropColumn('warehouse_id'); + table.dropColumn('branch_id'); + }) + .table('sales_receipts', (table) => { + table.dropColumn('warehouse_id'); + table.dropColumn('branch_id'); + }) + .table('payment_receives', (table) => { + table.dropColumn('branch_id'); + }) + .table('credit_notes', (table) => { + table.dropColumn('warehouse_id'); + table.dropColumn('branch_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20220125121920_add_warehouse_column_to_inventory_transactions.ts b/packages/server-nest/src/database/migrations/20220125121920_add_warehouse_column_to_inventory_transactions.ts new file mode 100644 index 000000000..622961f22 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220125121920_add_warehouse_column_to_inventory_transactions.ts @@ -0,0 +1,40 @@ +exports.up = (knex) => { + return knex.schema + .table('inventory_transactions', (table) => { + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }) + .table('inventory_cost_lot_tracker', (table) => { + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + + table + .integer('branch_id') + .unsigned() + .references('id') + .inTable('branches'); + }); +}; + +exports.down = (knex) => { + return knex.schema + .table('inventory_transactions', (table) => { + table.dropColumn('warehouse_id'); + table.dropColumn('branch_id'); + }) + .table('inventory_cost_lot_tracker', (table) => { + table.dropColumn('warehouse_id'); + table.dropColumn('branch_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20220125121920_add_warehouse_column_to_items_entries.ts b/packages/server-nest/src/database/migrations/20220125121920_add_warehouse_column_to_items_entries.ts new file mode 100644 index 000000000..bac471bfe --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220125121920_add_warehouse_column_to_items_entries.ts @@ -0,0 +1,15 @@ +exports.up = (knex) => { + return knex.schema.table('items_entries', (table) => { + table + .integer('warehouse_id') + .unsigned() + .references('id') + .inTable('warehouses'); + }); +}; + +exports.down = (knex) => { + return knex.schema.table('items_entries', (table) => { + table.dropColumn('warehouse_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20220128121920_add_exchange_rate_to_transactions.ts b/packages/server-nest/src/database/migrations/20220128121920_add_exchange_rate_to_transactions.ts new file mode 100644 index 000000000..86c7836dc --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220128121920_add_exchange_rate_to_transactions.ts @@ -0,0 +1,114 @@ +exports.up = (knex) => { + return knex.schema + .table('sales_invoices', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('sales_estimates', (table) => { + table.decimal('exchange_rate', 13, 9); + }) + .table('sales_receipts', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('payment_receives', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('bills', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('bills_payments', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('credit_notes', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('vendor_credits', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('accounts_transactions', (table) => { + table.string('currency_code', 3).after('debit'); + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('manual_journals', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('cashflow_transactions', (table) => { + table.string('currency_code', 3).after('amount'); + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('expenses_transactions', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('refund_credit_note_transactions', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('refund_vendor_credit_transactions', (table) => { + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('bill_located_costs', (table) => { + table.string('currency_code', 3).after('amount'); + table.decimal('exchange_rate', 13, 9).after('currency_code'); + }) + .table('contacts', (table) => { + table + .decimal('opening_balance_exchange_rate', 13, 9) + .after('opening_balance_at'); + }) + .table('items', (table) => { + table.dropColumn('currency_code'); + }); +}; + +exports.down = (knex) => { + return knex.schema + .table('sales_invoices', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('sales_estimates', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('sales_receipts', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('payment_receives', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('bills', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('bills_payments', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('credit_notes', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('vendor_credits', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('accounts_transactions', (table) => { + table.dropColumn('currency_code'); + table.dropColumn('exchange_rate'); + }) + .table('manual_journals', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('cashflow_transactions', (table) => { + table.dropColumn('currency_code'); + table.dropColumn('exchange_rate'); + }) + .table('expenses_transactions', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('refund_credit_note_transactions', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('refund_vendor_credit_transactions', (table) => { + table.dropColumn('exchange_rate'); + }) + .table('bill_located_costs', (table) => { + table.dropColumn('currency_code'); + table.dropColumn('exchange_rate'); + }) + .table('contacts', (table) => { + table.dropColumn('opening_balance_exchange_rate'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20220129121920_add_writtenoff_expense_account_to_invoices.ts b/packages/server-nest/src/database/migrations/20220129121920_add_writtenoff_expense_account_to_invoices.ts new file mode 100644 index 000000000..4f496cd11 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220129121920_add_writtenoff_expense_account_to_invoices.ts @@ -0,0 +1,15 @@ +exports.up = (knex) => { + return knex.schema.table('sales_invoices', (table) => { + table + .integer('writtenoff_expense_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + }); +}; + +exports.down = (knex) => { + return knex.schema.table('sales_invoices', (table) => { + table.dropColumn('writtenoff_expense_account_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20220229121920_rename_contacts_shipping_billing_addresses.ts b/packages/server-nest/src/database/migrations/20220229121920_rename_contacts_shipping_billing_addresses.ts new file mode 100644 index 000000000..117086ef0 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220229121920_rename_contacts_shipping_billing_addresses.ts @@ -0,0 +1,19 @@ +exports.up = (knex) => { + return knex.schema + .raw( + 'ALTER TABLE CONTACTS CHANGE SHIPPING_ADDRESS_1 SHIPPING_ADDRESS1 VARCHAR(255)' + ) + .raw( + 'ALTER TABLE CONTACTS CHANGE SHIPPING_ADDRESS_2 SHIPPING_ADDRESS2 VARCHAR(255)' + ) + .raw( + 'ALTER TABLE CONTACTS CHANGE BILLING_ADDRESS_1 BILLING_ADDRESS1 VARCHAR(255)' + ) + .raw( + 'ALTER TABLE CONTACTS CHANGE BILLING_ADDRESS_2 BILLING_ADDRESS2 VARCHAR(255)' + ); +}; + +exports.down = (knex) => { + return knex.schema.table('contacts', (table) => {}); +}; diff --git a/packages/server-nest/src/database/migrations/20220329121920_add_cashflow_credit_account.ts b/packages/server-nest/src/database/migrations/20220329121920_add_cashflow_credit_account.ts new file mode 100644 index 000000000..4bc18796f --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220329121920_add_cashflow_credit_account.ts @@ -0,0 +1,18 @@ +exports.up = (knex) => { + return knex.schema.table('cashflow_transactions', (table) => { + table + .integer('cashflow_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table + .integer('credit_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + }); +}; + +exports.down = (knex) => { + return knex.schema.table('cashflow_transactions', () => {}); +}; diff --git a/packages/server-nest/src/database/migrations/20220329121920_add_seed_at_column_to_accounts.ts b/packages/server-nest/src/database/migrations/20220329121920_add_seed_at_column_to_accounts.ts new file mode 100644 index 000000000..257602b05 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220329121920_add_seed_at_column_to_accounts.ts @@ -0,0 +1,7 @@ +exports.up = (knex) => { + return knex.schema.table('accounts', (table) => { + table.date('seeded_at').after('currency_code').nullable(); + }); +}; + +exports.down = (knex) => {}; diff --git a/packages/server-nest/src/database/migrations/20220429121920_create_projects_table.ts b/packages/server-nest/src/database/migrations/20220429121920_create_projects_table.ts new file mode 100644 index 000000000..96518246f --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220429121920_create_projects_table.ts @@ -0,0 +1,93 @@ +exports.up = (knex) => { + return knex.schema + .createTable('projects', (table) => { + table.increments('id').comment('Auto-generated id'); + table.string('name'); + table.integer('contact_id').unsigned(); + table.date('deadline'); + table.decimal('cost_estimate'); + table.string('status'); + table.timestamps(); + }) + .createTable('tasks', (table) => { + table.increments('id').comment('Auto-generated id'); + table.string('name'); + table.string('charge_type'); + table.decimal('rate'); + table.decimal('estimate_hours').unsigned(); + table.decimal('actual_hours').unsigned(); + table.decimal('invoiced_hours').unsigned().default(0); + table + .integer('project_id') + .unsigned() + .references('id') + .inTable('projects'); + table.timestamps(); + }) + .createTable('times', (table) => { + table.increments('id').comment('Auto-generated id'); + table.integer('duration').unsigned(); + table.string('description'); + table.date('date'); + + table.integer('taskId').unsigned().references('id').inTable('tasks'); + table + .integer('project_id') + .unsigned() + .references('id') + .inTable('projects'); + table.timestamps(); + }) + .table('accounts_transactions', (table) => { + table + .integer('projectId') + .unsigned() + .references('id') + .inTable('projects'); + }) + .table('manual_journals_entries', (table) => { + table + .integer('projectId') + .unsigned() + .references('id') + .inTable('projects'); + }) + .table('bills', (table) => { + table + .integer('projectId') + .unsigned() + .references('id') + .inTable('projects'); + table.decimal('invoiced_amount').unsigned().defaultTo(0); + }) + .table('items_entries', (table) => { + table + .integer('projectId') + .unsigned() + .references('id') + .inTable('projects'); + + table.integer('project_ref_id').unsigned(); + table.string('project_ref_type'); + table.decimal('project_ref_invoiced_amount').unsigned().defaultTo(0); + }) + .table('sales_invoices', (table) => { + table + .integer('projectId') + .unsigned() + .references('id') + .inTable('projects'); + }) + .table('expenses_transactions', (table) => { + table + .integer('projectId') + .unsigned() + .references('id') + .inTable('projects'); + table.decimal('invoiced_amount').unsigned().defaultTo(0); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTable('tasks'); +}; diff --git a/packages/server-nest/src/database/migrations/20220429121922_add_project_id_to_expense_lines.ts b/packages/server-nest/src/database/migrations/20220429121922_add_project_id_to_expense_lines.ts new file mode 100644 index 000000000..7d3931f35 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20220429121922_add_project_id_to_expense_lines.ts @@ -0,0 +1,7 @@ +exports.up = (knex) => { + return knex.schema.table('expense_transaction_categories', (table) => { + table.integer('projectId').unsigned().references('id').inTable('projects'); + }); +}; + +exports.down = (knex) => {}; diff --git a/packages/server-nest/src/database/migrations/20230405232607_drop_phone_number_from_users.ts b/packages/server-nest/src/database/migrations/20230405232607_drop_phone_number_from_users.ts new file mode 100644 index 000000000..9ab142779 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20230405232607_drop_phone_number_from_users.ts @@ -0,0 +1,9 @@ +exports.up = function (knex) { + return knex.schema.table('users', (table) => { + table.dropColumn('phone_number'); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('users', (table) => {}); +}; diff --git a/packages/server-nest/src/database/migrations/20230810191606_create_tax_rates.ts b/packages/server-nest/src/database/migrations/20230810191606_create_tax_rates.ts new file mode 100644 index 000000000..881a9db31 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20230810191606_create_tax_rates.ts @@ -0,0 +1,52 @@ +exports.up = (knex) => { + return knex.schema + .createTable('tax_rates', (table) => { + table.increments(); + table.string('name'); + table.string('code'); + table.decimal('rate'); + table.string('description'); + table.boolean('is_non_recoverable').defaultTo(false); + table.boolean('is_compound').defaultTo(false); + table.boolean('active').defaultTo(false); + table.date('deleted_at'); + table.timestamps(); + }) + .table('items_entries', (table) => { + table.boolean('is_inclusive_tax').defaultTo(false); + table + .integer('tax_rate_id') + .unsigned() + .references('id') + .inTable('tax_rates'); + table.decimal('tax_rate').unsigned(); + }) + .table('sales_invoices', (table) => { + table.boolean('is_inclusive_tax').defaultTo(false); + table.decimal('tax_amount_withheld'); + }) + .createTable('tax_rate_transactions', (table) => { + table.increments('id'); + table + .integer('tax_rate_id') + .unsigned() + .references('id') + .inTable('tax_rates'); + table.string('reference_type'); + table.integer('reference_id'); + table.decimal('rate').unsigned(); + table.integer('tax_account_id').unsigned(); + }) + .table('accounts_transactions', (table) => { + table + .integer('tax_rate_id') + .unsigned() + .references('id') + .inTable('tax_rates'); + table.decimal('tax_rate').unsigned(); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTableIfExists('tax_rates'); +}; diff --git a/packages/server-nest/src/database/migrations/20231004012644_add_tax_amount_withheld_to_bills_table.ts b/packages/server-nest/src/database/migrations/20231004012644_add_tax_amount_withheld_to_bills_table.ts new file mode 100644 index 000000000..f61655877 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20231004012644_add_tax_amount_withheld_to_bills_table.ts @@ -0,0 +1,10 @@ +exports.up = (knex) => { + return knex.schema.table('bills', (table) => { + table.boolean('is_inclusive_tax').defaultTo(false); + table.decimal('tax_amount_withheld'); + }); +}; + +exports.down = (knex) => { + return knex.schema.table('bills', () => {}); +}; diff --git a/packages/server-nest/src/database/migrations/20231004020636_add_sell_purchase_tax_to_items_table.ts b/packages/server-nest/src/database/migrations/20231004020636_add_sell_purchase_tax_to_items_table.ts new file mode 100644 index 000000000..51b842df2 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20231004020636_add_sell_purchase_tax_to_items_table.ts @@ -0,0 +1,18 @@ +exports.up = (knex) => { + return knex.schema.table('items', (table) => { + table + .integer('sell_tax_rate_id') + .unsigned() + .references('id') + .inTable('tax_rates'); + table + .integer('purchase_tax_rate_id') + .unsigned() + .references('id') + .inTable('tax_rates'); + }); +}; + +exports.down = (knex) => { + return knex.schema.dropTableIfExists('tax_rates'); +}; diff --git a/packages/server-nest/src/database/migrations/20231108170207_create_storage_table.ts b/packages/server-nest/src/database/migrations/20231108170207_create_storage_table.ts new file mode 100644 index 000000000..0bf9e8ef9 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20231108170207_create_storage_table.ts @@ -0,0 +1,14 @@ +exports.up = function (knex) { + return knex.schema.createTable('storage', (table) => { + table.increments('id').primary(); + table.string('key').notNullable(); + table.string('path').notNullable(); + table.string('extension').notNullable(); + table.integer('expire_in'); + table.timestamps(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('storage'); +}; \ No newline at end of file diff --git a/packages/server-nest/src/database/migrations/20231202124014_change_item_entries_rate_to_float.ts b/packages/server-nest/src/database/migrations/20231202124014_change_item_entries_rate_to_float.ts new file mode 100644 index 000000000..802440045 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20231202124014_change_item_entries_rate_to_float.ts @@ -0,0 +1,9 @@ +exports.up = function (knex) { + return knex.schema.alterTable('items_entries', (table) => { + table.decimal('rate', 15, 5).alter(); + }); +}; + +exports.down = function (knex) { + return knex.table('items_entries', (table) => {}); +}; diff --git a/packages/server-nest/src/database/migrations/20240201160214_create_plaid_items_Table.ts b/packages/server-nest/src/database/migrations/20240201160214_create_plaid_items_Table.ts new file mode 100644 index 000000000..089283290 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240201160214_create_plaid_items_Table.ts @@ -0,0 +1,14 @@ +exports.up = function (knex) { + return knex.schema.createTable('plaid_items', (table) => { + table.increments('id'); + table.integer('tenant_id').unsigned(); + table.string('plaid_item_id'); + table.string('plaid_institution_id'); + table.string('plaid_access_token'); + table.string('last_cursor'); + table.string('status'); + table.timestamps(); + }); +}; + +exports.down = function (knex) {}; diff --git a/packages/server-nest/src/database/migrations/20240201235818_add_plaid_account_id_to_accounts_table.ts b/packages/server-nest/src/database/migrations/20240201235818_add_plaid_account_id_to_accounts_table.ts new file mode 100644 index 000000000..901f08987 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240201235818_add_plaid_account_id_to_accounts_table.ts @@ -0,0 +1,9 @@ +exports.up = function (knex) { + return knex.schema.table('accounts', (table) => { + table.string('plaid_account_id'); + table.string('account_mask').nullable(); + table.decimal('bank_balance', 15, 5); + }); +}; + +exports.down = function (knex) {}; diff --git a/packages/server-nest/src/database/migrations/20240204180554_add_plaid_transaction_id_to_cashflow_transaction.ts b/packages/server-nest/src/database/migrations/20240204180554_add_plaid_transaction_id_to_cashflow_transaction.ts new file mode 100644 index 000000000..3c88cd589 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240204180554_add_plaid_transaction_id_to_cashflow_transaction.ts @@ -0,0 +1,7 @@ +exports.up = function (knex) { + return knex.schema.table('cashflow_transactions', (table) => { + table.string('plaid_transaction_id'); + }); +}; + +exports.down = function (knex) {}; diff --git a/packages/server-nest/src/database/migrations/20240228183404_create_uncateogrized_cashflow_transactions_table.ts b/packages/server-nest/src/database/migrations/20240228183404_create_uncateogrized_cashflow_transactions_table.ts new file mode 100644 index 000000000..29a596772 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240228183404_create_uncateogrized_cashflow_transactions_table.ts @@ -0,0 +1,28 @@ +exports.up = function (knex) { + return knex.schema.createTable( + 'uncategorized_cashflow_transactions', + (table) => { + table.increments('id'); + table.date('date').index(); + table.decimal('amount'); + table.string('currency_code'); + table.string('reference_no').index(); + table.string('payee'); + table + .integer('account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table.string('description'); + table.string('categorize_ref_type'); + table.integer('categorize_ref_id').unsigned(); + table.boolean('categorized').defaultTo(false); + table.string('plaid_transaction_id'); + table.timestamps(); + } + ); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('uncategorized_cashflow_transactions'); +}; diff --git a/packages/server-nest/src/database/migrations/20240304153926_add_uncategorized_transactions_column_to_accounts_table.ts b/packages/server-nest/src/database/migrations/20240304153926_add_uncategorized_transactions_column_to_accounts_table.ts new file mode 100644 index 000000000..06d05f521 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240304153926_add_uncategorized_transactions_column_to_accounts_table.ts @@ -0,0 +1,10 @@ +exports.up = function (knex) { + return knex.schema.table('accounts', (table) => { + table.integer('uncategorized_transactions').defaultTo(0); + table.boolean('is_system_account').defaultTo(true); + table.boolean('is_feeds_active').defaultTo(false); + table.datetime('last_feeds_updated_at').nullable(); + }); +}; + +exports.down = function (knex) {}; diff --git a/packages/server-nest/src/database/migrations/20240308122047_add_uncategorized_transaction_id_to_cashflow_transactions.ts b/packages/server-nest/src/database/migrations/20240308122047_add_uncategorized_transaction_id_to_cashflow_transactions.ts new file mode 100644 index 000000000..01b93bea5 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240308122047_add_uncategorized_transaction_id_to_cashflow_transactions.ts @@ -0,0 +1,15 @@ +exports.up = function (knex) { + return knex.schema.table('cashflow_transactions', (table) => { + table + .integer('uncategorized_transaction_id') + .unsigned() + .references('id') + .inTable('uncategorized_cashflow_transactions'); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('cashflow_transactions', (table) => { + table.dropColumn('uncategorized_transaction_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240604153938_drop_storage_table.ts b/packages/server-nest/src/database/migrations/20240604153938_drop_storage_table.ts new file mode 100644 index 000000000..f84c6abd0 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240604153938_drop_storage_table.ts @@ -0,0 +1,5 @@ +exports.up = function (knex) { + return knex.schema.dropTableIfExists('storage'); +}; + +exports.down = function (knex) {}; diff --git a/packages/server-nest/src/database/migrations/20240604153951_create_documents_table.ts b/packages/server-nest/src/database/migrations/20240604153951_create_documents_table.ts new file mode 100644 index 000000000..44e2f269c --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240604153951_create_documents_table.ts @@ -0,0 +1,14 @@ +exports.up = function (knex) { + return knex.schema.createTable('documents', (table) => { + table.increments('id').primary(); + table.string('key').notNullable(); + table.string('mime_type').notNullable(); + table.integer('size').unsigned(); + table.string('origin_name'); + table.timestamps(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('documents'); +}; diff --git a/packages/server-nest/src/database/migrations/20240604154005_create_documents_links_table.ts b/packages/server-nest/src/database/migrations/20240604154005_create_documents_links_table.ts new file mode 100644 index 000000000..7cdb91429 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240604154005_create_documents_links_table.ts @@ -0,0 +1,14 @@ +exports.up = function (knex) { + return knex.schema.createTable('document_links', (table) => { + table.increments('id').primary(); + table.string('model_ref').notNullable(); + table.string('model_id').notNullable(); + table.integer('document_id').unsigned(); + table.datetime('expires_at').nullable(); + table.timestamps(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('document_links'); +}; diff --git a/packages/server-nest/src/database/migrations/20240618100137_create_bank_rules_table.ts b/packages/server-nest/src/database/migrations/20240618100137_create_bank_rules_table.ts new file mode 100644 index 000000000..c11ce89f2 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240618100137_create_bank_rules_table.ts @@ -0,0 +1,45 @@ +exports.up = function (knex) { + return knex.schema + .createTable('bank_rules', (table) => { + table.increments('id').primary(); + table.string('name'); + table.integer('order').unsigned(); + + table + .integer('apply_if_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table.string('apply_if_transaction_type'); + + table.string('assign_category'); + table + .integer('assign_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table.string('assign_payee'); + table.string('assign_memo'); + + table.string('conditions_type'); + + table.timestamps(); + }) + .createTable('bank_rule_conditions', (table) => { + table.increments('id').primary(); + table + .integer('rule_id') + .unsigned() + .references('id') + .inTable('bank_rules'); + table.string('field'); + table.string('comparator'); + table.string('value'); + }); +}; + +exports.down = function (knex) { + return knex.schema + .dropTableIfExists('bank_rules') + .dropTableIfExists('bank_rule_conditions'); +}; diff --git a/packages/server-nest/src/database/migrations/20240618171553_create_recognized_bank_transactions_table.ts b/packages/server-nest/src/database/migrations/20240618171553_create_recognized_bank_transactions_table.ts new file mode 100644 index 000000000..fbb8e7734 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240618171553_create_recognized_bank_transactions_table.ts @@ -0,0 +1,31 @@ +exports.up = function (knex) { + return knex.schema.createTable('recognized_bank_transactions', (table) => { + table.increments('id'); + table + .integer('uncategorized_transaction_id') + .unsigned() + .references('id') + .inTable('uncategorized_cashflow_transactions') + .withKeyName('recognizedBankTransactionsUncategorizedTransIdForeign'); + table + .integer('bank_rule_id') + .unsigned() + .references('id') + .inTable('bank_rules'); + + table.string('assigned_category'); + table + .integer('assigned_account_id') + .unsigned() + .references('id') + .inTable('accounts'); + table.string('assigned_payee'); + table.string('assigned_memo'); + + table.timestamps(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('recognized_bank_transactions'); +}; diff --git a/packages/server-nest/src/database/migrations/20240618175241_add_recognized_transaction_id_to_uncategorized_transactins_table.ts b/packages/server-nest/src/database/migrations/20240618175241_add_recognized_transaction_id_to_uncategorized_transactins_table.ts new file mode 100644 index 000000000..64c1e5450 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240618175241_add_recognized_transaction_id_to_uncategorized_transactins_table.ts @@ -0,0 +1,16 @@ +exports.up = function (knex) { + return knex.schema.table('uncategorized_cashflow_transactions', (table) => { + table + .integer('recognized_transaction_id') + .unsigned() + .references('id') + .inTable('recognized_bank_transactions') + .withKeyName('uncategorizedCashflowTransRecognizedTranIdForeign'); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('uncategorized_cashflow_transactions', (table) => { + table.dropColumn('recognized_transaction_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240619133733_create_matched_bank_transactions_table.ts b/packages/server-nest/src/database/migrations/20240619133733_create_matched_bank_transactions_table.ts new file mode 100644 index 000000000..f804ad7c7 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240619133733_create_matched_bank_transactions_table.ts @@ -0,0 +1,18 @@ +exports.up = function (knex) { + return knex.schema.createTable('matched_bank_transactions', (table) => { + table.increments('id'); + table + .integer('uncategorized_transaction_id') + .unsigned() + .references('id') + .inTable('uncategorized_cashflow_transactions'); + table.string('reference_type'); + table.integer('reference_id').unsigned(); + table.decimal('amount'); + table.timestamps(); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists('matched_bank_transactions'); +}; diff --git a/packages/server-nest/src/database/migrations/20240620111308_add_excluded_column_to_uncategorized_cashflow_transactions_table.ts b/packages/server-nest/src/database/migrations/20240620111308_add_excluded_column_to_uncategorized_cashflow_transactions_table.ts new file mode 100644 index 000000000..f27351438 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240620111308_add_excluded_column_to_uncategorized_cashflow_transactions_table.ts @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.table('uncategorized_cashflow_transactions', (table) => { + table.datetime('excluded_at'); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('uncategorized_cashflow_transactions', (table) => { + table.dropColumn('excluded_at'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240623154149_add_batch_column_to_uncategorized_cashflow_transactions_table.ts b/packages/server-nest/src/database/migrations/20240623154149_add_batch_column_to_uncategorized_cashflow_transactions_table.ts new file mode 100644 index 000000000..7641da878 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240623154149_add_batch_column_to_uncategorized_cashflow_transactions_table.ts @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.table('uncategorized_cashflow_transactions', (table) => { + table.string('batch'); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('uncategorized_cashflow_transactions', (table) => { + table.dropColumn('batch'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240704064858_change_settings_value_to_text.ts b/packages/server-nest/src/database/migrations/20240704064858_change_settings_value_to_text.ts new file mode 100644 index 000000000..985947276 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240704064858_change_settings_value_to_text.ts @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.table('settings', (table) => { + table.text('value').alter(); + }); +}; + +exports.down = (knex) => { + return knex.schema.table('settings', (table) => { + table.string('value').alter(); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240709122347_move_cashflow_transaction_type_to_transaction_type_column.ts b/packages/server-nest/src/database/migrations/20240709122347_move_cashflow_transaction_type_to_transaction_type_column.ts new file mode 100644 index 000000000..cc8a52e8c --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240709122347_move_cashflow_transaction_type_to_transaction_type_column.ts @@ -0,0 +1,60 @@ +exports.up = function (knex) { + return knex('accounts_transactions') + .whereIn('referenceType', [ + 'OtherIncome', + 'OtherExpense', + 'OwnerDrawing', + 'OwnerContribution', + 'TransferToAccount', + 'TransferFromAccount', + ]) + .update({ + transactionType: knex.raw(` + CASE + WHEN REFERENCE_TYPE = 'OtherIncome' THEN 'OtherIncome' + WHEN REFERENCE_TYPE = 'OtherExpense' THEN 'OtherExpense' + WHEN REFERENCE_TYPE = 'OwnerDrawing' THEN 'OwnerDrawing' + WHEN REFERENCE_TYPE = 'OwnerContribution' THEN 'OwnerContribution' + WHEN REFERENCE_TYPE = 'TransferToAccount' THEN 'TransferToAccount' + WHEN REFERENCE_TYPE = 'TransferFromAccount' THEN 'TransferFromAccount' + END + `), + referenceType: knex.raw(` + CASE + WHEN REFERENCE_TYPE IN ('OtherIncome', 'OtherExpense', 'OwnerDrawing', 'OwnerContribution', 'TransferToAccount', 'TransferFromAccount') THEN 'CashflowTransaction' + ELSE REFERENCE_TYPE + END + `), + }); +}; + +exports.down = function (knex) { + return knex('accounts_transactions') + .whereIn('transactionType', [ + 'OtherIncome', + 'OtherExpense', + 'OwnerDrawing', + 'OwnerContribution', + 'TransferToAccount', + 'TransferFromAccount', + ]) + .update({ + referenceType: knex.raw(` + CASE + WHEN TRANSACTION_TYPE = 'OtherIncome' THEN 'OtherIncome' + WHEN TRANSACTION_TYPE = 'OtherExpense' THEN 'OtherExpense' + WHEN TRANSACTION_TYPE = 'OwnerDrawing' THEN 'OwnerDrawing' + WHEN TRANSACTION_TYPE = 'OwnerContribution' THEN 'OwnerContribution' + WHEN TRANSACTION_TYPE = 'TransferToAccount' THEN 'TransferToAccount' + WHEN TRANSACTION_TYPE = 'TransferFromAccount' THEN 'TransferFromAccount' + ELSE REFERENCE_TYPE + END + `), + transactionType: knex.raw(` + CASE + WHEN TRANSACTION_TYPE IN ('OtherIncome', 'OtherExpense', 'OwnerDrawing', 'OwnerContribution', 'TransferToAccount', 'TransferFromAccount') THEN NULL + ELSE TRANSACTION_TYPE + END + `), + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240716114732_add_plaid_item_id_to_accounts_table.ts b/packages/server-nest/src/database/migrations/20240716114732_add_plaid_item_id_to_accounts_table.ts new file mode 100644 index 000000000..ce084dca8 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240716114732_add_plaid_item_id_to_accounts_table.ts @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.table('accounts', (table) => { + table.string('plaid_item_id').nullable(); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('accounts', (table) => { + table.dropColumn('plaid_item_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240729172403_add_is_syncing_owner_to_accounts_table.ts b/packages/server-nest/src/database/migrations/20240729172403_add_is_syncing_owner_to_accounts_table.ts new file mode 100644 index 000000000..e441ae31d --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240729172403_add_is_syncing_owner_to_accounts_table.ts @@ -0,0 +1,21 @@ +exports.up = function (knex) { + return knex.schema + .table('accounts', (table) => { + table + .boolean('is_syncing_owner') + .defaultTo(false) + .after('is_feeds_active'); + }) + .then(() => { + return knex('accounts') + .whereNotNull('plaid_item_id') + .orWhereNotNull('plaid_account_id') + .update('is_syncing_owner', true); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('accounts', (table) => { + table.dropColumn('is_syncing_owner'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240801130829_change_tax_amount_withheld_column_precision_in_bills_and_sales_invoices_tables.ts b/packages/server-nest/src/database/migrations/20240801130829_change_tax_amount_withheld_column_precision_in_bills_and_sales_invoices_tables.ts new file mode 100644 index 000000000..e03346a5f --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240801130829_change_tax_amount_withheld_column_precision_in_bills_and_sales_invoices_tables.ts @@ -0,0 +1,18 @@ +// This migration changes the precision of the tax_amount_withheld column in the bills and sales_invoices tables from 8, 2 to 13, 2. +// This migration is necessary to allow tax_amount_withheld filed to store values bigger than 999,999.99. + +exports.up = function(knex) { + return knex.schema.alterTable('bills', function (table) { + table.decimal('tax_amount_withheld', 13, 2).alter(); + }).alterTable('sales_invoices', function (table) { + table.decimal('tax_amount_withheld', 13, 2).alter(); + }); +}; + +exports.down = function(knex) { + return knex.schema.alterTable('bills', function (table) { + table.decimal('tax_amount_withheld', 8, 2).alter(); + }).alterTable('sales_invoices', function (table) { + table.decimal('tax_amount_withheld', 8, 2).alter(); + }); +}; \ No newline at end of file diff --git a/packages/server-nest/src/database/migrations/20240804084709_create_paused_at_column_to_plaid_items_table.ts b/packages/server-nest/src/database/migrations/20240804084709_create_paused_at_column_to_plaid_items_table.ts new file mode 100644 index 000000000..2947eb30b --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240804084709_create_paused_at_column_to_plaid_items_table.ts @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.table('plaid_items', (table) => { + table.datetime('paused_at'); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('plaid_items', (table) => { + table.dropColumn('paused_at'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240811121028_add_pending_column_to_uncategorized_transactions_table.ts b/packages/server-nest/src/database/migrations/20240811121028_add_pending_column_to_uncategorized_transactions_table.ts new file mode 100644 index 000000000..73713b98b --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240811121028_add_pending_column_to_uncategorized_transactions_table.ts @@ -0,0 +1,13 @@ +exports.up = function (knex) { + return knex.schema.table('uncategorized_cashflow_transactions', (table) => { + table.boolean('pending').defaultTo(false); + table.string('pending_plaid_transaction_id').nullable(); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('uncategorized_cashflow_transactions', (table) => { + table.dropColumn('pending'); + table.dropColumn('pending_plaid_transaction_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240909101051_add_stripe_pintent_id_to_payments_received.ts b/packages/server-nest/src/database/migrations/20240909101051_add_stripe_pintent_id_to_payments_received.ts new file mode 100644 index 000000000..5a82f16bb --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240909101051_add_stripe_pintent_id_to_payments_received.ts @@ -0,0 +1,19 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema.table('payment_receives', (table) => { + table.string('stripe_pintent_id').nullable(); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema.table('payment_receives', (table) => { + table.dropColumn('stripe_pintent_id'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20240911112147_create_pdf_templates_table.ts b/packages/server-nest/src/database/migrations/20240911112147_create_pdf_templates_table.ts new file mode 100644 index 000000000..be880c262 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240911112147_create_pdf_templates_table.ts @@ -0,0 +1,75 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema + .createTable('pdf_templates', (table) => { + table.increments('id').primary(); + table.text('resource'); + table.text('template_name'); + table.json('attributes'); + table.boolean('predefined').defaultTo(false); + table.boolean('default').defaultTo(false); + table.timestamps(); + }) + .table('sales_invoices', (table) => { + table + .integer('pdf_template_id') + .unsigned() + .references('id') + .inTable('pdf_templates'); + }) + .table('sales_estimates', (table) => { + table + .integer('pdf_template_id') + .unsigned() + .references('id') + .inTable('pdf_templates'); + }) + .table('sales_receipts', (table) => { + table + .integer('pdf_template_id') + .unsigned() + .references('id') + .inTable('pdf_templates'); + }) + .table('credit_notes', (table) => { + table + .integer('pdf_template_id') + .unsigned() + .references('id') + .inTable('pdf_templates'); + }) + .table('payment_receives', (table) => { + table + .integer('pdf_template_id') + .unsigned() + .references('id') + .inTable('pdf_templates'); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema + .table('payment_receives', (table) => { + table.dropColumn('pdf_template_id'); + }) + .table('credit_notes', (table) => { + table.dropColumn('pdf_template_id'); + }) + .table('sales_receipts', (table) => { + table.dropColumn('pdf_template_id'); + }) + .table('sales_estimates', (table) => { + table.dropColumn('pdf_template_id'); + }) + .table('sales_invoices', (table) => { + table.dropColumn('pdf_template_id'); + }) + .dropTableIfExists('pdf_templates'); +}; diff --git a/packages/server-nest/src/database/migrations/20240915155403_payment_integration.ts b/packages/server-nest/src/database/migrations/20240915155403_payment_integration.ts new file mode 100644 index 000000000..01fac495c --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240915155403_payment_integration.ts @@ -0,0 +1,25 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema.createTable('payment_integrations', (table) => { + table.increments('id'); + table.string('service'); + table.string('name'); + table.string('slug'); + table.boolean('payment_enabled').defaultTo(false); + table.boolean('payout_enabled').defaultTo(false); + table.string('account_id'); + table.json('options'); + table.timestamps(); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema.dropTableIfExists('payment_integrations'); +}; diff --git a/packages/server-nest/src/database/migrations/20240915163722_creat_transaction_payment_service_table.ts b/packages/server-nest/src/database/migrations/20240915163722_creat_transaction_payment_service_table.ts new file mode 100644 index 000000000..17b7fceda --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240915163722_creat_transaction_payment_service_table.ts @@ -0,0 +1,27 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema.createTable('transactions_payment_methods', (table) => { + table.increments('id'); + table.integer('reference_id').unsigned(); + table.string('reference_type'); + table + .integer('payment_integration_id') + .unsigned() + .index() + .references('id') + .inTable('payment_integrations'); + table.boolean('enable').defaultTo(false); + table.json('options').nullable(); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema.dropTableIfExists('transactions_payment_methods'); +}; diff --git a/packages/server-nest/src/database/migrations/20240915195024_seed_standard_pdf_templates.ts b/packages/server-nest/src/database/migrations/20240915195024_seed_standard_pdf_templates.ts new file mode 100644 index 000000000..74c87bb14 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20240915195024_seed_standard_pdf_templates.ts @@ -0,0 +1,44 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex('pdf_templates').insert([ + { + resource: 'SaleInvoice', + templateName: 'Standard Template', + predefined: true, + default: true, + }, + { + resource: 'SaleEstimate', + templateName: 'Standard Template', + predefined: true, + default: true, + }, + { + resource: 'SaleReceipt', + templateName: 'Standard Template', + predefined: true, + default: true, + }, + { + resource: 'CreditNote', + templateName: 'Standard Template', + predefined: true, + default: true, + }, + { + resource: 'PaymentReceive', + templateName: 'Standard Template', + predefined: true, + default: true, + }, + ]); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) {}; diff --git a/packages/server-nest/src/database/migrations/20241113113437_change_quantity_in_items_entries_to_decimal.ts b/packages/server-nest/src/database/migrations/20241113113437_change_quantity_in_items_entries_to_decimal.ts new file mode 100644 index 000000000..dcc711ffc --- /dev/null +++ b/packages/server-nest/src/database/migrations/20241113113437_change_quantity_in_items_entries_to_decimal.ts @@ -0,0 +1,39 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema + .table('items_entries', (table) => { + table.decimal('quantity', 13, 3).alter(); + }) + .table('inventory_transactions', (table) => { + table.decimal('quantity', 13, 3).alter(); + }) + .table('inventory_cost_lot_tracker', (table) => { + table.decimal('quantity', 13, 3).alter(); + }) + .table('items', (table) => { + table.decimal('quantityOnHand', 13, 3).alter(); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema + .table('items_entries', (table) => { + table.integer('quantity').alter(); + }) + .table('inventory_transactions', (table) => { + table.integer('quantity').alter(); + }) + .table('inventory_cost_lot_tracker', (table) => { + table.integer('quantity').alter(); + }) + .table('items', (table) => { + table.integer('quantityOnHand').alter(); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20241128080734_add_discount_to_invoices_table.ts b/packages/server-nest/src/database/migrations/20241128080734_add_discount_to_invoices_table.ts new file mode 100644 index 000000000..d65b00615 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20241128080734_add_discount_to_invoices_table.ts @@ -0,0 +1,23 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema.alterTable('sales_invoices', (table) => { + table.decimal('discount', 10, 2).nullable().after('credited_amount'); + table.string('discount_type').nullable().after('discount'); + table.decimal('adjustment', 10, 2).nullable().after('discount_type'); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema.alterTable('sale_invoices', (table) => { + table.dropColumn('discount'); + table.dropColumn('discount_type'); + table.dropColumn('adjustment'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20241128081259_add_discount_to_estimates_table.ts b/packages/server-nest/src/database/migrations/20241128081259_add_discount_to_estimates_table.ts new file mode 100644 index 000000000..748483f26 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20241128081259_add_discount_to_estimates_table.ts @@ -0,0 +1,24 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function(knex) { + return knex.schema.alterTable('sales_estimates', (table) => { + table.decimal('discount', 10, 2).nullable().after('amount'); + table.string('discount_type').nullable().after('discount'); + + table.decimal('adjustment', 10, 2).nullable().after('discount_type'); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function(knex) { + return knex.schema.alterTable('sales_estimates', (table) => { + table.dropColumn('discount'); + table.dropColumn('discount_type'); + table.dropColumn('adjustment'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20241128084550_add_discount_to_receipts_table.ts b/packages/server-nest/src/database/migrations/20241128084550_add_discount_to_receipts_table.ts new file mode 100644 index 000000000..8b46955cc --- /dev/null +++ b/packages/server-nest/src/database/migrations/20241128084550_add_discount_to_receipts_table.ts @@ -0,0 +1,24 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function(knex) { + return knex.schema.alterTable('sales_receipts', (table) => { + table.decimal('discount', 10, 2).nullable().after('amount'); + table.string('discount_type').nullable().after('discount'); + + table.decimal('adjustment', 10, 2).nullable().after('discount_type'); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function(knex) { + return knex.schema.alterTable('sales_receipts', (table) => { + table.dropColumn('discount'); + table.dropColumn('discount_type'); + table.dropColumn('adjustment'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20241128085243_add_discount_to_bills_table.ts b/packages/server-nest/src/database/migrations/20241128085243_add_discount_to_bills_table.ts new file mode 100644 index 000000000..9de24d9ab --- /dev/null +++ b/packages/server-nest/src/database/migrations/20241128085243_add_discount_to_bills_table.ts @@ -0,0 +1,26 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function(knex) { + return knex.schema.alterTable('bills', (table) => { + // Discount. + table.decimal('discount', 10, 2).nullable().after('amount'); + table.string('discount_type').nullable().after('discount'); + + // Adjustment. + table.decimal('adjustment', 10, 2).nullable().after('discount_type'); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function(knex) { + return knex.schema.alterTable('bills', (table) => { + table.dropColumn('discount'); + table.dropColumn('discount_type'); + table.dropColumn('adjustment'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20241128090222_add_discount_to_credit_notes_table.ts b/packages/server-nest/src/database/migrations/20241128090222_add_discount_to_credit_notes_table.ts new file mode 100644 index 000000000..fcca5bfa4 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20241128090222_add_discount_to_credit_notes_table.ts @@ -0,0 +1,23 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function(knex) { + return knex.schema.alterTable('credit_notes', (table) => { + table.decimal('discount', 10, 2).nullable().after('exchange_rate'); + table.string('discount_type').nullable().after('discount'); + table.decimal('adjustment', 10, 2).nullable().after('discount_type'); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function(knex) { + return knex.schema.alterTable('credit_notes', (table) => { + table.dropColumn('discount'); + table.dropColumn('discount_type'); + table.dropColumn('adjustment'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20241128160604_add_discount_to_vendor_credits_table.ts b/packages/server-nest/src/database/migrations/20241128160604_add_discount_to_vendor_credits_table.ts new file mode 100644 index 000000000..706bad1f7 --- /dev/null +++ b/packages/server-nest/src/database/migrations/20241128160604_add_discount_to_vendor_credits_table.ts @@ -0,0 +1,23 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function(knex) { + return knex.schema.alterTable('vendor_credits', (table) => { + table.decimal('discount', 10, 2).nullable().after('amount'); + table.string('discount_type').nullable().after('discount'); + table.decimal('adjustment', 10, 2).nullable().after('discount_type'); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function(knex) { + return knex.schema.alterTable('vendor_credits', (table) => { + table.dropColumn('discount'); + table.dropColumn('discount_type'); + table.dropColumn('adjustment'); + }); +}; diff --git a/packages/server-nest/src/database/migrations/20241211103019_add_discount_type_to_items_entries_table.ts b/packages/server-nest/src/database/migrations/20241211103019_add_discount_type_to_items_entries_table.ts new file mode 100644 index 000000000..292bdd8dc --- /dev/null +++ b/packages/server-nest/src/database/migrations/20241211103019_add_discount_type_to_items_entries_table.ts @@ -0,0 +1,19 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema.alterTable('items_entries', (table) => { + table.string('discount_type').defaultTo('percentage').after('discount'); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema.alterTable('items_entries', (table) => { + table.dropColumn('discount_type'); + }); +}; diff --git a/packages/server-nest/src/modules/App/App.module.ts b/packages/server-nest/src/modules/App/App.module.ts index a5ab3e410..0be07bc05 100644 --- a/packages/server-nest/src/modules/App/App.module.ts +++ b/packages/server-nest/src/modules/App/App.module.ts @@ -75,6 +75,7 @@ import { OrganizationModule } from '../Organization/Organization.module'; import { TenantDBManagerModule } from '../TenantDBManager/TenantDBManager.module'; import { PaymentServicesModule } from '../PaymentServices/PaymentServices.module'; import { AuthModule } from '../Auth/Auth.module'; +import { TenancyModule } from '../Tenancy/Tenancy.module'; @Module({ imports: [ @@ -135,6 +136,7 @@ import { AuthModule } from '../Auth/Auth.module'; }), TenancyDatabaseModule, TenancyModelsModule, + TenancyModule, ChromiumlyTenancyModule, TransformerModule, MailModule, diff --git a/packages/server-nest/src/modules/Auth/Auth.module.ts b/packages/server-nest/src/modules/Auth/Auth.module.ts index 235656459..3a711eec1 100644 --- a/packages/server-nest/src/modules/Auth/Auth.module.ts +++ b/packages/server-nest/src/modules/Auth/Auth.module.ts @@ -26,6 +26,7 @@ import { import { SendResetPasswordMailProcessor } from './processors/SendResetPasswordMail.processor'; import { SendSignupVerificationMailProcessor } from './processors/SendSignupVerificationMail.processor'; import { MailModule } from '../Mail/Mail.module'; +import { ConfigService } from '@nestjs/config'; const models = [RegisterTenancyModel(PasswordReset)]; @@ -34,9 +35,13 @@ const models = [RegisterTenancyModel(PasswordReset)]; imports: [ MailModule, PassportModule.register({ defaultStrategy: 'jwt' }), - JwtModule.register({ - signOptions: { expiresIn: '1d', algorithm: 'HS384' }, - verifyOptions: { algorithms: ['HS384'] }, + JwtModule.registerAsync({ + inject: [ConfigService], + useFactory: (configService: ConfigService) => ({ + secret: configService.get('jwt.secret'), + signOptions: { expiresIn: '1d', algorithm: 'HS384' }, + verifyOptions: { algorithms: ['HS384'] }, + }), }), TenantDBManagerModule, BullModule.registerQueue({ name: SendResetPasswordMailQueue }), diff --git a/packages/server-nest/src/modules/Auth/commands/AuthSignin.service.ts b/packages/server-nest/src/modules/Auth/commands/AuthSignin.service.ts index 0ca89fe6f..375a6bfad 100644 --- a/packages/server-nest/src/modules/Auth/commands/AuthSignin.service.ts +++ b/packages/server-nest/src/modules/Auth/commands/AuthSignin.service.ts @@ -41,6 +41,11 @@ export class AuthSigninService { `Wrong password for user with email: ${email}`, ); } + if (!user.verified) { + throw new UnauthorizedException( + `The user is not verified yet, check out your mail inbox.` + ); + } return user; } diff --git a/packages/server-nest/src/modules/InventoryCost/processors/ComputeItemCost.processor.ts b/packages/server-nest/src/modules/InventoryCost/processors/ComputeItemCost.processor.ts index 60772a6ef..7bc9882c2 100644 --- a/packages/server-nest/src/modules/InventoryCost/processors/ComputeItemCost.processor.ts +++ b/packages/server-nest/src/modules/InventoryCost/processors/ComputeItemCost.processor.ts @@ -1,6 +1,6 @@ import { EventEmitter2 } from '@nestjs/event-emitter'; -import { JOB_REF, Processor, WorkerHost } from '@nestjs/bullmq'; -import { Inject, Scope } from '@nestjs/common'; +import { Processor, WorkerHost } from '@nestjs/bullmq'; +import { Scope } from '@nestjs/common'; import { Job } from 'bullmq'; import { ClsService } from 'nestjs-cls'; import { TenantJobPayload } from '@/interfaces/Tenant'; diff --git a/packages/server-nest/src/modules/Organization/Organization.controller.ts b/packages/server-nest/src/modules/Organization/Organization.controller.ts index 5721f6248..b75b38a42 100644 --- a/packages/server-nest/src/modules/Organization/Organization.controller.ts +++ b/packages/server-nest/src/modules/Organization/Organization.controller.ts @@ -1,3 +1,4 @@ +import { ApiTags, ApiOperation, ApiResponse, ApiBody } from '@nestjs/swagger'; import { Controller, Post, @@ -16,10 +17,13 @@ import { } from './dtos/Organization.dto'; import { GetCurrentOrganizationService } from './queries/GetCurrentOrganization.service'; import { UpdateOrganizationService } from './commands/UpdateOrganization.service'; -import { ApiTags, ApiOperation, ApiResponse, ApiBody } from '@nestjs/swagger'; +import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard'; +import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards'; @ApiTags('Organization') @Controller('organization') +@IgnoreTenantInitializedRoute() +@IgnoreTenantSeededRoute() export class OrganizationController { constructor( private readonly buildOrganizationService: BuildOrganizationService, diff --git a/packages/server-nest/src/modules/Organization/Organization.module.ts b/packages/server-nest/src/modules/Organization/Organization.module.ts index 86c19c524..43f957151 100644 --- a/packages/server-nest/src/modules/Organization/Organization.module.ts +++ b/packages/server-nest/src/modules/Organization/Organization.module.ts @@ -22,9 +22,7 @@ import { OrganizationBaseCurrencyLocking } from './Organization/OrganizationBase OrganizationBaseCurrencyLocking, ], imports: [ - BullModule.registerQueue({ - name: OrganizationBuildQueue, - }), + BullModule.registerQueue({ name: OrganizationBuildQueue }), TenantDBManagerModule, ], controllers: [OrganizationController], diff --git a/packages/server-nest/src/modules/Organization/commands/BuildOrganization.service.ts b/packages/server-nest/src/modules/Organization/commands/BuildOrganization.service.ts index baa00ee06..9f1014084 100644 --- a/packages/server-nest/src/modules/Organization/commands/BuildOrganization.service.ts +++ b/packages/server-nest/src/modules/Organization/commands/BuildOrganization.service.ts @@ -29,7 +29,7 @@ export class BuildOrganizationService { private readonly tenantRepository: TenantRepository, @InjectQueue(OrganizationBuildQueue) - private readonly computeItemCostProcessor: Queue, + private readonly organizationBuildQueue: Queue, ) {} /** @@ -44,9 +44,9 @@ export class BuildOrganizationService { // Throw error if the tenant is already initialized. throwIfTenantInitizalized(tenant); - await this.tenantsManager.dropDatabaseIfExists(tenant); - await this.tenantsManager.createDatabase(tenant); - await this.tenantsManager.migrateTenant(tenant); + await this.tenantsManager.dropDatabaseIfExists(); + await this.tenantsManager.createDatabase(); + await this.tenantsManager.migrateTenant(); // Migrated tenant. const migratedTenant = await tenant.$query().withGraphFetched('metadata'); @@ -56,7 +56,7 @@ export class BuildOrganizationService { // this.tenantsManager.getSeedMigrationContext(migratedTenant); // Seed tenant. - await this.tenantsManager.seedTenant(migratedTenant, {}); + // await this.tenantsManager.seedTenant(migratedTenant, {}); // Throws `onOrganizationBuild` event. await this.eventPublisher.emitAsync(events.organization.build, { @@ -101,7 +101,8 @@ export class BuildOrganizationService { // Saves the tenant metadata. await this.tenantRepository.saveMetadata(tenant.id, transformedBuildDTO); - const jobMeta = await this.computeItemCostProcessor.add( + // Run the organization database build job. + const jobMeta = await this.organizationBuildQueue.add( OrganizationBuildQueueJob, { organizationId: tenant.organizationId, @@ -124,6 +125,8 @@ export class BuildOrganizationService { * @param {number} jobId */ public async revertBuildRunJob() { - // await Tenant.markAsBuildCompleted(tenantId, jobId); + const tenant = await this.tenancyContext.getTenant(); + + await this.tenantRepository.markAsBuildCompleted().findById(tenant.id); } } diff --git a/packages/server-nest/src/modules/Organization/commands/CommandOrganizationValidators.service.ts b/packages/server-nest/src/modules/Organization/commands/CommandOrganizationValidators.service.ts index 092b6aef8..de45d7c87 100644 --- a/packages/server-nest/src/modules/Organization/commands/CommandOrganizationValidators.service.ts +++ b/packages/server-nest/src/modules/Organization/commands/CommandOrganizationValidators.service.ts @@ -9,14 +9,7 @@ export class CommandOrganizationValidators { constructor( private readonly baseCurrencyMutateLocking: OrganizationBaseCurrencyLocking, ) {} - - /** - * Throw base currency mutate locked error. - */ - throwBaseCurrencyMutateLocked() { - throw new ServiceError(ERRORS.BASE_CURRENCY_MUTATE_LOCKED); - } - + /** * Validate mutate base currency ability. * @param {Tenant} tenant - @@ -35,7 +28,7 @@ export class CommandOrganizationValidators { ); if (isLocked) { - this.throwBaseCurrencyMutateLocked(); + throw new ServiceError(ERRORS.BASE_CURRENCY_MUTATE_LOCKED); } } } diff --git a/packages/server-nest/src/modules/Organization/processors/OrganizationBuild.processor.ts b/packages/server-nest/src/modules/Organization/processors/OrganizationBuild.processor.ts index 15551dbcc..490626d9d 100644 --- a/packages/server-nest/src/modules/Organization/processors/OrganizationBuild.processor.ts +++ b/packages/server-nest/src/modules/Organization/processors/OrganizationBuild.processor.ts @@ -1,7 +1,8 @@ import { Processor, WorkerHost } from '@nestjs/bullmq'; import { Scope } from '@nestjs/common'; -import { Process } from '@nestjs/bull'; import { Job } from 'bullmq'; +import { ClsService, UseCls } from 'nestjs-cls'; +import { Process } from '@nestjs/bull'; import { OrganizationBuildQueue, OrganizationBuildQueueJob, @@ -16,18 +17,26 @@ import { BuildOrganizationService } from '../commands/BuildOrganization.service' export class OrganizationBuildProcessor extends WorkerHost { constructor( private readonly organizationBuildService: BuildOrganizationService, + private readonly clsService: ClsService, ) { super(); } @Process(OrganizationBuildQueueJob) + @UseCls() async process(job: Job) { - try { - await this.organizationBuildService.build(job.data.buildDto); - } catch (e) { - // Unlock build status of the tenant. - await this.organizationBuildService.revertBuildRunJob(); - console.error(e); - } + console.log('Processing organization build job:', job.data); + + this.clsService.set('organizationId', job.data.organizationId); + this.clsService.set('userId', job.data.userId); + + try { + await this.organizationBuildService.build(job.data.buildDto); + } catch (e) { + // Unlock build status of the tenant. + await this.organizationBuildService.revertBuildRunJob(); + console.error('Error processing organization build job:', e); + throw e; // Re-throw to mark job as failed + } } } diff --git a/packages/server-nest/src/modules/System/models/SystemUser.ts b/packages/server-nest/src/modules/System/models/SystemUser.ts index 167a99b7e..eb56dcf1d 100644 --- a/packages/server-nest/src/modules/System/models/SystemUser.ts +++ b/packages/server-nest/src/modules/System/models/SystemUser.ts @@ -17,6 +17,20 @@ export class SystemUser extends BaseModel { return 'users'; } + /** + * Model modifiers. + */ + static get modifiers() { + return { + /** + * Filters the invite accepted users. + */ + inviteAccepted(query) { + query.whereNotNull('invite_accepted_at'); + }, + }; + } + async hashPassword(): Promise { const salt = await bcrypt.genSalt(); if (!/^\$2[abxy]?\$\d+\$/.test(this.password)) { diff --git a/packages/server-nest/src/modules/System/repositories/Tenant.repository.ts b/packages/server-nest/src/modules/System/repositories/Tenant.repository.ts index 328a6df53..48eb81f18 100644 --- a/packages/server-nest/src/modules/System/repositories/Tenant.repository.ts +++ b/packages/server-nest/src/modules/System/repositories/Tenant.repository.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Knex } from 'knex'; import * as uniqid from 'uniqid'; +import * as moment from 'moment'; import { TenantRepository as TenantBaseRepository } from '@/common/repository/TenantRepository'; import { SystemKnexConnection } from '../SystemDB/SystemDB.constants'; import { TenantModel } from '../models/TenantModel'; diff --git a/packages/server-nest/src/modules/Tenancy/EnsureTenantIsInitialized.guard.ts b/packages/server-nest/src/modules/Tenancy/EnsureTenantIsInitialized.guard.ts index 587094cd9..4c05573fc 100644 --- a/packages/server-nest/src/modules/Tenancy/EnsureTenantIsInitialized.guard.ts +++ b/packages/server-nest/src/modules/Tenancy/EnsureTenantIsInitialized.guard.ts @@ -2,13 +2,19 @@ import { CanActivate, ExecutionContext, Injectable, + SetMetadata, UnauthorizedException, } from '@nestjs/common'; import { TenancyContext } from './TenancyContext.service'; +import { Reflector } from '@nestjs/core'; +import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants'; + +export const IS_IGNORE_TENANT_INITIALIZED = 'IS_IGNORE_TENANT_INITIALIZED'; +export const IgnoreTenantInitializedRoute = () => SetMetadata(IS_IGNORE_TENANT_INITIALIZED, true); @Injectable() export class EnsureTenantIsInitializedGuard implements CanActivate { - constructor(private readonly tenancyContext: TenancyContext) {} + constructor(private readonly tenancyContext: TenancyContext, private reflector: Reflector) {} /** * Validate the tenant of the current request is initialized.. @@ -16,7 +22,18 @@ export class EnsureTenantIsInitializedGuard implements CanActivate { * @returns {Promise} */ async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); + const isIgnoreEnsureTenantInitialized = this.reflector.getAllAndOverride( + IS_IGNORE_TENANT_INITIALIZED, + [context.getHandler(), context.getClass()], + ); + const isPublic = this.reflector.getAllAndOverride( + IS_PUBLIC_ROUTE, + [context.getHandler(), context.getClass()], + ); + // Skip the guard early if the route marked as public or ignored. + if (isPublic || isIgnoreEnsureTenantInitialized) { + return true; + } const tenant = await this.tenancyContext.getTenant(); if (!tenant?.initializedAt) { diff --git a/packages/server-nest/src/modules/Tenancy/EnsureTenantIsSeeded.guards.ts b/packages/server-nest/src/modules/Tenancy/EnsureTenantIsSeeded.guards.ts index 725aaf2bf..b9929db92 100644 --- a/packages/server-nest/src/modules/Tenancy/EnsureTenantIsSeeded.guards.ts +++ b/packages/server-nest/src/modules/Tenancy/EnsureTenantIsSeeded.guards.ts @@ -3,13 +3,19 @@ import { ExecutionContext, Inject, Injectable, + SetMetadata, UnauthorizedException, } from '@nestjs/common'; import { TenancyContext } from './TenancyContext.service'; +import { Reflector } from '@nestjs/core'; +import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants'; + +export const IS_IGNORE_TENANT_SEEDED = 'IS_IGNORE_TENANT_SEEDED'; +export const IgnoreTenantSeededRoute = () => SetMetadata(IS_IGNORE_TENANT_SEEDED, true); @Injectable() export class EnsureTenantIsSeededGuard implements CanActivate { - constructor(private readonly tenancyContext: TenancyContext) {} + constructor(private readonly tenancyContext: TenancyContext, private reflector: Reflector) {} /** * Validate the tenant of the current request is seeded. @@ -17,9 +23,19 @@ export class EnsureTenantIsSeededGuard implements CanActivate { * @returns {Promise} */ async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); + const isPublic = this.reflector.getAllAndOverride( + IS_PUBLIC_ROUTE, + [context.getHandler(), context.getClass()], + ); + const isIgnoreEnsureTenantSeeded = this.reflector.getAllAndOverride( + IS_IGNORE_TENANT_SEEDED, + [context.getHandler(), context.getClass()], + ); + if (isPublic || isIgnoreEnsureTenantSeeded) { + return true; + } const tenant = await this.tenancyContext.getTenant(); - + if (!tenant.seededAt) { throw new UnauthorizedException({ message: 'Tenant database is not seeded with initial data yet.', diff --git a/packages/server-nest/src/modules/Tenancy/Tenancy.module.ts b/packages/server-nest/src/modules/Tenancy/Tenancy.module.ts new file mode 100644 index 000000000..0fe737d7a --- /dev/null +++ b/packages/server-nest/src/modules/Tenancy/Tenancy.module.ts @@ -0,0 +1,29 @@ +import { Module } from "@nestjs/common"; +import { EnsureTenantIsInitializedGuard } from "./EnsureTenantIsInitialized.guard"; +import { TenancyGlobalGuard } from "./TenancyGlobal.guard"; +import { EnsureTenantIsSeededGuard } from "./EnsureTenantIsSeeded.guards"; +import { APP_GUARD } from "@nestjs/core"; +import { TenancyContext } from "./TenancyContext.service"; +import { TenantController } from "./Tenant.controller"; + + +@Module({ + exports: [TenancyContext], + controllers: [TenantController], + providers: [ + TenancyContext, + { + provide: APP_GUARD, + useClass: TenancyGlobalGuard, + }, + { + provide: APP_GUARD, + useClass: EnsureTenantIsInitializedGuard, + }, + { + provide: APP_GUARD, + useClass: EnsureTenantIsSeededGuard + } + ] +}) +export class TenancyModule {} \ No newline at end of file diff --git a/packages/server-nest/src/modules/Tenancy/TenancyDB/TenancyDB.module.ts b/packages/server-nest/src/modules/Tenancy/TenancyDB/TenancyDB.module.ts index 003d2d3f1..ad2690e08 100644 --- a/packages/server-nest/src/modules/Tenancy/TenancyDB/TenancyDB.module.ts +++ b/packages/server-nest/src/modules/Tenancy/TenancyDB/TenancyDB.module.ts @@ -32,7 +32,7 @@ export const TenancyDatabaseProxyProvider = ClsModule.forFeatureAsync({ charset: 'utf8', }, migrations: { - directory: configService.get('tenantDatabase.migrationDir'), + directory: configService.get('tenantDatabase.migrationsDir'), }, seeds: { directory: configService.get('tenantDatabase.seedsDir'), diff --git a/packages/server-nest/src/modules/Tenancy/TenancyGlobal.guard.ts b/packages/server-nest/src/modules/Tenancy/TenancyGlobal.guard.ts index acee9bda9..43f9673cc 100644 --- a/packages/server-nest/src/modules/Tenancy/TenancyGlobal.guard.ts +++ b/packages/server-nest/src/modules/Tenancy/TenancyGlobal.guard.ts @@ -3,10 +3,15 @@ import { CanActivate, ExecutionContext, UnauthorizedException, + SetMetadata, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants'; +export const IS_TENANT_AGNOSTIC = 'IS_TENANT_AGNOSTIC'; + +export const TenantAgnosticRoute = () => SetMetadata(IS_TENANT_AGNOSTIC, true); + @Injectable() export class TenancyGlobalGuard implements CanActivate { constructor(private reflector: Reflector) {} @@ -23,8 +28,12 @@ export class TenancyGlobalGuard implements CanActivate { const isPublic = this.reflector.getAllAndOverride( IS_PUBLIC_ROUTE, [context.getHandler(), context.getClass()], + ) + const isTenantAgnostic = this.reflector.getAllAndOverride( + IS_TENANT_AGNOSTIC, + [context.getHandler(), context.getClass()], ); - if (isPublic) { + if (isPublic || isTenantAgnostic) { return true; } if (!organizationId) { diff --git a/packages/server-nest/src/modules/Tenancy/TenancyGlobal.middleware.ts b/packages/server-nest/src/modules/Tenancy/TenancyGlobal.middleware.ts deleted file mode 100644 index db9558bb5..000000000 --- a/packages/server-nest/src/modules/Tenancy/TenancyGlobal.middleware.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { - Injectable, - NestMiddleware, - UnauthorizedException, -} from '@nestjs/common'; -import { Request, Response, NextFunction } from 'express'; -import { ClsService, ClsServiceManager } from 'nestjs-cls'; - -export class TenancyGlobalMiddleware implements NestMiddleware { - constructor(private readonly cls: ClsService) {} - /** - * Validates the organization ID in the request headers. - * @param {Request} req - * @param {Response} res - * @param {NextFunction} next - */ - public use(req: Request, res: Response, next: NextFunction) { - const organizationId = req.headers['organization-id']; - - if (!organizationId) { - throw new UnauthorizedException('Organization ID is required.'); - } - next(); - } -} diff --git a/packages/server-nest/src/modules/Tenancy/TenancyIdCls.interceptor.ts b/packages/server-nest/src/modules/Tenancy/TenancyIdCls.interceptor.ts deleted file mode 100644 index 86474d2a6..000000000 --- a/packages/server-nest/src/modules/Tenancy/TenancyIdCls.interceptor.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - CallHandler, - ExecutionContext, - Injectable, - NestInterceptor, -} from '@nestjs/common'; -import { ClsService } from 'nestjs-cls'; -import { Observable } from 'rxjs'; - -@Injectable() -export class TenancyIdClsInterceptor implements NestInterceptor { - constructor(private readonly cls: ClsService) {} - - intercept(context: ExecutionContext, next: CallHandler): Observable { - const request = context.switchToHttp().getRequest(); - const organizationId = request.headers['organization-id']; - // this.cls.get('organizationId'); - - // console.log(organizationId, 'organizationId22'); - - return next.handle(); - } -} diff --git a/packages/server-nest/src/modules/Tenancy/TenancyModels/Tenancy.module.ts b/packages/server-nest/src/modules/Tenancy/TenancyModels/Tenancy.module.ts index 50badda8d..66f3e1b40 100644 --- a/packages/server-nest/src/modules/Tenancy/TenancyModels/Tenancy.module.ts +++ b/packages/server-nest/src/modules/Tenancy/TenancyModels/Tenancy.module.ts @@ -40,8 +40,6 @@ import { PaymentReceived } from '@/modules/PaymentReceived/models/PaymentReceive import { Model } from 'objection'; import { ClsModule } from 'nestjs-cls'; import { TenantUser } from './models/TenantUser.model'; -import { APP_GUARD } from '@nestjs/core'; -import { TenancyGlobalGuard } from '../TenancyGlobal.guard'; const models = [ Item, @@ -108,11 +106,5 @@ const modelProviders = models.map((model) => RegisterTenancyModel(model)); @Module({ imports: [...modelProviders], exports: [...modelProviders], - providers: [ - { - provide: APP_GUARD, - useClass: TenancyGlobalGuard, - }, - ], }) export class TenancyModelsModule {} diff --git a/packages/server-nest/src/modules/Tenancy/Tenant.controller.ts b/packages/server-nest/src/modules/Tenancy/Tenant.controller.ts index c85918e30..9d97585b9 100644 --- a/packages/server-nest/src/modules/Tenancy/Tenant.controller.ts +++ b/packages/server-nest/src/modules/Tenancy/Tenant.controller.ts @@ -1,7 +1,4 @@ -import { UseGuards } from '@nestjs/common'; -import { EnsureTenantIsSeededGuard } from '../Tenancy/EnsureTenantIsSeeded.guards'; -import { EnsureTenantIsInitializedGuard } from '../Tenancy/EnsureTenantIsInitialized.guard'; +import { Controller } from "@nestjs/common"; -@UseGuards(EnsureTenantIsInitializedGuard) -@UseGuards(EnsureTenantIsSeededGuard) +@Controller('/') export class TenantController {} diff --git a/packages/server-nest/src/modules/TenantDBManager/TenantDBManager.module.ts b/packages/server-nest/src/modules/TenantDBManager/TenantDBManager.module.ts index 6099b313b..460a2d45c 100644 --- a/packages/server-nest/src/modules/TenantDBManager/TenantDBManager.module.ts +++ b/packages/server-nest/src/modules/TenantDBManager/TenantDBManager.module.ts @@ -1,9 +1,10 @@ import { Module } from '@nestjs/common'; import { TenantsManagerService } from './TenantsManager'; import { TenantDBManager } from './TenantDBManager'; +import { TenancyContext } from '../Tenancy/TenancyContext.service'; @Module({ - providers: [TenantsManagerService, TenantDBManager], + providers: [TenancyContext, TenantsManagerService, TenantDBManager], exports: [TenantsManagerService, TenantDBManager], }) export class TenantDBManagerModule {} diff --git a/packages/server-nest/src/modules/TenantDBManager/TenantDBManager.ts b/packages/server-nest/src/modules/TenantDBManager/TenantDBManager.ts index c4bdfbd48..750f2ccbd 100644 --- a/packages/server-nest/src/modules/TenantDBManager/TenantDBManager.ts +++ b/packages/server-nest/src/modules/TenantDBManager/TenantDBManager.ts @@ -7,6 +7,8 @@ import { ConfigService } from '@nestjs/config'; import { SystemKnexConnection } from '../System/SystemDB/SystemDB.constants'; import { Inject, Injectable } from '@nestjs/common'; import { TenantModel } from '../System/models/TenantModel'; +import { TenancyContext } from '../Tenancy/TenancyContext.service'; +import { TENANCY_DB_CONNECTION } from '../Tenancy/TenancyDB/TenancyDB.constants'; @Injectable() export class TenantDBManager { @@ -14,6 +16,10 @@ export class TenantDBManager { constructor( private readonly configService: ConfigService, + private readonly tenancyContext: TenancyContext, + + @Inject(TENANCY_DB_CONNECTION) + private readonly tenantKnex: () => Knex, @Inject(SystemKnexConnection) private readonly systemKnex: Knex, @@ -25,7 +31,7 @@ export class TenantDBManager { */ private getDatabaseName(tenant: TenantModel) { return sanitizeDatabaseName( - `${this.configService.get('tenant.db_name_prefix')}${tenant.organizationId}`, + `${this.configService.get('tenantDatabase.dbNamePrefix')}${tenant.organizationId}`, ); } @@ -33,8 +39,10 @@ export class TenantDBManager { * Determines the tenant database weather exists. * @return {Promise} */ - public async databaseExists(tenant: TenantModel) { + public async databaseExists() { + const tenant = await this.tenancyContext.getTenant(); const databaseName = this.getDatabaseName(tenant); + const results = await this.systemKnex.raw( 'SELECT * FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = "' + databaseName + @@ -48,10 +56,14 @@ export class TenantDBManager { * @throws {TenantAlreadyInitialized} * @return {Promise} */ - public async createDatabase(tenant: TenantModel): Promise { + public async createDatabase(): Promise { + const tenant = await this.tenancyContext.getTenant(); + const databaseName = this.getDatabaseName(tenant); + + console.log(databaseName, 'name') + await this.throwErrorIfTenantDBExists(tenant); - const databaseName = this.getDatabaseName(tenant); await this.systemKnex.raw( `CREATE DATABASE ${databaseName} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci`, ); @@ -61,7 +73,8 @@ export class TenantDBManager { * Dropdowns the tenant database if it was exist. * @param {ITenant} tenant - */ - public async dropDatabaseIfExists(tenant: TenantModel) { + public async dropDatabaseIfExists() { + const tenant = await this.tenancyContext.getTenant(); const isExists = await this.databaseExists(tenant); if (!isExists) { @@ -72,9 +85,9 @@ export class TenantDBManager { /** * dropdowns the tenant's database. - * @param {ITenant} tenant */ - public async dropDatabase(tenant: TenantModel) { + public async dropDatabase() { + const tenant = await this.tenancyContext.getTenant(); const databaseName = this.getDatabaseName(tenant); await this.systemKnex.raw(`DROP DATABASE IF EXISTS ${databaseName}`); @@ -84,55 +97,21 @@ export class TenantDBManager { * Migrate tenant database schema to the latest version. * @return {Promise} */ - public async migrate(tenant: TenantModel): Promise { - const knex = this.setupKnexInstance(tenant); - await knex.migrate.latest(); + public async migrate(): Promise { + await this.tenantKnex().migrate.latest(); } /** * Seeds initial data to the tenant database. * @return {Promise} */ - public async seed(tenant: TenantModel): Promise { - const knex = this.setupKnexInstance(tenant); - - await knex.migrate.latest({ + public async seed(): Promise { + await this.systemKnex.migrate.latest({ ...tenantSeedConfig(tenant), disableMigrationsListValidation: true, }); } - /** - * Retrieve the knex instance of tenant. - * @return {Knex} - */ - private setupKnexInstance(tenant: TenantModel) { - const key: string = `${tenant.id}`; - let knexInstance = TenantDBManager.knexCache[key]; - - if (!knexInstance) { - knexInstance = knex({ - ...tenantKnexConfig(tenant), - ...knexSnakeCaseMappers({ upperCase: true }), - }); - TenantDBManager.knexCache[key] = knexInstance; - } - return knexInstance; - } - - /** - * Retrieve knex instance from the givne tenant. - */ - public getKnexInstance(tenantId: number) { - const key: string = `${tenantId}`; - let knexInstance = TenantDBManager.knexCache[key]; - - if (!knexInstance) { - throw new Error('Knex instance is not initialized yut.'); - } - return knexInstance; - } - /** * Throws error if the tenant database already exists. * @return {Promise} diff --git a/packages/server-nest/src/modules/TenantDBManager/TenantsManager.ts b/packages/server-nest/src/modules/TenantDBManager/TenantsManager.ts index e5a673ad0..243553fd7 100644 --- a/packages/server-nest/src/modules/TenantDBManager/TenantsManager.ts +++ b/packages/server-nest/src/modules/TenantDBManager/TenantsManager.ts @@ -10,11 +10,13 @@ import { } from './_utils'; import { SeedMigration } from '@/libs/migration-seed/SeedMigration'; import { TenantRepository } from '../System/repositories/Tenant.repository'; +import { TenancyContext } from '../Tenancy/TenancyContext.service'; @Injectable() export class TenantsManagerService { constructor( private readonly tenantDbManager: TenantDBManager, + private readonly tenancyContext: TenancyContext, private readonly eventEmitter: EventEmitter2, private readonly tenantRepository: TenantRepository, ) {} @@ -29,14 +31,13 @@ export class TenantsManagerService { /** * Creates a new tenant database. - * @param {ITenant} tenant - * @return {Promise} */ - public async createDatabase(tenant: TenantModel): Promise { + public async createDatabase(): Promise { + const tenant = await this.tenancyContext.getTenant(); throwErrorIfTenantAlreadyInitialized(tenant); - await this.tenantDbManager.createDatabase(tenant); - + await this.tenantDbManager.createDatabase(); await this.eventEmitter.emitAsync(events.tenantManager.databaseCreated); } @@ -44,9 +45,8 @@ export class TenantsManagerService { * Drops the database if the given tenant. * @param {number} tenantId */ - async dropDatabaseIfExists(tenant: TenantModel) { - // Drop the database if exists. - await this.tenantDbManager.dropDatabaseIfExists(tenant); + async dropDatabaseIfExists() { + await this.tenantDbManager.dropDatabaseIfExists(); } /** @@ -54,21 +54,22 @@ export class TenantsManagerService { * @param {ITenant} tenant * @returns {Promise} */ - public async hasDatabase(tenant: TenantModel): Promise { - return this.tenantDbManager.databaseExists(tenant); + public async hasDatabase(): Promise { + return this.tenantDbManager.databaseExists(); } /** * Migrates the tenant database. - * @param {ITenant} tenant * @return {Promise} */ - public async migrateTenant(tenant: TenantModel): Promise { + public async migrateTenant(): Promise { + const tenant = await this.tenancyContext.getTenant(); + // Throw error if the tenant already initialized. throwErrorIfTenantAlreadyInitialized(tenant); // Migrate the database tenant. - await this.tenantDbManager.migrate(tenant); + await this.tenantDbManager.migrate(); // Mark the tenant as initialized. await this.tenantRepository.markAsInitialized().findById(tenant.id); @@ -81,7 +82,6 @@ export class TenantsManagerService { /** * Seeds the tenant database. - * @param {ITenant} tenant * @return {Promise} */ public async seedTenant(tenant: TenantModel, tenancyContext): Promise { @@ -102,22 +102,4 @@ export class TenantsManagerService { tenantId: tenant.id, }); } - - /** - * Initialize knex instance or retrieve the instance of cache map. - * @param {ITenant} tenant - * @returns {Knex} - */ - public setupKnexInstance(tenant: TenantModel) { - // return this.tenantDbManager.setupKnexInstance(tenant); - } - - /** - * Retrieve tenant knex instance or throw error in case was not initialized. - * @param {number} tenantId - * @returns {Knex} - */ - public getKnexInstance(tenantId: number) { - return this.tenantDbManager.getKnexInstance(tenantId); - } }