1
0

chore: format all packages

This commit is contained in:
Ahmed Bouhuolia
2026-03-27 18:46:24 +02:00
parent 8c22c34c3a
commit 4526c147d8
751 changed files with 4062 additions and 3168 deletions
@@ -1,4 +1,4 @@
import { registerAs } from "@nestjs/config"; import { registerAs } from '@nestjs/config';
export default registerAs('inventory', () => ({ export default registerAs('inventory', () => ({
scheduleComputeItemCost: process.env.INVENTORY_SCHEDULE_COMPUTE_ITEM_COST, scheduleComputeItemCost: process.env.INVENTORY_SCHEDULE_COMPUTE_ITEM_COST,
@@ -1,4 +1,3 @@
import { registerAs } from '@nestjs/config'; import { registerAs } from '@nestjs/config';
export default registerAs('loops', () => ({ export default registerAs('loops', () => ({
@@ -7,6 +7,7 @@ export default registerAs('systemDatabase', () => ({
user: process.env.SYSTEM_DB_USER || process.env.DB_USER, user: process.env.SYSTEM_DB_USER || process.env.DB_USER,
password: process.env.SYSTEM_DB_PASSWORD || process.env.DB_PASSWORD, password: process.env.SYSTEM_DB_PASSWORD || process.env.DB_PASSWORD,
databaseName: process.env.SYSTEM_DB_NAME || process.env.DB_NAME, databaseName: process.env.SYSTEM_DB_NAME || process.env.DB_NAME,
migrationDir: process.env.SYSTEM_DB_MIGRATION_DIR || './src/database/system/migrations', migrationDir:
process.env.SYSTEM_DB_MIGRATION_DIR || './src/database/system/migrations',
seedsDir: process.env.SYSTEM_DB_SEEDS_DIR || './src/database/system/seeds', seedsDir: process.env.SYSTEM_DB_SEEDS_DIR || './src/database/system/seeds',
})); }));
@@ -10,5 +10,3 @@ export default registerAs('throttle', () => ({
limit: parseInt(process.env.THROTTLE_AUTH_LIMIT ?? '10', 10), limit: parseInt(process.env.THROTTLE_AUTH_LIMIT ?? '10', 10),
}, },
})); }));
@@ -1 +1 @@
export const MULTER_MODULE_OPTIONS = 'MULTER_MODULE_OPTIONS'; export const MULTER_MODULE_OPTIONS = 'MULTER_MODULE_OPTIONS';
@@ -5,4 +5,4 @@ export const ACCEPT_TYPE = {
APPLICATION_XLSX: 'application/xlsx', APPLICATION_XLSX: 'application/xlsx',
APPLICATION_CSV: 'application/csv', APPLICATION_CSV: 'application/csv',
APPLICATION_TEXT_HTML: 'application/json+html', APPLICATION_TEXT_HTML: 'application/json+html',
}; };
@@ -20,5 +20,3 @@ export const busboyExceptions = {
MULTIPART_UNEXPECTED_END_OF_FORM: 'Unexpected end of form', MULTIPART_UNEXPECTED_END_OF_FORM: 'Unexpected end of form',
MULTIPART_UNEXPECTED_END_OF_FILE: 'Unexpected end of file', MULTIPART_UNEXPECTED_END_OF_FILE: 'Unexpected end of file',
}; };
@@ -35,4 +35,4 @@ export function transformException(
return new BadRequestException(`Multipart: ${error.message}`); return new BadRequestException(`Multipart: ${error.message}`);
} }
return error; return error;
} }
@@ -1,4 +1,10 @@
import { IsArray, IsInt, ArrayNotEmpty, IsBoolean, IsOptional } from 'class-validator'; import {
IsArray,
IsInt,
ArrayNotEmpty,
IsBoolean,
IsOptional,
} from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Transform } from 'class-transformer'; import { Transform } from 'class-transformer';
import { parseBoolean } from '@/utils/parse-boolean'; import { parseBoolean } from '@/utils/parse-boolean';
@@ -16,9 +22,12 @@ export class BulkDeleteDto {
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@Transform(({ value, obj }) => parseBoolean(value ?? obj?.skip_undeletable, false)) @Transform(({ value, obj }) =>
parseBoolean(value ?? obj?.skip_undeletable, false),
)
@ApiPropertyOptional({ @ApiPropertyOptional({
description: 'When true, undeletable items will be skipped and only deletable ones will be removed.', description:
'When true, undeletable items will be skipped and only deletable ones will be removed.',
type: Boolean, type: Boolean,
default: false, default: false,
}) })
@@ -52,4 +61,3 @@ export class ValidateBulkDeleteResponseDto {
}) })
nonDeletableIds: number[]; nonDeletableIds: number[];
} }
@@ -21,8 +21,8 @@ export class ServiceErrorFilter implements ExceptionFilter {
type: exception.errorType, type: exception.errorType,
message: exception.message, message: exception.message,
payload: exception.payload, payload: exception.payload,
} },
] ],
}); });
} }
} }
@@ -29,12 +29,11 @@ export function FileInterceptor(
constructor( constructor(
@Optional() @Optional()
@Inject(MULTER_MODULE_OPTIONS) @Inject(MULTER_MODULE_OPTIONS)
options: (() => MulterModuleOptions | MulterModuleOptions) = () => ({}), options: () => MulterModuleOptions | MulterModuleOptions = () => ({}),
) { ) {
const resolvedOptions = typeof localOptions === 'function' const resolvedOptions =
? localOptions(this) typeof localOptions === 'function' ? localOptions(this) : localOptions;
: localOptions;
this.multer = (multer as any)({ this.multer = (multer as any)({
...(typeof options === 'function' ? options() : options), ...(typeof options === 'function' ? options() : options),
...resolvedOptions, ...resolvedOptions,
@@ -67,4 +66,4 @@ export function FileInterceptor(
const Interceptor = mixin(MixinInterceptor); const Interceptor = mixin(MixinInterceptor);
return Interceptor; return Interceptor;
} }
@@ -56,7 +56,7 @@ export const DEFAULT_STRATEGY = {
@Injectable() @Injectable()
export class SerializeInterceptor implements NestInterceptor<any, any> { export class SerializeInterceptor implements NestInterceptor<any, any> {
constructor(@Optional() readonly strategy = DEFAULT_STRATEGY) { } constructor(@Optional() readonly strategy = DEFAULT_STRATEGY) {}
intercept( intercept(
context: ExecutionContext, context: ExecutionContext,
@@ -16,7 +16,11 @@ export class ToJsonInterceptor implements NestInterceptor {
return data; return data;
} }
return mapValuesDeep(data, (value) => { return mapValuesDeep(data, (value) => {
if (value !== null && value !== undefined && typeof value.toJSON === 'function') { if (
value !== null &&
value !== undefined &&
typeof value.toJSON === 'function'
) {
return value.toJSON(); return value.toJSON();
} }
return value; return value;
@@ -1,6 +1,4 @@
// import { CachableRepository } from './CachableRepository'; // import { CachableRepository } from './CachableRepository';
import { EntityRepository } from './EntityRepository'; import { EntityRepository } from './EntityRepository';
export class TenantRepository extends EntityRepository { export class TenantRepository extends EntityRepository {}
}
+1 -1
View File
@@ -1,3 +1,3 @@
import * as moment from 'moment'; import * as moment from 'moment';
export type DateInput = moment.MomentInput; export type DateInput = moment.MomentInput;
+1 -1
View File
@@ -1,4 +1,4 @@
export enum DiscountType { export enum DiscountType {
Percentage = 'percentage', Percentage = 'percentage',
Amount = 'amount', Amount = 'amount',
} }
@@ -1,19 +1,25 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema.createTable('accounts', (table) => { return knex.schema
table.increments('id').comment('Auto-generated id'); .createTable('accounts', (table) => {
table.string('name').index(); table.increments('id').comment('Auto-generated id');
table.string('slug'); table.string('name').index();
table.string('account_type').index(); table.string('slug');
table.integer('parent_account_id').unsigned().references('id').inTable('accounts'); table.string('account_type').index();
table.string('code', 10).index(); table
table.text('description'); .integer('parent_account_id')
table.boolean('active').defaultTo(true).index(); .unsigned()
table.integer('index').unsigned(); .references('id')
table.boolean('predefined').defaultTo(false).index(); .inTable('accounts');
table.decimal('amount', 15, 5); table.string('code', 10).index();
table.string('currency_code', 3).index(); table.text('description');
table.timestamps(); table.boolean('active').defaultTo(true).index();
}).raw('ALTER TABLE `ACCOUNTS` AUTO_INCREMENT = 1000'); 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'); exports.down = (knex) => knex.schema.dropTableIfExists('accounts');
@@ -1,15 +1,26 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema.createTable('items_categories', (table) => { return knex.schema.createTable('items_categories', (table) => {
table.increments(); table.increments();
table.string('name').index(); table.string('name').index();
table.text('description'); table.text('description');
table.integer('user_id').unsigned().index(); table.integer('user_id').unsigned().index();
table.integer('cost_account_id').unsigned().references('id').inTable('accounts'); table
table.integer('sell_account_id').unsigned().references('id').inTable('accounts'); .integer('cost_account_id')
table.integer('inventory_account_id').unsigned().references('id').inTable('accounts'); .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.string('cost_method');
table.timestamps(); table.timestamps();
@@ -1,30 +1,50 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema.createTable('items', (table) => { return knex.schema
table.increments(); .createTable('items', (table) => {
table.string('name').index(); table.increments();
table.string('type').index(); table.string('name').index();
table.string('code'); table.string('type').index();
table.boolean('sellable').index(); table.string('code');
table.boolean('purchasable').index(); table.boolean('sellable').index();
table.decimal('sell_price', 13, 3).unsigned(); table.boolean('purchasable').index();
table.decimal('cost_price', 13, 3).unsigned(); table.decimal('sell_price', 13, 3).unsigned();
table.string('currency_code', 3); table.decimal('cost_price', 13, 3).unsigned();
table.string('picture_uri'); table.string('currency_code', 3);
table.integer('cost_account_id').nullable().unsigned().references('id').inTable('accounts'); table.string('picture_uri');
table.integer('sell_account_id').nullable().unsigned().references('id').inTable('accounts'); table
table.integer('inventory_account_id').unsigned().references('id').inTable('accounts'); .integer('cost_account_id')
table.text('sell_description').nullable(); .nullable()
table.text('purchase_description').nullable(); .unsigned()
table.integer('quantity_on_hand'); .references('id')
table.boolean('landed_cost').nullable(); .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.text('note').nullable();
table.boolean('active'); table.boolean('active');
table.integer('category_id').unsigned().index().references('id').inTable('items_categories'); table
table.integer('user_id').unsigned().index(); .integer('category_id')
table.timestamps(); .unsigned()
}).raw('ALTER TABLE `ITEMS` AUTO_INCREMENT = 1000'); .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'); exports.down = (knex) => knex.schema.dropTableIfExists('items');
@@ -1,15 +1,16 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema.createTable('views', (table) => { return knex.schema
table.increments(); .createTable('views', (table) => {
table.string('name').index(); table.increments();
table.string('slug').index(); table.string('name').index();
table.boolean('predefined'); table.string('slug').index();
table.string('resource_model').index(); table.boolean('predefined');
table.boolean('favourite'); table.string('resource_model').index();
table.string('roles_logic_expression'); table.boolean('favourite');
table.timestamps(); table.string('roles_logic_expression');
}).raw('ALTER TABLE `VIEWS` AUTO_INCREMENT = 1000'); table.timestamps();
})
.raw('ALTER TABLE `VIEWS` AUTO_INCREMENT = 1000');
}; };
exports.down = (knex) => knex.schema.dropTableIfExists('views'); exports.down = (knex) => knex.schema.dropTableIfExists('views');
@@ -1,13 +1,14 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema.createTable('settings', (table) => { return knex.schema
table.increments(); .createTable('settings', (table) => {
table.integer('user_id').unsigned().index(); table.increments();
table.string('group').index(); table.integer('user_id').unsigned().index();
table.string('type'); table.string('group').index();
table.string('key').index(); table.string('type');
table.string('value'); table.string('key').index();
}).raw('ALTER TABLE `SETTINGS` AUTO_INCREMENT = 2000'); table.string('value');
})
.raw('ALTER TABLE `SETTINGS` AUTO_INCREMENT = 2000');
}; };
exports.down = (knex) => knex.schema.dropTableIfExists('settings'); exports.down = (knex) => knex.schema.dropTableIfExists('settings');
@@ -1,11 +1,17 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema.createTable('view_has_columns', (table) => { return knex.schema
table.increments(); .createTable('view_has_columns', (table) => {
table.integer('view_id').unsigned().index().references('id').inTable('views'); table.increments();
table.string('field_key'); table
table.integer('index').unsigned(); .integer('view_id')
}).raw('ALTER TABLE `ITEMS_CATEGORIES` AUTO_INCREMENT = 1000'); .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'); exports.down = (knex) => knex.schema.dropTableIfExists('view_has_columns');
@@ -1,6 +1,5 @@
exports.up = function (knex) {
exports.up = function(knex) { return knex.schema.createTable('contacts', (table) => {
return knex.schema.createTable('contacts', table => {
table.increments(); table.increments();
table.string('contact_service'); table.string('contact_service');
@@ -32,8 +31,7 @@ exports.up = function(knex) {
table.string('billing_address_postcode').nullable(); table.string('billing_address_postcode').nullable();
table.string('billing_address_phone').nullable(); table.string('billing_address_phone').nullable();
table.string('billing_address_state').nullable(), table.string('billing_address_state').nullable(),
table.string('shipping_address_1').nullable();
table.string('shipping_address_1').nullable();
table.string('shipping_address_2').nullable(); table.string('shipping_address_2').nullable();
table.string('shipping_address_city').nullable(); table.string('shipping_address_city').nullable();
table.string('shipping_address_country').nullable(); table.string('shipping_address_country').nullable();
@@ -49,6 +47,6 @@ exports.up = function(knex) {
}); });
}; };
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.dropTableIfExists('contacts'); return knex.schema.dropTableIfExists('contacts');
}; };
@@ -1,21 +1,22 @@
exports.up = function (knex) {
exports.up = function(knex) { return knex.schema
return knex.schema.createTable('manual_journals', (table) => { .createTable('manual_journals', (table) => {
table.increments(); table.increments();
table.string('journal_number').index(); table.string('journal_number').index();
table.string('reference').index(); table.string('reference').index();
table.string('journal_type').index(); table.string('journal_type').index();
table.decimal('amount', 13, 3); table.decimal('amount', 13, 3);
table.string('currency_code', 3); table.string('currency_code', 3);
table.date('date').index(); table.date('date').index();
table.string('description'); table.string('description');
table.date('published_at').index(); table.date('published_at').index();
table.string('attachment_file'); table.string('attachment_file');
table.integer('user_id').unsigned().index(); table.integer('user_id').unsigned().index();
table.timestamps(); table.timestamps();
}).raw('ALTER TABLE `MANUAL_JOURNALS` AUTO_INCREMENT = 1000'); })
.raw('ALTER TABLE `MANUAL_JOURNALS` AUTO_INCREMENT = 1000');
}; };
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.dropTableIfExists('manual_journals'); return knex.schema.dropTableIfExists('manual_journals');
}; };
@@ -1,17 +1,28 @@
exports.up = function (knex) {
exports.up = function(knex) { return knex.schema
return knex.schema.createTable('manual_journals_entries', (table) => { .createTable('manual_journals_entries', (table) => {
table.increments(); table.increments();
table.decimal('credit', 13, 3); table.decimal('credit', 13, 3);
table.decimal('debit', 13, 3); table.decimal('debit', 13, 3);
table.integer('index').unsigned(); table.integer('index').unsigned();
table.integer('account_id').unsigned().index().references('id').inTable('accounts'); table
table.integer('contact_id').unsigned().nullable().index(); .integer('account_id')
table.string('note'); .unsigned()
table.integer('manual_journal_id').unsigned().index().references('id').inTable('manual_journals'); .index()
}).raw('ALTER TABLE `MANUAL_JOURNALS_ENTRIES` AUTO_INCREMENT = 1000'); .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) { exports.down = function (knex) {
return knex.schema.dropTableIfExists('manual_journals_entries'); return knex.schema.dropTableIfExists('manual_journals_entries');
}; };
@@ -1,14 +1,15 @@
exports.up = function (knex) {
exports.up = function(knex) { return knex.schema
return knex.schema.createTable('currencies', table => { .createTable('currencies', (table) => {
table.increments(); table.increments();
table.string('currency_name').index(); table.string('currency_name').index();
table.string('currency_code', 4).index(); table.string('currency_code', 4).index();
table.string('currency_sign').index(); table.string('currency_sign').index();
table.timestamps(); table.timestamps();
}).raw('ALTER TABLE `CURRENCIES` AUTO_INCREMENT = 1000'); })
.raw('ALTER TABLE `CURRENCIES` AUTO_INCREMENT = 1000');
}; };
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.dropTableIfExists('currencies'); return knex.schema.dropTableIfExists('currencies');
}; };
@@ -1,14 +1,15 @@
exports.up = function (knex) {
exports.up = function(knex) { return knex.schema
return knex.schema.createTable('exchange_rates', table => { .createTable('exchange_rates', (table) => {
table.increments(); table.increments();
table.string('currency_code', 4).index(); table.string('currency_code', 4).index();
table.decimal('exchange_rate'); table.decimal('exchange_rate');
table.date('date').index(); table.date('date').index();
table.timestamps(); table.timestamps();
}).raw('ALTER TABLE `EXCHANGE_RATES` AUTO_INCREMENT = 1000'); })
.raw('ALTER TABLE `EXCHANGE_RATES` AUTO_INCREMENT = 1000');
}; };
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.dropTableIfExists('exchange_rates'); return knex.schema.dropTableIfExists('exchange_rates');
}; };
@@ -1,12 +1,11 @@
exports.up = function (knex) {
exports.up = function(knex) {
return knex.schema.createTable('media', (table) => { return knex.schema.createTable('media', (table) => {
table.increments(); table.increments();
table.string('attachment_file'); table.string('attachment_file');
table.timestamps(); table.timestamps();
}); });
}; };
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.dropTableIfExists('media'); return knex.schema.dropTableIfExists('media');
}; };
@@ -1,13 +1,12 @@
exports.up = function (knex) {
exports.up = function(knex) { return knex.schema.createTable('media_links', (table) => {
return knex.schema.createTable('media_links', table => {
table.increments(); table.increments();
table.string('model_name').index(); table.string('model_name').index();
table.integer('media_id').unsigned().references('id').inTable('media'); table.integer('media_id').unsigned().references('id').inTable('media');
table.integer('model_id').unsigned().index(); table.integer('model_id').unsigned().index();
}) });
}; };
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.dropTableIfExists('media_links'); return knex.schema.dropTableIfExists('media_links');
}; };
@@ -1,11 +1,20 @@
exports.up = function (knex) {
exports.up = function(knex) { return knex.schema.createTable('sales_receipts', (table) => {
return knex.schema.createTable('sales_receipts', table => {
table.increments(); table.increments();
table.decimal('amount', 13, 3); table.decimal('amount', 13, 3);
table.string('currency_code', 3); table.string('currency_code', 3);
table.integer('deposit_account_id').unsigned().index().references('id').inTable('accounts'); table
table.integer('customer_id').unsigned().index().references('id').inTable('contacts'); .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.date('receipt_date').index();
table.string('receipt_number').index(); table.string('receipt_number').index();
table.string('reference_no').index(); table.string('reference_no').index();
@@ -14,9 +23,9 @@ exports.up = function(knex) {
table.text('statement'); table.text('statement');
table.date('closed_at').index(); table.date('closed_at').index();
table.timestamps(); table.timestamps();
}) });
}; };
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.dropTableIfExists('sales_receipts'); return knex.schema.dropTableIfExists('sales_receipts');
}; };
@@ -1,11 +1,19 @@
exports.up = function (knex) {
exports.up = function(knex) { return knex.schema.createTable('bills_payments', (table) => {
return knex.schema.createTable('bills_payments', table => {
table.increments(); table.increments();
table.integer('vendor_id').unsigned().index().references('id').inTable('contacts'); table
.integer('vendor_id')
.unsigned()
.index()
.references('id')
.inTable('contacts');
table.decimal('amount', 13, 3).defaultTo(0); table.decimal('amount', 13, 3).defaultTo(0);
table.string('currency_code'); table.string('currency_code');
table.integer('payment_account_id').unsigned().references('id').inTable('accounts'); table
.integer('payment_account_id')
.unsigned()
.references('id')
.inTable('accounts');
table.string('payment_number').nullable().index(); table.string('payment_number').nullable().index();
table.date('payment_date').index(); table.date('payment_date').index();
table.string('payment_method'); table.string('payment_method');
@@ -13,9 +21,7 @@ exports.up = function(knex) {
table.integer('user_id').unsigned().index(); table.integer('user_id').unsigned().index();
table.text('statement'); table.text('statement');
table.timestamps(); table.timestamps();
}); });
}; };
exports.down = function(knex) { exports.down = function (knex) {};
};
@@ -1,11 +1,10 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema.createTable('inventory_transaction_meta', (table) => { return knex.schema.createTable('inventory_transaction_meta', (table) => {
table.increments('id'); table.increments('id');
table.string('transaction_number'); table.string('transaction_number');
table.text('description'); table.text('description');
table.integer('inventory_transaction_id').unsigned(); table.integer('inventory_transaction_id').unsigned();
}); });
}; };
exports.down = function (knex) {}; exports.down = function (knex) {};
@@ -1,19 +1,22 @@
exports.up = function (knex) {
exports.up = function(knex) { return knex.schema.createTable('inventory_adjustments', (table) => {
return knex.schema.createTable('inventory_adjustments', table => {
table.increments(); table.increments();
table.date('date').index(); table.date('date').index();
table.string('type').index(); table.string('type').index();
table.integer('adjustment_account_id').unsigned().references('id').inTable('accounts'); table
.integer('adjustment_account_id')
.unsigned()
.references('id')
.inTable('accounts');
table.string('reason'); table.string('reason');
table.string('reference_no').index(); table.string('reference_no').index();
table.string('description'); table.string('description');
table.integer('user_id').unsigned(); table.integer('user_id').unsigned();
table.date('published_at'); table.date('published_at');
table.timestamps(); table.timestamps();
}); });
}; };
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.dropTableIfExists('inventory_adjustments'); return knex.schema.dropTableIfExists('inventory_adjustments');
}; };
@@ -1,16 +1,16 @@
exports.up = (knex) => { exports.up = (knex) => {
return knex.schema return knex.schema
.raw( .raw(
'ALTER TABLE CONTACTS CHANGE SHIPPING_ADDRESS_1 SHIPPING_ADDRESS1 VARCHAR(255)' 'ALTER TABLE CONTACTS CHANGE SHIPPING_ADDRESS_1 SHIPPING_ADDRESS1 VARCHAR(255)',
) )
.raw( .raw(
'ALTER TABLE CONTACTS CHANGE SHIPPING_ADDRESS_2 SHIPPING_ADDRESS2 VARCHAR(255)' 'ALTER TABLE CONTACTS CHANGE SHIPPING_ADDRESS_2 SHIPPING_ADDRESS2 VARCHAR(255)',
) )
.raw( .raw(
'ALTER TABLE CONTACTS CHANGE BILLING_ADDRESS_1 BILLING_ADDRESS1 VARCHAR(255)' 'ALTER TABLE CONTACTS CHANGE BILLING_ADDRESS_1 BILLING_ADDRESS1 VARCHAR(255)',
) )
.raw( .raw(
'ALTER TABLE CONTACTS CHANGE BILLING_ADDRESS_2 BILLING_ADDRESS2 VARCHAR(255)' 'ALTER TABLE CONTACTS CHANGE BILLING_ADDRESS_2 BILLING_ADDRESS2 VARCHAR(255)',
); );
}; };
@@ -11,4 +11,4 @@ exports.up = function (knex) {
exports.down = function (knex) { exports.down = function (knex) {
return knex.schema.dropTableIfExists('storage'); return knex.schema.dropTableIfExists('storage');
}; };
@@ -19,7 +19,7 @@ exports.up = function (knex) {
table.boolean('categorized').defaultTo(false); table.boolean('categorized').defaultTo(false);
table.string('plaid_transaction_id'); table.string('plaid_transaction_id');
table.timestamps(); table.timestamps();
} },
); );
}; };
@@ -1,18 +1,22 @@
// 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 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. // This migration is necessary to allow tax_amount_withheld filed to store values bigger than 999,999.99.
exports.up = function(knex) { exports.up = function (knex) {
return knex.schema.alterTable('bills', function (table) { return knex.schema
table.decimal('tax_amount_withheld', 13, 2).alter(); .alterTable('bills', function (table) {
}).alterTable('sales_invoices', function (table) { table.decimal('tax_amount_withheld', 13, 2).alter();
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) { exports.down = function (knex) {
return knex.schema.alterTable('bills', function (table) { return knex.schema
table.decimal('tax_amount_withheld', 8, 2).alter(); .alterTable('bills', function (table) {
}).alterTable('sales_invoices', function (table) { table.decimal('tax_amount_withheld', 8, 2).alter();
table.decimal('tax_amount_withheld', 8, 2).alter(); })
}); .alterTable('sales_invoices', function (table) {
}; table.decimal('tax_amount_withheld', 8, 2).alter();
});
};
@@ -6,7 +6,7 @@ exports.up = function (knex) {
return knex.schema.createTable('payment_integrations', (table) => { return knex.schema.createTable('payment_integrations', (table) => {
table.increments('id'); table.increments('id');
table.string('service'); table.string('service');
table.string('name'); table.string('name');
table.string('slug'); table.string('slug');
table.boolean('payment_enabled').defaultTo(false); table.boolean('payment_enabled').defaultTo(false);
table.boolean('payout_enabled').defaultTo(false); table.boolean('payout_enabled').defaultTo(false);
@@ -6,7 +6,7 @@ exports.up = function (knex) {
return knex.schema.alterTable('sales_invoices', (table) => { return knex.schema.alterTable('sales_invoices', (table) => {
table.decimal('discount', 10, 2).nullable().after('credited_amount'); table.decimal('discount', 10, 2).nullable().after('credited_amount');
table.string('discount_type').nullable().after('discount'); table.string('discount_type').nullable().after('discount');
table.decimal('adjustment', 10, 2).nullable().after('discount_type'); table.decimal('adjustment', 10, 2).nullable().after('discount_type');
}); });
}; };
@@ -2,20 +2,20 @@
* @param { import("knex").Knex } knex * @param { import("knex").Knex } knex
* @returns { Promise<void> } * @returns { Promise<void> }
*/ */
exports.up = function(knex) { exports.up = function (knex) {
return knex.schema.alterTable('sales_estimates', (table) => { return knex.schema.alterTable('sales_estimates', (table) => {
table.decimal('discount', 10, 2).nullable().after('amount'); table.decimal('discount', 10, 2).nullable().after('amount');
table.string('discount_type').nullable().after('discount'); table.string('discount_type').nullable().after('discount');
table.decimal('adjustment', 10, 2).nullable().after('discount_type'); table.decimal('adjustment', 10, 2).nullable().after('discount_type');
}); });
}; };
/** /**
* @param { import("knex").Knex } knex * @param { import("knex").Knex } knex
* @returns { Promise<void> } * @returns { Promise<void> }
*/ */
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.alterTable('sales_estimates', (table) => { return knex.schema.alterTable('sales_estimates', (table) => {
table.dropColumn('discount'); table.dropColumn('discount');
table.dropColumn('discount_type'); table.dropColumn('discount_type');
@@ -2,7 +2,7 @@
* @param { import("knex").Knex } knex * @param { import("knex").Knex } knex
* @returns { Promise<void> } * @returns { Promise<void> }
*/ */
exports.up = function(knex) { exports.up = function (knex) {
return knex.schema.alterTable('sales_receipts', (table) => { return knex.schema.alterTable('sales_receipts', (table) => {
table.decimal('discount', 10, 2).nullable().after('amount'); table.decimal('discount', 10, 2).nullable().after('amount');
table.string('discount_type').nullable().after('discount'); table.string('discount_type').nullable().after('discount');
@@ -15,7 +15,7 @@ exports.up = function(knex) {
* @param { import("knex").Knex } knex * @param { import("knex").Knex } knex
* @returns { Promise<void> } * @returns { Promise<void> }
*/ */
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.alterTable('sales_receipts', (table) => { return knex.schema.alterTable('sales_receipts', (table) => {
table.dropColumn('discount'); table.dropColumn('discount');
table.dropColumn('discount_type'); table.dropColumn('discount_type');
@@ -2,7 +2,7 @@
* @param { import("knex").Knex } knex * @param { import("knex").Knex } knex
* @returns { Promise<void> } * @returns { Promise<void> }
*/ */
exports.up = function(knex) { exports.up = function (knex) {
return knex.schema.alterTable('bills', (table) => { return knex.schema.alterTable('bills', (table) => {
// Discount. // Discount.
table.decimal('discount', 10, 2).nullable().after('amount'); table.decimal('discount', 10, 2).nullable().after('amount');
@@ -17,7 +17,7 @@ exports.up = function(knex) {
* @param { import("knex").Knex } knex * @param { import("knex").Knex } knex
* @returns { Promise<void> } * @returns { Promise<void> }
*/ */
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.alterTable('bills', (table) => { return knex.schema.alterTable('bills', (table) => {
table.dropColumn('discount'); table.dropColumn('discount');
table.dropColumn('discount_type'); table.dropColumn('discount_type');
@@ -2,7 +2,7 @@
* @param { import("knex").Knex } knex * @param { import("knex").Knex } knex
* @returns { Promise<void> } * @returns { Promise<void> }
*/ */
exports.up = function(knex) { exports.up = function (knex) {
return knex.schema.alterTable('credit_notes', (table) => { return knex.schema.alterTable('credit_notes', (table) => {
table.decimal('discount', 10, 2).nullable().after('exchange_rate'); table.decimal('discount', 10, 2).nullable().after('exchange_rate');
table.string('discount_type').nullable().after('discount'); table.string('discount_type').nullable().after('discount');
@@ -14,7 +14,7 @@ exports.up = function(knex) {
* @param { import("knex").Knex } knex * @param { import("knex").Knex } knex
* @returns { Promise<void> } * @returns { Promise<void> }
*/ */
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.alterTable('credit_notes', (table) => { return knex.schema.alterTable('credit_notes', (table) => {
table.dropColumn('discount'); table.dropColumn('discount');
table.dropColumn('discount_type'); table.dropColumn('discount_type');
@@ -2,7 +2,7 @@
* @param { import("knex").Knex } knex * @param { import("knex").Knex } knex
* @returns { Promise<void> } * @returns { Promise<void> }
*/ */
exports.up = function(knex) { exports.up = function (knex) {
return knex.schema.alterTable('vendor_credits', (table) => { return knex.schema.alterTable('vendor_credits', (table) => {
table.decimal('discount', 10, 2).nullable().after('amount'); table.decimal('discount', 10, 2).nullable().after('amount');
table.string('discount_type').nullable().after('discount'); table.string('discount_type').nullable().after('discount');
@@ -14,7 +14,7 @@ exports.up = function(knex) {
* @param { import("knex").Knex } knex * @param { import("knex").Knex } knex
* @returns { Promise<void> } * @returns { Promise<void> }
*/ */
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.alterTable('vendor_credits', (table) => { return knex.schema.alterTable('vendor_credits', (table) => {
table.dropColumn('discount'); table.dropColumn('discount');
table.dropColumn('discount_type'); table.dropColumn('discount_type');
@@ -1,12 +1,11 @@
exports.up = function (knex) {
exports.up = function(knex) { return knex.schema.alterTable('contacts', (table) => {
return knex.schema.alterTable('contacts', table => {
table.string('code').nullable().unique(); table.string('code').nullable().unique();
}); });
}; };
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema.alterTable('contacts', table => { return knex.schema.alterTable('contacts', (table) => {
table.dropColumn('code'); table.dropColumn('code');
}); });
}; };
@@ -2,9 +2,9 @@ import { TenantSeeder } from '@/libs/migration-seed/TenantSeeder';
export default class SeedSettings extends TenantSeeder { export default class SeedSettings extends TenantSeeder {
/** /**
* *
* @param knex * @param knex
* @returns * @returns
*/ */
async up(knex) { async up(knex) {
const costAccount = await knex('accounts') const costAccount = await knex('accounts')
@@ -1 +1 @@
// .gitkeep // .gitkeep
@@ -20,8 +20,8 @@ export class AccountTypesUtils {
/** /**
* Retrieve account type by the given account type key. * Retrieve account type by the given account type key.
* @param {string} key * @param {string} key
* @param {string} accessor * @param {string} accessor
*/ */
static getType(key: string, accessor?: string) { static getType(key: string, accessor?: string) {
const type = ACCOUNT_TYPES.find((type) => type.key === key); const type = ACCOUNT_TYPES.find((type) => type.key === key);
@@ -34,7 +34,7 @@ export class AccountTypesUtils {
/** /**
* Retrieve accounts types by the parent account type. * Retrieve accounts types by the parent account type.
* @param {string} parentType * @param {string} parentType
*/ */
static getTypesByParentType(parentType: string) { static getTypesByParentType(parentType: string) {
return ACCOUNT_TYPES.filter((type) => type.parentType === parentType); return ACCOUNT_TYPES.filter((type) => type.parentType === parentType);
@@ -42,16 +42,16 @@ export class AccountTypesUtils {
/** /**
* Retrieve accounts types by the given account normal. * Retrieve accounts types by the given account normal.
* @param {string} normal * @param {string} normal
*/ */
static getTypesByNormal(normal: string) { static getTypesByNormal(normal: string) {
return ACCOUNT_TYPES.filter((type) => type.normal === normal); return ACCOUNT_TYPES.filter((type) => type.normal === normal);
} }
/** /**
* Detarmines whether the root type equals the account type. * Detarmines whether the root type equals the account type.
* @param {string} key * @param {string} key
* @param {string} rootType * @param {string} rootType
*/ */
static isRootTypeEqualsKey(key: string, rootType: string): boolean { static isRootTypeEqualsKey(key: string, rootType: string): boolean {
return ACCOUNT_TYPES.some((type) => { return ACCOUNT_TYPES.some((type) => {
@@ -79,7 +79,7 @@ export class AccountTypesUtils {
/** /**
* Detarmines whether account type has balance sheet. * Detarmines whether account type has balance sheet.
* @param {string} key - Account type key. * @param {string} key - Account type key.
* *
*/ */
static isTypeBalanceSheet(key: string): boolean { static isTypeBalanceSheet(key: string): boolean {
return ACCOUNT_TYPES.some((type) => { return ACCOUNT_TYPES.some((type) => {
@@ -98,4 +98,4 @@ export class AccountTypesUtils {
return isType && type.incomeSheet; return isType && type.incomeSheet;
}); });
} }
} }
@@ -5,12 +5,12 @@ import { PageProperties } from './_types';
export class ConverterUtils { export class ConverterUtils {
public static injectPageProperties( public static injectPageProperties(
data: FormData, data: FormData,
pageProperties: PageProperties pageProperties: PageProperties,
): void { ): void {
if (pageProperties.size) { if (pageProperties.size) {
GotenbergUtils.assert( GotenbergUtils.assert(
pageProperties.size.width >= 1.0 && pageProperties.size.height >= 1.5, pageProperties.size.width >= 1.0 && pageProperties.size.height >= 1.5,
'size is smaller than the minimum printing requirements (i.e. 1.0 x 1.5 in)' 'size is smaller than the minimum printing requirements (i.e. 1.0 x 1.5 in)',
); );
data.append('paperWidth', pageProperties.size.width); data.append('paperWidth', pageProperties.size.width);
@@ -22,7 +22,7 @@ export class ConverterUtils {
pageProperties.margins.bottom >= 0 && pageProperties.margins.bottom >= 0 &&
pageProperties.margins.left >= 0 && pageProperties.margins.left >= 0 &&
pageProperties.margins.left >= 0, pageProperties.margins.left >= 0,
'negative margins are not allowed' 'negative margins are not allowed',
); );
data.append('marginTop', pageProperties.margins.top); data.append('marginTop', pageProperties.margins.top);
data.append('marginBottom', pageProperties.margins.bottom); data.append('marginBottom', pageProperties.margins.bottom);
@@ -32,7 +32,7 @@ export class ConverterUtils {
if (pageProperties.preferCssPageSize) { if (pageProperties.preferCssPageSize) {
data.append( data.append(
'preferCssPageSize', 'preferCssPageSize',
String(pageProperties.preferCssPageSize) String(pageProperties.preferCssPageSize),
); );
} }
if (pageProperties.printBackground) { if (pageProperties.printBackground) {
@@ -44,7 +44,7 @@ export class ConverterUtils {
if (pageProperties.scale) { if (pageProperties.scale) {
GotenbergUtils.assert( GotenbergUtils.assert(
pageProperties.scale >= 0.1 && pageProperties.scale <= 2.0, pageProperties.scale >= 0.1 && pageProperties.scale <= 2.0,
'scale is outside of [0.1 - 2] range' 'scale is outside of [0.1 - 2] range',
); );
data.append('scale', pageProperties.scale); data.append('scale', pageProperties.scale);
} }
@@ -55,11 +55,11 @@ export class ConverterUtils {
pageProperties.nativePageRanges.to > 0 && pageProperties.nativePageRanges.to > 0 &&
pageProperties.nativePageRanges.to >= pageProperties.nativePageRanges.to >=
pageProperties.nativePageRanges.from, pageProperties.nativePageRanges.from,
'page ranges syntax error' 'page ranges syntax error',
); );
data.append( data.append(
'nativePageRanges', 'nativePageRanges',
`${pageProperties.nativePageRanges.from}-${pageProperties.nativePageRanges.to}` `${pageProperties.nativePageRanges.from}-${pageProperties.nativePageRanges.to}`,
); );
} }
} }
@@ -1,4 +1,4 @@
import * as FormData from 'form-data'; import * as FormData from 'form-data';
import { IConverter, PageProperties, PdfFormat, ChromiumRoute } from './_types'; import { IConverter, PageProperties, PdfFormat, ChromiumRoute } from './_types';
import { ConverterUtils } from './ConvertUtils'; import { ConverterUtils } from './ConvertUtils';
import { Converter } from './Converter'; import { Converter } from './Converter';
+313 -313
View File
@@ -14,337 +14,337 @@
* @param circular A boolean to allow circular dependencies * @param circular A boolean to allow circular dependencies
*/ */
function createDFS(edges, leavesOnly, result, circular) { function createDFS(edges, leavesOnly, result, circular) {
var visited = {}; var visited = {};
return function (start) { return function (start) {
if (visited[start]) { if (visited[start]) {
return; return;
} }
var inCurrentPath = {}; var inCurrentPath = {};
var currentPath = []; var currentPath = [];
var todo = []; // used as a stack var todo = []; // used as a stack
todo.push({ node: start, processed: false }); todo.push({ node: start, processed: false });
while (todo.length > 0) { while (todo.length > 0) {
var current = todo[todo.length - 1]; // peek at the todo stack var current = todo[todo.length - 1]; // peek at the todo stack
var processed = current.processed; var processed = current.processed;
var node = current.node; var node = current.node;
if (!processed) { if (!processed) {
// Haven't visited edges yet (visiting phase) // Haven't visited edges yet (visiting phase)
if (visited[node]) { if (visited[node]) {
todo.pop();
continue;
} else if (inCurrentPath[node]) {
// It's not a DAG
if (circular) {
todo.pop();
// If we're tolerating cycles, don't revisit the node
continue;
}
currentPath.push(node);
throw new DepGraphCycleError(currentPath);
}
inCurrentPath[node] = true;
currentPath.push(node);
var nodeEdges = edges[node];
// (push edges onto the todo stack in reverse order to be order-compatible with the old DFS implementation)
for (var i = nodeEdges.length - 1; i >= 0; i--) {
todo.push({ node: nodeEdges[i], processed: false });
}
current.processed = true;
} else {
// Have visited edges (stack unrolling phase)
todo.pop(); todo.pop();
currentPath.pop(); continue;
inCurrentPath[node] = false; } else if (inCurrentPath[node]) {
visited[node] = true; // It's not a DAG
if (!leavesOnly || edges[node].length === 0) { if (circular) {
result.push(node); todo.pop();
// If we're tolerating cycles, don't revisit the node
continue;
} }
currentPath.push(node);
throw new DepGraphCycleError(currentPath);
} }
}
}; inCurrentPath[node] = true;
} currentPath.push(node);
var nodeEdges = edges[node];
/** // (push edges onto the todo stack in reverse order to be order-compatible with the old DFS implementation)
* Simple Dependency Graph for (var i = nodeEdges.length - 1; i >= 0; i--) {
*/ todo.push({ node: nodeEdges[i], processed: false });
var DepGraph = (DepGraph = function DepGraph(opts) {
this.nodes = {}; // Node -> Node/Data (treated like a Set)
this.outgoingEdges = {}; // Node -> [Dependency Node]
this.incomingEdges = {}; // Node -> [Dependant Node]
this.circular = opts && !!opts.circular; // Allows circular deps
});
DepGraph.fromArray = (
items,
options = { itemId: 'id', parentItemId: 'parent_id' }
) => {
const depGraph = new DepGraph();
items.forEach((item) => {
depGraph.addNode(item[options.itemId], item);
});
items.forEach((item) => {
if (item[options.parentItemId]) {
depGraph.addDependency(item[options.parentItemId], item[options.itemId]);
}
});
return depGraph;
};
DepGraph.prototype = {
/**
* The number of nodes in the graph.
*/
size: function () {
return Object.keys(this.nodes).length;
},
/**
* Add a node to the dependency graph. If a node already exists, this method will do nothing.
*/
addNode: function (node, data) {
if (!this.hasNode(node)) {
// Checking the arguments length allows the user to add a node with undefined data
if (arguments.length === 2) {
this.nodes[node] = data;
} else {
this.nodes[node] = node;
} }
this.outgoingEdges[node] = []; current.processed = true;
this.incomingEdges[node] = [];
}
},
/**
* Remove a node from the dependency graph. If a node does not exist, this method will do nothing.
*/
removeNode: function (node) {
if (this.hasNode(node)) {
delete this.nodes[node];
delete this.outgoingEdges[node];
delete this.incomingEdges[node];
[this.incomingEdges, this.outgoingEdges].forEach(function (edgeList) {
Object.keys(edgeList).forEach(function (key) {
var idx = edgeList[key].indexOf(node);
if (idx >= 0) {
edgeList[key].splice(idx, 1);
}
}, this);
});
}
},
/**
* Check if a node exists in the graph
*/
hasNode: function (node) {
return this.nodes.hasOwnProperty(node);
},
/**
* Get the data associated with a node name
*/
getNodeData: function (node) {
if (this.hasNode(node)) {
return this.nodes[node];
} else { } else {
throw new Error('Node does not exist: ' + node); // Have visited edges (stack unrolling phase)
todo.pop();
currentPath.pop();
inCurrentPath[node] = false;
visited[node] = true;
if (!leavesOnly || edges[node].length === 0) {
result.push(node);
}
} }
}, }
};
/** }
* Set the associated data for a given node name. If the node does not exist, this method will throw an error
*/ /**
setNodeData: function (node, data) { * Simple Dependency Graph
if (this.hasNode(node)) { */
var DepGraph = (DepGraph = function DepGraph(opts) {
this.nodes = {}; // Node -> Node/Data (treated like a Set)
this.outgoingEdges = {}; // Node -> [Dependency Node]
this.incomingEdges = {}; // Node -> [Dependant Node]
this.circular = opts && !!opts.circular; // Allows circular deps
});
DepGraph.fromArray = (
items,
options = { itemId: 'id', parentItemId: 'parent_id' },
) => {
const depGraph = new DepGraph();
items.forEach((item) => {
depGraph.addNode(item[options.itemId], item);
});
items.forEach((item) => {
if (item[options.parentItemId]) {
depGraph.addDependency(item[options.parentItemId], item[options.itemId]);
}
});
return depGraph;
};
DepGraph.prototype = {
/**
* The number of nodes in the graph.
*/
size: function () {
return Object.keys(this.nodes).length;
},
/**
* Add a node to the dependency graph. If a node already exists, this method will do nothing.
*/
addNode: function (node, data) {
if (!this.hasNode(node)) {
// Checking the arguments length allows the user to add a node with undefined data
if (arguments.length === 2) {
this.nodes[node] = data; this.nodes[node] = data;
} else { } else {
throw new Error('Node does not exist: ' + node); this.nodes[node] = node;
} }
}, this.outgoingEdges[node] = [];
/** this.incomingEdges[node] = [];
* Add a dependency between two nodes. If either of the nodes does not exist, }
* an Error will be thrown. },
*/ /**
addDependency: function (from, to) { * Remove a node from the dependency graph. If a node does not exist, this method will do nothing.
if (!this.hasNode(from)) { */
throw new Error('Node does not exist: ' + from); removeNode: function (node) {
} if (this.hasNode(node)) {
if (!this.hasNode(to)) { delete this.nodes[node];
throw new Error('Node does not exist: ' + to); delete this.outgoingEdges[node];
} delete this.incomingEdges[node];
if (this.outgoingEdges[from].indexOf(to) === -1) { [this.incomingEdges, this.outgoingEdges].forEach(function (edgeList) {
this.outgoingEdges[from].push(to); Object.keys(edgeList).forEach(function (key) {
} var idx = edgeList[key].indexOf(node);
if (this.incomingEdges[to].indexOf(from) === -1) { if (idx >= 0) {
this.incomingEdges[to].push(from); edgeList[key].splice(idx, 1);
} }
return true; }, this);
},
/**
* Remove a dependency between two nodes.
*/
removeDependency: function (from, to) {
var idx;
if (this.hasNode(from)) {
idx = this.outgoingEdges[from].indexOf(to);
if (idx >= 0) {
this.outgoingEdges[from].splice(idx, 1);
}
}
if (this.hasNode(to)) {
idx = this.incomingEdges[to].indexOf(from);
if (idx >= 0) {
this.incomingEdges[to].splice(idx, 1);
}
}
},
/**
* Return a clone of the dependency graph. If any custom data is attached
* to the nodes, it will only be shallow copied.
*/
clone: function () {
var source = this;
var result = new DepGraph();
var keys = Object.keys(source.nodes);
keys.forEach(function (n) {
result.nodes[n] = source.nodes[n];
result.outgoingEdges[n] = source.outgoingEdges[n].slice(0);
result.incomingEdges[n] = source.incomingEdges[n].slice(0);
}); });
return result; }
}, },
/** /**
* Get an array containing the nodes that the specified node depends on (transitively). * Check if a node exists in the graph
* */
* Throws an Error if the graph has a cycle, or the specified node does not exist. hasNode: function (node) {
* return this.nodes.hasOwnProperty(node);
* If `leavesOnly` is true, only nodes that do not depend on any other nodes will be returned },
* in the array. /**
*/ * Get the data associated with a node name
dependenciesOf: function (node, leavesOnly) { */
if (this.hasNode(node)) { getNodeData: function (node) {
var result = []; if (this.hasNode(node)) {
var DFS = createDFS( return this.nodes[node];
this.outgoingEdges, } else {
leavesOnly, throw new Error('Node does not exist: ' + node);
result, }
this.circular },
);
DFS(node); /**
var idx = result.indexOf(node); * Set the associated data for a given node name. If the node does not exist, this method will throw an error
if (idx >= 0) { */
result.splice(idx, 1); setNodeData: function (node, data) {
} if (this.hasNode(node)) {
return result; this.nodes[node] = data;
} else { } else {
throw new Error('Node does not exist: ' + node); throw new Error('Node does not exist: ' + node);
}
},
/**
* Add a dependency between two nodes. If either of the nodes does not exist,
* an Error will be thrown.
*/
addDependency: function (from, to) {
if (!this.hasNode(from)) {
throw new Error('Node does not exist: ' + from);
}
if (!this.hasNode(to)) {
throw new Error('Node does not exist: ' + to);
}
if (this.outgoingEdges[from].indexOf(to) === -1) {
this.outgoingEdges[from].push(to);
}
if (this.incomingEdges[to].indexOf(from) === -1) {
this.incomingEdges[to].push(from);
}
return true;
},
/**
* Remove a dependency between two nodes.
*/
removeDependency: function (from, to) {
var idx;
if (this.hasNode(from)) {
idx = this.outgoingEdges[from].indexOf(to);
if (idx >= 0) {
this.outgoingEdges[from].splice(idx, 1);
} }
}, }
/**
* get an array containing the nodes that depend on the specified node (transitively). if (this.hasNode(to)) {
* idx = this.incomingEdges[to].indexOf(from);
* Throws an Error if the graph has a cycle, or the specified node does not exist. if (idx >= 0) {
* this.incomingEdges[to].splice(idx, 1);
* If `leavesOnly` is true, only nodes that do not have any dependants will be returned in the array.
*/
dependantsOf: function (node, leavesOnly) {
if (this.hasNode(node)) {
var result = [];
var DFS = createDFS(
this.incomingEdges,
leavesOnly,
result,
this.circular
);
DFS(node);
var idx = result.indexOf(node);
if (idx >= 0) {
result.splice(idx, 1);
}
return result;
} else {
throw new Error('Node does not exist: ' + node);
} }
}, }
/** },
* Construct the overall processing order for the dependency graph. /**
* * Return a clone of the dependency graph. If any custom data is attached
* Throws an Error if the graph has a cycle. * to the nodes, it will only be shallow copied.
* */
* If `leavesOnly` is true, only nodes that do not depend on any other nodes will be returned. clone: function () {
*/ var source = this;
overallOrder: function (leavesOnly) { var result = new DepGraph();
var self = this; var keys = Object.keys(source.nodes);
keys.forEach(function (n) {
result.nodes[n] = source.nodes[n];
result.outgoingEdges[n] = source.outgoingEdges[n].slice(0);
result.incomingEdges[n] = source.incomingEdges[n].slice(0);
});
return result;
},
/**
* Get an array containing the nodes that the specified node depends on (transitively).
*
* Throws an Error if the graph has a cycle, or the specified node does not exist.
*
* If `leavesOnly` is true, only nodes that do not depend on any other nodes will be returned
* in the array.
*/
dependenciesOf: function (node, leavesOnly) {
if (this.hasNode(node)) {
var result = []; var result = [];
var keys = Object.keys(this.nodes); var DFS = createDFS(
if (keys.length === 0) { this.outgoingEdges,
return result; // Empty graph leavesOnly,
} else { result,
if (!this.circular) { this.circular,
// Look for cycles - we run the DFS starting at all the nodes in case there );
// are several disconnected subgraphs inside this dependency graph. DFS(node);
var CycleDFS = createDFS(this.outgoingEdges, false, [], this.circular); var idx = result.indexOf(node);
keys.forEach(function (n) { if (idx >= 0) {
CycleDFS(n); result.splice(idx, 1);
}); }
} return result;
} else {
var DFS = createDFS( throw new Error('Node does not exist: ' + node);
this.outgoingEdges, }
leavesOnly, },
result, /**
this.circular * get an array containing the nodes that depend on the specified node (transitively).
); *
// Find all potential starting points (nodes with nothing depending on them) an * Throws an Error if the graph has a cycle, or the specified node does not exist.
// run a DFS starting at these points to get the order *
* If `leavesOnly` is true, only nodes that do not have any dependants will be returned in the array.
*/
dependantsOf: function (node, leavesOnly) {
if (this.hasNode(node)) {
var result = [];
var DFS = createDFS(
this.incomingEdges,
leavesOnly,
result,
this.circular,
);
DFS(node);
var idx = result.indexOf(node);
if (idx >= 0) {
result.splice(idx, 1);
}
return result;
} else {
throw new Error('Node does not exist: ' + node);
}
},
/**
* Construct the overall processing order for the dependency graph.
*
* Throws an Error if the graph has a cycle.
*
* If `leavesOnly` is true, only nodes that do not depend on any other nodes will be returned.
*/
overallOrder: function (leavesOnly) {
var self = this;
var result = [];
var keys = Object.keys(this.nodes);
if (keys.length === 0) {
return result; // Empty graph
} else {
if (!this.circular) {
// Look for cycles - we run the DFS starting at all the nodes in case there
// are several disconnected subgraphs inside this dependency graph.
var CycleDFS = createDFS(this.outgoingEdges, false, [], this.circular);
keys.forEach(function (n) {
CycleDFS(n);
});
}
var DFS = createDFS(
this.outgoingEdges,
leavesOnly,
result,
this.circular,
);
// Find all potential starting points (nodes with nothing depending on them) an
// run a DFS starting at these points to get the order
keys
.filter(function (node) {
return self.incomingEdges[node].length === 0;
})
.forEach(function (n) {
DFS(n);
});
// If we're allowing cycles - we need to run the DFS against any remaining
// nodes that did not end up in the initial result (as they are part of a
// subgraph that does not have a clear starting point)
if (this.circular) {
keys keys
.filter(function (node) { .filter(function (node) {
return self.incomingEdges[node].length === 0; return result.indexOf(node) === -1;
}) })
.forEach(function (n) { .forEach(function (n) {
DFS(n); DFS(n);
}); });
// If we're allowing cycles - we need to run the DFS against any remaining
// nodes that did not end up in the initial result (as they are part of a
// subgraph that does not have a clear starting point)
if (this.circular) {
keys
.filter(function (node) {
return result.indexOf(node) === -1;
})
.forEach(function (n) {
DFS(n);
});
}
return result;
} }
},
return result;
mapNodes(mapper) {},
};
/**
* Cycle error, including the path of the cycle.
*/
var DepGraphCycleError = (exports.DepGraphCycleError = function (cyclePath) {
var message = 'Dependency Cycle Found: ' + cyclePath.join(' -> ');
var instance = new Error(message);
instance.cyclePath = cyclePath;
Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
if (Error.captureStackTrace) {
Error.captureStackTrace(instance, DepGraphCycleError);
} }
return instance; },
});
DepGraphCycleError.prototype = Object.create(Error.prototype, { mapNodes(mapper) {},
constructor: { };
value: Error,
enumerable: false, /**
writable: true, * Cycle error, including the path of the cycle.
configurable: true, */
}, var DepGraphCycleError = (exports.DepGraphCycleError = function (cyclePath) {
}); var message = 'Dependency Cycle Found: ' + cyclePath.join(' -> ');
Object.setPrototypeOf(DepGraphCycleError, Error); var instance = new Error(message);
instance.cyclePath = cyclePath;
export default DepGraph; Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
if (Error.captureStackTrace) {
Error.captureStackTrace(instance, DepGraphCycleError);
}
return instance;
});
DepGraphCycleError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true,
},
});
Object.setPrototypeOf(DepGraphCycleError, Error);
export default DepGraph;
@@ -1,4 +1,3 @@
const OperationType = { const OperationType = {
LOGIC: 'LOGIC', LOGIC: 'LOGIC',
STRING: 'STRING', STRING: 'STRING',
@@ -20,7 +19,7 @@ export class Lexer {
'?': OperationType.LOGIC, '?': OperationType.LOGIC,
':': OperationType.LOGIC, ':': OperationType.LOGIC,
'\'': OperationType.STRING, "'": OperationType.STRING,
'"': OperationType.STRING, '"': OperationType.STRING,
'!': OperationType.COMPARISON, '!': OperationType.COMPARISON,
@@ -79,11 +78,13 @@ export class Lexer {
// we must move the pos forward // we must move the pos forward
// so here we should throw error, for example `1 & 2` // so here we should throw error, for example `1 & 2`
if (pos === this.currentIndex && tok !== undefined) { if (pos === this.currentIndex && tok !== undefined) {
const err = new Error(`unkonw token ${tok} from input string ${this.input}`); const err = new Error(
`unkonw token ${tok} from input string ${this.input}`,
);
err.name = 'UnknowToken'; err.name = 'UnknowToken';
throw err; throw err;
} }
} while (tok !== undefined) } while (tok !== undefined);
return this.tokenList; return this.tokenList;
} }
@@ -103,7 +104,9 @@ export class Lexer {
* @param index * @param index
*/ */
receiveToken(index = 1) { receiveToken(index = 1) {
const tok = this.input.slice(this.currentIndex, this.currentIndex + index).trim(); const tok = this.input
.slice(this.currentIndex, this.currentIndex + index)
.trim();
// skip empty string // skip empty string
if (tok) { if (tok) {
this.tokenList.push(tok); this.tokenList.push(tok);
@@ -38,8 +38,8 @@ export class Parser {
} }
/** /**
* *
* @return {Node | string} =- * @return {Node | string} =-
*/ */
parse() { parse() {
let tok; let tok;
@@ -67,7 +67,9 @@ export class Parser {
root.right = this.parseStatement(); root.right = this.parseStatement();
} else { } else {
if (typeof tok !== 'string') { if (typeof tok !== 'string') {
throw new Error('operation must be string, but get ' + JSON.stringify(tok)); throw new Error(
'operation must be string, but get ' + JSON.stringify(tok),
);
} }
root = this.addNode(tok, this.parseStatement(), root); root = this.addNode(tok, this.parseStatement(), root);
} }
@@ -86,19 +88,21 @@ export class Parser {
} }
/** /**
* *
* @param {string} operation * @param {string} operation
* @param {Node|String|null} right * @param {Node|String|null} right
* @param {Node} root * @param {Node} root
*/ */
addNode(operation, right, root) { addNode(operation, right, root) {
let pre = root; let pre = root;
if (this.compare(pre.operation, operation) < 0 && !pre.grouped) { if (this.compare(pre.operation, operation) < 0 && !pre.grouped) {
while (
while (pre.right !== null && pre.right !== null &&
typeof pre.right !== 'string' && typeof pre.right !== 'string' &&
this.compare(pre.right.operation, operation) < 0 && !pre.right.grouped) { this.compare(pre.right.operation, operation) < 0 &&
!pre.right.grouped
) {
pre = pre.right; pre = pre.right;
} }
@@ -113,13 +117,13 @@ export class Parser {
left: pre, left: pre,
right, right,
operation, operation,
} };
} }
/** /**
* *
* @param {String} a * @param {String} a
* @param {String} b * @param {String} b
*/ */
compare(a, b) { compare(a, b) {
if (!OPERATION.hasOwnProperty(a) || !OPERATION.hasOwnProperty(b)) { if (!OPERATION.hasOwnProperty(a) || !OPERATION.hasOwnProperty(b)) {
@@ -149,12 +153,20 @@ export class Parser {
} }
if (token === '!') { if (token === '!') {
return { left: null, operation: token, right: this.parseStatement() } return { left: null, operation: token, right: this.parseStatement() };
} }
// 3 > -12 or -12 + 10 // 3 > -12 or -12 + 10
if (token === '-' && (OPERATION[this.prevToken()] > 0 || this.prevToken() === undefined)) { if (
return { left: '0', operation: token, right: this.parseStatement(), grouped: true }; token === '-' &&
(OPERATION[this.prevToken()] > 0 || this.prevToken() === undefined)
) {
return {
left: '0',
operation: token,
right: this.parseStatement(),
grouped: true,
};
} }
return token; return token;
@@ -22,7 +22,9 @@ export class QueryParser {
parseNode(node) { parseNode(node) {
if (typeof node === 'string') { if (typeof node === 'string') {
const nodeQuery = this.getQuery(node); const nodeQuery = this.getQuery(node);
return (query) => { nodeQuery(query); }; return (query) => {
nodeQuery(query);
};
} }
if (OPERATION[node.operation] === undefined) { if (OPERATION[node.operation] === undefined) {
throw new Error(`unknow expression ${node.operation}`); throw new Error(`unknow expression ${node.operation}`);
@@ -34,16 +36,26 @@ export class QueryParser {
case '&&': case '&&':
case 'AND': case 'AND':
default: default:
return (nodeQuery) => nodeQuery.where((query) => { return (nodeQuery) =>
query.where((q) => { leftQuery(q); }); nodeQuery.where((query) => {
query.andWhere((q) => { rightQuery(q); }); query.where((q) => {
}); leftQuery(q);
});
query.andWhere((q) => {
rightQuery(q);
});
});
case '||': case '||':
case 'OR': case 'OR':
return (nodeQuery) => nodeQuery.where((query) => { return (nodeQuery) =>
query.where((q) => { leftQuery(q); }); nodeQuery.where((query) => {
query.orWhere((q) => { rightQuery(q); }); query.where((q) => {
}); leftQuery(q);
});
query.orWhere((q) => {
rightQuery(q);
});
});
} }
} }
@@ -61,4 +73,4 @@ export class QueryParser {
} }
return null; return null;
} }
} }
@@ -1,4 +1,3 @@
export class Seeder { export class Seeder {
knex: any; knex: any;
@@ -8,4 +7,3 @@ export class Seeder {
up(knex) {} up(knex) {}
down(knex) {} down(knex) {}
} }
@@ -37,7 +37,7 @@ export default function getMergedConfig(config, currentConfig) {
mergedConfig.migrationSource = new FsMigrations( mergedConfig.migrationSource = new FsMigrations(
mergedConfig.directory, mergedConfig.directory,
mergedConfig.sortDirsSeparately, mergedConfig.sortDirsSeparately,
mergedConfig.loadExtensions mergedConfig.loadExtensions,
); );
} }
return mergedConfig; return mergedConfig;
@@ -35,7 +35,7 @@ export function getLockTableName(tableName: string): string {
*/ */
export function getLockTableNameWithSchema( export function getLockTableNameWithSchema(
tableName: string, tableName: string,
schemaName = null schemaName = null,
): string { ): string {
return schemaName return schemaName
? `${schemaName} + ${getLockTableName(tableName)}` ? `${schemaName} + ${getLockTableName(tableName)}`
+5 -4
View File
@@ -50,13 +50,14 @@ export class PaginationQueryBuilder<
return this.delete(); return this.delete();
} }
// Only check HasManyRelation and ManyToManyRelation relations, as BelongsToOneRelation are just // Only check HasManyRelation and ManyToManyRelation relations, as BelongsToOneRelation are just
// foreign key references and shouldn't prevent deletion. Only dependent records should block deletion. // foreign key references and shouldn't prevent deletion. Only dependent records should block deletion.
const dependentRelationNames = relationNames.filter((name) => { const dependentRelationNames = relationNames.filter((name) => {
const relation = relationMappings[name]; const relation = relationMappings[name];
return relation && ( return (
relation.relation === Model.HasManyRelation || relation &&
relation.relation === Model.ManyToManyRelation (relation.relation === Model.HasManyRelation ||
relation.relation === Model.ManyToManyRelation)
); );
}); });
@@ -3,7 +3,9 @@ import { Model } from 'objection';
type Constructor<T = {}> = new (...args: any[]) => T; type Constructor<T = {}> = new (...args: any[]) => T;
export const withDateSessionMixin = <T extends Constructor<Model>>(BaseModel: T) => { export const withDateSessionMixin = <T extends Constructor<Model>>(
BaseModel: T,
) => {
return class DateSession extends BaseModel { return class DateSession extends BaseModel {
constructor(...args: any[]) { constructor(...args: any[]) {
super(...args); super(...args);
@@ -12,7 +14,7 @@ export const withDateSessionMixin = <T extends Constructor<Model>>(BaseModel: T)
get timestamps() { get timestamps() {
return []; return [];
} }
$beforeUpdate(opt, context) { $beforeUpdate(opt, context) {
const maybePromise = super.$beforeUpdate(opt, context); const maybePromise = super.$beforeUpdate(opt, context);
@@ -36,5 +38,5 @@ export const withDateSessionMixin = <T extends Constructor<Model>>(BaseModel: T)
} }
}); });
} }
} };
} };
@@ -48,7 +48,7 @@ import { AccountAction } from './Accounts.types';
@ApiCommonHeaders() @ApiCommonHeaders()
@UseGuards(AuthorizationGuard, PermissionGuard) @UseGuards(AuthorizationGuard, PermissionGuard)
export class AccountsController { export class AccountsController {
constructor(private readonly accountsApplication: AccountsApplication) { } constructor(private readonly accountsApplication: AccountsApplication) {}
@Post('validate-bulk-delete') @Post('validate-bulk-delete')
@HttpCode(200) @HttpCode(200)
@@ -42,7 +42,7 @@ export class AccountsApplication {
private readonly getAccountsService: GetAccountsService, private readonly getAccountsService: GetAccountsService,
private readonly bulkDeleteAccountsService: BulkDeleteAccountsService, private readonly bulkDeleteAccountsService: BulkDeleteAccountsService,
private readonly validateBulkDeleteAccountsService: ValidateBulkDeleteAccountsService, private readonly validateBulkDeleteAccountsService: ValidateBulkDeleteAccountsService,
) { } ) {}
/** /**
* Creates a new account. * Creates a new account.
@@ -11,7 +11,7 @@ import { Account } from './models/Account.model';
@Global() @Global()
export class AccountsExportable extends Exportable { export class AccountsExportable extends Exportable {
/** /**
* @param {AccountsApplication} accountsApplication * @param {AccountsApplication} accountsApplication
*/ */
constructor(private readonly accountsApplication: AccountsApplication) { constructor(private readonly accountsApplication: AccountsApplication) {
super(); super();
@@ -23,10 +23,7 @@ export class AccountsImportable extends Importable {
createAccountDTO: CreateAccountDTO, createAccountDTO: CreateAccountDTO,
trx?: Knex.Transaction, trx?: Knex.Transaction,
) { ) {
return this.createAccountService.createAccount( return this.createAccountService.createAccount(createAccountDTO, trx);
createAccountDTO,
trx,
);
} }
/** /**
@@ -6,7 +6,7 @@ import { DeleteAccount } from './DeleteAccount.service';
@Injectable() @Injectable()
export class BulkDeleteAccountsService { export class BulkDeleteAccountsService {
constructor(private readonly deleteAccountService: DeleteAccount) { } constructor(private readonly deleteAccountService: DeleteAccount) {}
/** /**
* Deletes multiple accounts. * Deletes multiple accounts.
@@ -38,4 +38,3 @@ export class BulkDeleteAccountsService {
} }
} }
} }
@@ -17,7 +17,7 @@ export class CommandAccountValidators {
@Inject(Account.name) @Inject(Account.name)
private readonly accountModel: TenantModelProxy<typeof Account>, private readonly accountModel: TenantModelProxy<typeof Account>,
private readonly accountRepository: AccountRepository, private readonly accountRepository: AccountRepository,
) { } ) {}
/** /**
* Throws error if the account was prefined. * Throws error if the account was prefined.
@@ -18,7 +18,7 @@ export class DeleteAccount {
private eventEmitter: EventEmitter2, private eventEmitter: EventEmitter2,
private uow: UnitOfWork, private uow: UnitOfWork,
private validator: CommandAccountValidators, private validator: CommandAccountValidators,
) { } ) {}
/** /**
* Authorize account delete. * Authorize account delete.
@@ -6,7 +6,7 @@ import { AccountTypesUtils } from './utils/AccountType.utils';
export class GetAccountTypesService { export class GetAccountTypesService {
/** /**
* Retrieve all accounts types. * Retrieve all accounts types.
* @param {number} tenantId - * @param {number} tenantId -
* @return {IAccountType} * @return {IAccountType}
*/ */
public getAccountsTypes() { public getAccountsTypes() {
@@ -19,7 +19,7 @@ export class GetAccountsService {
@Inject(Account.name) @Inject(Account.name)
private readonly accountModel: TenantModelProxy<typeof Account>, private readonly accountModel: TenantModelProxy<typeof Account>,
) { } ) {}
/** /**
* Retrieve accounts datatable list. * Retrieve accounts datatable list.
@@ -60,4 +60,3 @@ export class ValidateBulkDeleteAccountsService {
} }
} }
} }
@@ -1,9 +1,20 @@
import { IsArray, IsBoolean, IsEnum, IsInt, IsOptional, IsString, Min } from 'class-validator'; import {
IsArray,
IsBoolean,
IsEnum,
IsInt,
IsOptional,
IsString,
Min,
} from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger'; import { ApiPropertyOptional } from '@nestjs/swagger';
import { Transform } from 'class-transformer'; import { Transform } from 'class-transformer';
import { parseBoolean } from '@/utils/parse-boolean'; import { parseBoolean } from '@/utils/parse-boolean';
import { IAccountsStructureType } from '../Accounts.types'; import { IAccountsStructureType } from '../Accounts.types';
import { IFilterRole, ISortOrder } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import {
IFilterRole,
ISortOrder,
} from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { ToNumber } from '@/common/decorators/Validators'; import { ToNumber } from '@/common/decorators/Validators';
export class GetAccountsQueryDto { export class GetAccountsQueryDto {
@@ -114,4 +125,3 @@ export class GetAccountsQueryDto {
@Min(1) @Min(1)
pageSize?: number; pageSize?: number;
} }
@@ -1,4 +1,4 @@
import { ACCOUNT_TYPES } from "../Accounts.constants"; import { ACCOUNT_TYPES } from '../Accounts.constants';
export const AccountMeta = { export const AccountMeta = {
defaultFilterField: 'name', defaultFilterField: 'name',
@@ -196,4 +196,4 @@ function RootTypeFieldFilterQuery(query, role) {
*/ */
function NormalTypeFieldFilterQuery(query, role) { function NormalTypeFieldFilterQuery(query, role) {
query.modify('filterByAccountNormal', role.value); query.modify('filterByAccountNormal', role.value);
} }
@@ -14,11 +14,9 @@ export class MutateBaseCurrencyAccountsSubscriber {
* of the organization is mutated. * of the organization is mutated.
*/ */
@OnEvent(events.organization.baseCurrencyUpdated) @OnEvent(events.organization.baseCurrencyUpdated)
async updateAccountsCurrencyOnBaseCurrencyMutated({ async updateAccountsCurrencyOnBaseCurrencyMutated({ organizationDTO }) {
organizationDTO,
}) {
await this.mutateBaseCurrencyAccounts.mutateAllAccountsCurrency( await this.mutateBaseCurrencyAccounts.mutateAllAccountsCurrency(
organizationDTO.baseCurrency organizationDTO.baseCurrency,
); );
}; }
} }
@@ -15,7 +15,6 @@ describe('AppController', () => {
}); });
describe('root', () => { describe('root', () => {
it('should return "Hello World!"', () => { it('should return "Hello World!"', () => {});
});
}); });
}); });
@@ -10,7 +10,9 @@ import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis'
inject: [ConfigService], inject: [ConfigService],
useFactory: (configService: ConfigService) => { useFactory: (configService: ConfigService) => {
// Use in-memory storage with very high limits for test environment // Use in-memory storage with very high limits for test environment
const isTest = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined; const isTest =
process.env.NODE_ENV === 'test' ||
process.env.JEST_WORKER_ID !== undefined;
if (isTest) { if (isTest) {
return { return {
@@ -64,6 +66,4 @@ import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis'
}), }),
], ],
}) })
export class AppThrottleModule { } export class AppThrottleModule {}
@@ -1,32 +1,32 @@
import { Module } from "@nestjs/common"; import { Module } from '@nestjs/common';
import * as multerS3 from 'multer-s3'; import * as multerS3 from 'multer-s3';
import { S3_CLIENT, S3Module } from "../S3/S3.module"; import { S3_CLIENT, S3Module } from '../S3/S3.module';
import { DeleteAttachment } from "./DeleteAttachment"; import { DeleteAttachment } from './DeleteAttachment';
import { GetAttachment } from "./GetAttachment"; import { GetAttachment } from './GetAttachment';
import { GetAttachmentPresignedUrl } from "./GetAttachmentPresignedUrl"; import { GetAttachmentPresignedUrl } from './GetAttachmentPresignedUrl';
import { LinkAttachment } from "./LinkAttachment"; import { LinkAttachment } from './LinkAttachment';
import { UnlinkAttachment } from "./UnlinkAttachment"; import { UnlinkAttachment } from './UnlinkAttachment';
import { ValidateAttachments } from "./ValidateAttachments"; import { ValidateAttachments } from './ValidateAttachments';
import { AttachmentsOnBillPayments } from "./events/AttachmentsOnPaymentsMade"; import { AttachmentsOnBillPayments } from './events/AttachmentsOnPaymentsMade';
import { AttachmentsOnBills } from "./events/AttachmentsOnBills"; import { AttachmentsOnBills } from './events/AttachmentsOnBills';
import { AttachmentsOnCreditNote } from "./events/AttachmentsOnCreditNote"; import { AttachmentsOnCreditNote } from './events/AttachmentsOnCreditNote';
import { AttachmentsOnExpenses } from "./events/AttachmentsOnExpenses"; import { AttachmentsOnExpenses } from './events/AttachmentsOnExpenses';
import { AttachmentsOnPaymentsReceived } from "./events/AttachmentsOnPaymentsReceived"; import { AttachmentsOnPaymentsReceived } from './events/AttachmentsOnPaymentsReceived';
import { AttachmentsOnManualJournals } from "./events/AttachmentsOnManualJournals"; import { AttachmentsOnManualJournals } from './events/AttachmentsOnManualJournals';
import { AttachmentsOnVendorCredits } from "./events/AttachmentsOnVendorCredits"; import { AttachmentsOnVendorCredits } from './events/AttachmentsOnVendorCredits';
import { AttachmentsOnSaleInvoiceCreated } from "./events/AttachmentsOnSaleInvoice"; import { AttachmentsOnSaleInvoiceCreated } from './events/AttachmentsOnSaleInvoice';
import { AttachmentsOnSaleReceipt } from "./events/AttachmentsOnSaleReceipts"; import { AttachmentsOnSaleReceipt } from './events/AttachmentsOnSaleReceipts';
import { AttachmentsOnSaleEstimates } from "./events/AttachmentsOnSaleEstimates"; import { AttachmentsOnSaleEstimates } from './events/AttachmentsOnSaleEstimates';
import { AttachmentsController } from "./Attachments.controller"; import { AttachmentsController } from './Attachments.controller';
import { RegisterTenancyModel } from "../Tenancy/TenancyModels/Tenancy.module"; import { RegisterTenancyModel } from '../Tenancy/TenancyModels/Tenancy.module';
import { DocumentModel } from "./models/Document.model"; import { DocumentModel } from './models/Document.model';
import { DocumentLinkModel } from "./models/DocumentLink.model"; import { DocumentLinkModel } from './models/DocumentLink.model';
import { AttachmentsApplication } from "./AttachmentsApplication"; import { AttachmentsApplication } from './AttachmentsApplication';
import { UploadDocument } from "./UploadDocument"; import { UploadDocument } from './UploadDocument';
import { AttachmentUploadPipeline } from "./S3UploadPipeline"; import { AttachmentUploadPipeline } from './S3UploadPipeline';
import { MULTER_MODULE_OPTIONS } from "@/common/constants/files.constants"; import { MULTER_MODULE_OPTIONS } from '@/common/constants/files.constants';
import { ConfigService } from "@nestjs/config"; import { ConfigService } from '@nestjs/config';
import { S3Client } from "@aws-sdk/client-s3"; import { S3Client } from '@aws-sdk/client-s3';
const models = [ const models = [
RegisterTenancyModel(DocumentModel), RegisterTenancyModel(DocumentModel),
@@ -71,15 +71,15 @@ const models = [
key: function (req, file, cb) { key: function (req, file, cb) {
cb(null, Date.now().toString()); cb(null, Date.now().toString());
}, },
acl: function(req, file, cb) { acl: function (req, file, cb) {
// Conditionally set file to public or private based on isPublic flag // Conditionally set file to public or private based on isPublic flag
const aclValue = true ? 'public-read' : 'private'; const aclValue = true ? 'public-read' : 'private';
// Set ACL based on the isPublic flag // Set ACL based on the isPublic flag
cb(null, aclValue); cb(null, aclValue);
} },
}), }),
}) }),
} },
] ],
}) })
export class AttachmentsModule {} export class AttachmentsModule {}
@@ -1,4 +1,4 @@
import { Transformer } from "../Transformer/Transformer"; import { Transformer } from '../Transformer/Transformer';
export class AttachmentTransformer extends Transformer { export class AttachmentTransformer extends Transformer {
/** /**
@@ -1,6 +1,6 @@
import { Transformer } from "../Transformer/Transformer"; import { Transformer } from '../Transformer/Transformer';
export class AttachmentTransformer extends Transformer{ export class AttachmentTransformer extends Transformer {
/** /**
* Exclude attributes. * Exclude attributes.
* @returns {string[]} * @returns {string[]}
@@ -11,7 +11,7 @@ export class GetAttachment {
@Inject(S3_CLIENT) @Inject(S3_CLIENT)
private readonly s3: S3Client, private readonly s3: S3Client,
) {} ) {}
/** /**
* Retrieves data of the given document key. * Retrieves data of the given document key.
* @param {string} filekey * @param {string} filekey
@@ -10,7 +10,7 @@ import { S3_CLIENT } from '../S3/S3.module';
export class GetAttachmentPresignedUrl { export class GetAttachmentPresignedUrl {
constructor( constructor(
private readonly configService: ConfigService, private readonly configService: ConfigService,
@Inject(DocumentModel.name) @Inject(DocumentModel.name)
private readonly documentModel: TenantModelProxy<typeof DocumentModel>, private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
@@ -20,7 +20,7 @@ export class GetAttachmentPresignedUrl {
/** /**
* Retrieves the presigned url of the given attachment key with the original filename. * Retrieves the presigned url of the given attachment key with the original filename.
* @param {string} key - * @param {string} key -
* @returns {string} * @returns {string}
*/ */
async getPresignedUrl(key: string) { async getPresignedUrl(key: string) {
@@ -36,7 +36,9 @@ export class GetAttachmentPresignedUrl {
Key: key, Key: key,
ResponseContentDisposition, ResponseContentDisposition,
}); });
const signedUrl = await getSignedUrl(this.s3Client, command, { expiresIn: 300 }); const signedUrl = await getSignedUrl(this.s3Client, command, {
expiresIn: 300,
});
return signedUrl; return signedUrl;
} }
@@ -4,9 +4,7 @@ import { Injectable } from '@nestjs/common';
@Injectable() @Injectable()
export class AttachmentUploadPipeline { export class AttachmentUploadPipeline {
constructor( constructor(private readonly configService: ConfigService) {}
private readonly configService: ConfigService
) {}
/** /**
* Middleware to ensure that S3 configuration is properly set before proceeding. * Middleware to ensure that S3 configuration is properly set before proceeding.
@@ -18,11 +16,7 @@ export class AttachmentUploadPipeline {
public validateS3Configured(req: Request, res: Response, next: NextFunction) { public validateS3Configured(req: Request, res: Response, next: NextFunction) {
const config = this.configService.get('s3'); const config = this.configService.get('s3');
if ( if (!config.region || !config.accessKeyId || !config.secretAccessKey) {
!config.region ||
!config.accessKeyId ||
!config.secretAccessKey
) {
const missingKeys = []; const missingKeys = [];
if (!config.region) missingKeys.push('region'); if (!config.region) missingKeys.push('region');
if (!config.accessKeyId) missingKeys.push('accessKeyId'); if (!config.accessKeyId) missingKeys.push('accessKeyId');
@@ -47,10 +47,13 @@ export class UnlinkAttachment {
const foundLinkModel = await LinkModel().query(trx).findById(modelId); const foundLinkModel = await LinkModel().query(trx).findById(modelId);
validateLinkModelEntryExists(foundLinkModel); validateLinkModelEntryExists(foundLinkModel);
const document = await this.documentModel().query(trx).findOne('key', filekey); const document = await this.documentModel()
.query(trx)
.findOne('key', filekey);
// Delete the document link. // Delete the document link.
await this.documentLinkModel().query(trx) await this.documentLinkModel()
.query(trx)
.where('modelRef', modelRef) .where('modelRef', modelRef)
.where('modelId', modelId) .where('modelId', modelId)
.where('documentId', document.id) .where('documentId', document.id)
@@ -1,5 +1,5 @@
export enum ERRORS { export enum ERRORS {
DOCUMENT_LINK_REF_INVALID = 'DOCUMENT_LINK_REF_INVALID', DOCUMENT_LINK_REF_INVALID = 'DOCUMENT_LINK_REF_INVALID',
DOCUMENT_LINK_ID_INVALID = 'DOCUMENT_LINK_ID_INVALID', DOCUMENT_LINK_ID_INVALID = 'DOCUMENT_LINK_ID_INVALID',
DOCUMENT_LINK_ALREADY_LINKED = 'DOCUMENT_LINK_ALREADY_LINKED' DOCUMENT_LINK_ALREADY_LINKED = 'DOCUMENT_LINK_ALREADY_LINKED',
} }
@@ -20,4 +20,3 @@ export function InjectAttachable() {
export function getAttachableModelsMap() { export function getAttachableModelsMap() {
return attachableModelsMap; return attachableModelsMap;
} }
@@ -1,6 +1,5 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from "class-validator"; import { IsNotEmpty, IsString } from 'class-validator';
export class AttachmentLinkDto { export class AttachmentLinkDto {
@IsString() @IsString()
@@ -8,12 +7,10 @@ export class AttachmentLinkDto {
key: string; key: string;
} }
export class UnlinkAttachmentDto { export class UnlinkAttachmentDto {
@IsNotEmpty() @IsNotEmpty()
modelRef: string; modelRef: string;
@IsNotEmpty() @IsNotEmpty()
modelId: number; modelId: number;
} }
@@ -22,12 +19,11 @@ export class LinkAttachmentDto {
@IsNotEmpty() @IsNotEmpty()
modelRef: string; modelRef: string;
@IsNotEmpty() @IsNotEmpty()
modelId: number; modelId: number;
} }
export class UploadAttachmentDto { export class UploadAttachmentDto {
@ApiProperty({ type: 'string', format: 'binary' }) @ApiProperty({ type: 'string', format: 'binary' })
file: any; file: any;
} }
@@ -15,9 +15,9 @@ import { events } from '@/common/events/events';
@Injectable() @Injectable()
export class AttachmentsOnBills { export class AttachmentsOnBills {
/** /**
* @param {LinkAttachment} linkAttachmentService * @param {LinkAttachment} linkAttachmentService
* @param {UnlinkAttachment} unlinkAttachmentService * @param {UnlinkAttachment} unlinkAttachmentService
* @param {ValidateAttachments} validateDocuments * @param {ValidateAttachments} validateDocuments
*/ */
constructor( constructor(
private readonly linkAttachmentService: LinkAttachment, private readonly linkAttachmentService: LinkAttachment,
@@ -15,9 +15,9 @@ import { events } from '@/common/events/events';
@Injectable() @Injectable()
export class AttachmentsOnCreditNote { export class AttachmentsOnCreditNote {
/** /**
* @param {LinkAttachment} linkAttachmentService - * @param {LinkAttachment} linkAttachmentService -
* @param {UnlinkAttachment} unlinkAttachmentService - * @param {UnlinkAttachment} unlinkAttachmentService -
* @param {ValidateAttachments} validateDocuments - * @param {ValidateAttachments} validateDocuments -
*/ */
constructor( constructor(
private readonly linkAttachmentService: LinkAttachment, private readonly linkAttachmentService: LinkAttachment,
@@ -52,12 +52,7 @@ export class AttachmentsOnExpenses {
const keys = expenseDTO.attachments?.map((attachment) => attachment.key); const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
await this.linkAttachmentService.bulkLink( await this.linkAttachmentService.bulkLink(keys, 'Expense', expense.id, trx);
keys,
'Expense',
expense.id,
trx,
);
} }
/** /**
@@ -44,7 +44,7 @@ export class AuthController {
@Inject(TenantModel.name) @Inject(TenantModel.name)
private readonly tenantModel: typeof TenantModel, private readonly tenantModel: typeof TenantModel,
) { } ) {}
@Post('/signin') @Post('/signin')
@UseGuards(LocalAuthGuard) @UseGuards(LocalAuthGuard)
@@ -52,7 +52,8 @@ export class AuthController {
@ApiBody({ type: AuthSigninDto }) @ApiBody({ type: AuthSigninDto })
@ApiResponse({ @ApiResponse({
status: 200, status: 200,
description: 'Sign-in successful. Returns access token and tenant/organization IDs.', description:
'Sign-in successful. Returns access token and tenant/organization IDs.',
schema: { $ref: getSchemaPath(AuthSigninResponseDto) }, schema: { $ref: getSchemaPath(AuthSigninResponseDto) },
}) })
async signin( async signin(
@@ -73,7 +74,10 @@ export class AuthController {
@Post('/signup') @Post('/signup')
@ApiOperation({ summary: 'Sign up a new user' }) @ApiOperation({ summary: 'Sign up a new user' })
@ApiBody({ type: AuthSignupDto }) @ApiBody({ type: AuthSignupDto })
@ApiResponse({ status: 201, description: 'Sign-up initiated. Check email for confirmation.' }) @ApiResponse({
status: 201,
description: 'Sign-up initiated. Check email for confirmation.',
})
signup(@Request() req: Request, @Body() signupDto: AuthSignupDto) { signup(@Request() req: Request, @Body() signupDto: AuthSignupDto) {
return this.authApp.signUp(signupDto); return this.authApp.signUp(signupDto);
} }
@@ -89,14 +93,20 @@ export class AuthController {
@Post('/send_reset_password') @Post('/send_reset_password')
@ApiOperation({ summary: 'Send reset password email' }) @ApiOperation({ summary: 'Send reset password email' })
@ApiBody({ type: AuthSendResetPasswordDto }) @ApiBody({ type: AuthSendResetPasswordDto })
@ApiResponse({ status: 200, description: 'Reset password email sent if the account exists.' }) @ApiResponse({
status: 200,
description: 'Reset password email sent if the account exists.',
})
sendResetPassword(@Body() body: AuthSendResetPasswordDto) { sendResetPassword(@Body() body: AuthSendResetPasswordDto) {
return this.authApp.sendResetPassword(body.email); return this.authApp.sendResetPassword(body.email);
} }
@Post('/reset_password/:token') @Post('/reset_password/:token')
@ApiOperation({ summary: 'Reset password using token' }) @ApiOperation({ summary: 'Reset password using token' })
@ApiParam({ name: 'token', description: 'Reset password token from email link' }) @ApiParam({
name: 'token',
description: 'Reset password token from email link',
})
@ApiBody({ type: AuthResetPasswordDto }) @ApiBody({ type: AuthResetPasswordDto })
@ApiResponse({ status: 200, description: 'Password reset successfully.' }) @ApiResponse({ status: 200, description: 'Password reset successfully.' })
resetPassword( resetPassword(
@@ -45,7 +45,7 @@ export class AuthApiKeysController {
constructor( constructor(
private readonly getApiKeysService: GetApiKeysService, private readonly getApiKeysService: GetApiKeysService,
private readonly generateApiKeyService: GenerateApiKey, private readonly generateApiKeyService: GenerateApiKey,
) { } ) {}
@Post('generate') @Post('generate')
@ApiOperation({ summary: 'Generate a new API key' }) @ApiOperation({ summary: 'Generate a new API key' })
@@ -41,9 +41,9 @@ export class AuthenticationMailMesssages {
} }
sendResetPasswordMail(user: ModelObject<SystemUser>, token: string) { sendResetPasswordMail(user: ModelObject<SystemUser>, token: string) {
const mail = this.resetPasswordMessage(user, token); const mail = this.resetPasswordMessage(user, token);
return this.mailTransporter.send(mail); return this.mailTransporter.send(mail);
} }
/** /**
@@ -72,11 +72,7 @@ export class AuthenticationMailMesssages {
} }
sendSignupVerificationMail(email: string, fullName: string, token: string) { sendSignupVerificationMail(email: string, fullName: string, token: string) {
const mail = this.signupVerificationMail( const mail = this.signupVerificationMail(email, fullName, token);
email,
fullName,
token,
);
return this.mailTransporter.send(mail); return this.mailTransporter.send(mail);
} }
} }
@@ -20,7 +20,7 @@ export class AuthedController {
constructor( constructor(
private readonly getAuthedAccountService: GetAuthenticatedAccount, private readonly getAuthedAccountService: GetAuthenticatedAccount,
private readonly authApp: AuthenticationApplication, private readonly authApp: AuthenticationApplication,
) { } ) {}
@Post('/signup/verify/resend') @Post('/signup/verify/resend')
@ApiOperation({ summary: 'Resend the signup confirmation message' }) @ApiOperation({ summary: 'Resend the signup confirmation message' })
@@ -24,16 +24,14 @@ export class AuthSendResetPasswordService {
@Inject(SystemUser.name) @Inject(SystemUser.name)
private readonly systemUserModel: typeof SystemUser, private readonly systemUserModel: typeof SystemUser,
) { } ) {}
/** /**
* Sends the given email reset password email. * Sends the given email reset password email.
* @param {string} email - Email address. * @param {string} email - Email address.
*/ */
async sendResetPassword(email: string): Promise<void> { async sendResetPassword(email: string): Promise<void> {
const user = await this.systemUserModel const user = await this.systemUserModel.query().findOne({ email });
.query()
.findOne({ email });
if (!user) return; if (!user) return;
@@ -14,7 +14,7 @@ export class AuthSigninService {
private readonly systemUserModel: typeof SystemUser, private readonly systemUserModel: typeof SystemUser,
private readonly jwtService: JwtService, private readonly jwtService: JwtService,
private readonly clsService: ClsService, private readonly clsService: ClsService,
) { } ) {}
/** /**
* Validates the given email and password. * Validates the given email and password.
@@ -33,7 +33,7 @@ export class AuthSignupService {
@Inject(SystemUser.name) @Inject(SystemUser.name)
private readonly systemUserModel: typeof SystemUser, private readonly systemUserModel: typeof SystemUser,
) { } ) {}
/** /**
* Registers a new tenant with user from user input. * Registers a new tenant with user from user input.
@@ -53,7 +53,7 @@ export class AuthSignupService {
const verifiedEnabed = signupConfirmation.enabled ?? false; const verifiedEnabed = signupConfirmation.enabled ?? false;
const verifyToken = verifiedEnabed ? verifyTokenCrypto : ''; const verifyToken = verifiedEnabed ? verifyTokenCrypto : '';
const verified = !verifiedEnabed; const verified = !verifiedEnabed;
const inviteAcceptedAt = moment().format('YYYY-MM-DD'); const inviteAcceptedAt = moment().format('YYYY-MM-DD');
// Triggers signin up event. // Triggers signin up event.
@@ -10,7 +10,7 @@ export class GenerateApiKey {
private readonly tenancyContext: TenancyContext, private readonly tenancyContext: TenancyContext,
@Inject(ApiKeyModel.name) @Inject(ApiKeyModel.name)
private readonly apiKeyModel: typeof ApiKeyModel, private readonly apiKeyModel: typeof ApiKeyModel,
) { } ) {}
/** /**
* Generates a new secure API key for the current tenant and system user. * Generates a new secure API key for the current tenant and system user.
@@ -11,5 +11,3 @@ export class InvalidEmailPasswordException extends UnauthorizedException {
}); });
} }
} }
@@ -1,7 +1,6 @@
import { Transformer } from '@/modules/Transformer/Transformer'; import { Transformer } from '@/modules/Transformer/Transformer';
export class GetApiKeysTransformer extends Transformer { export class GetApiKeysTransformer extends Transformer {
public includeAttributes = (): string[] => { public includeAttributes = (): string[] => {
return ['token']; return ['token'];
}; };
@@ -4,12 +4,7 @@ import { IAuthGetMetaPOJO } from '../Auth.interfaces';
@Injectable() @Injectable()
export class GetAuthMetaService { export class GetAuthMetaService {
constructor(private readonly configService: ConfigService) {}
constructor(
private readonly configService: ConfigService,
) {
}
/** /**
* Retrieves the authentication meta for SPA. * Retrieves the authentication meta for SPA.
* @returns {Promise<IAuthGetMetaPOJO>} * @returns {Promise<IAuthGetMetaPOJO>}
@@ -22,9 +22,7 @@ export class BankRulesApplication {
* @param {ICreateBankRuleDTO} createRuleDTO - Bank rule data. * @param {ICreateBankRuleDTO} createRuleDTO - Bank rule data.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public createBankRule( public createBankRule(createRuleDTO: CreateBankRuleDto): Promise<BankRule> {
createRuleDTO: CreateBankRuleDto,
): Promise<BankRule> {
return this.createBankRuleService.createBankRule(createRuleDTO); return this.createBankRuleService.createBankRule(createRuleDTO);
} }
@@ -1,4 +1,4 @@
import { Transformer } from "@/modules/Transformer/Transformer"; import { Transformer } from '@/modules/Transformer/Transformer';
export class GetBankRuleTransformer extends Transformer { export class GetBankRuleTransformer extends Transformer {
/** /**

Some files were not shown because too many files have changed in this diff Show More