chore: format all packages
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { registerAs } from "@nestjs/config";
|
||||
import { registerAs } from '@nestjs/config';
|
||||
|
||||
export default registerAs('inventory', () => ({
|
||||
scheduleComputeItemCost: process.env.INVENTORY_SCHEDULE_COMPUTE_ITEM_COST,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { registerAs } from '@nestjs/config';
|
||||
|
||||
export default registerAs('loops', () => ({
|
||||
|
||||
@@ -7,6 +7,7 @@ export default registerAs('systemDatabase', () => ({
|
||||
user: process.env.SYSTEM_DB_USER || process.env.DB_USER,
|
||||
password: process.env.SYSTEM_DB_PASSWORD || process.env.DB_PASSWORD,
|
||||
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',
|
||||
}));
|
||||
|
||||
@@ -10,5 +10,3 @@ export default registerAs('throttle', () => ({
|
||||
limit: parseInt(process.env.THROTTLE_AUTH_LIMIT ?? '10', 10),
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
|
||||
@@ -20,5 +20,3 @@ export const busboyExceptions = {
|
||||
MULTIPART_UNEXPECTED_END_OF_FORM: 'Unexpected end of form',
|
||||
MULTIPART_UNEXPECTED_END_OF_FILE: 'Unexpected end of file',
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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 { Transform } from 'class-transformer';
|
||||
import { parseBoolean } from '@/utils/parse-boolean';
|
||||
@@ -16,9 +22,12 @@ export class BulkDeleteDto {
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform(({ value, obj }) => parseBoolean(value ?? obj?.skip_undeletable, false))
|
||||
@Transform(({ value, obj }) =>
|
||||
parseBoolean(value ?? obj?.skip_undeletable, false),
|
||||
)
|
||||
@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,
|
||||
default: false,
|
||||
})
|
||||
@@ -52,4 +61,3 @@ export class ValidateBulkDeleteResponseDto {
|
||||
})
|
||||
nonDeletableIds: number[];
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ export class ServiceErrorFilter implements ExceptionFilter {
|
||||
type: exception.errorType,
|
||||
message: exception.message,
|
||||
payload: exception.payload,
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,10 @@ export function FileInterceptor(
|
||||
constructor(
|
||||
@Optional()
|
||||
@Inject(MULTER_MODULE_OPTIONS)
|
||||
options: (() => MulterModuleOptions | MulterModuleOptions) = () => ({}),
|
||||
options: () => MulterModuleOptions | MulterModuleOptions = () => ({}),
|
||||
) {
|
||||
const resolvedOptions = typeof localOptions === 'function'
|
||||
? localOptions(this)
|
||||
: localOptions;
|
||||
const resolvedOptions =
|
||||
typeof localOptions === 'function' ? localOptions(this) : localOptions;
|
||||
|
||||
this.multer = (multer as any)({
|
||||
...(typeof options === 'function' ? options() : options),
|
||||
|
||||
@@ -16,7 +16,11 @@ export class ToJsonInterceptor implements NestInterceptor {
|
||||
return data;
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// import { CachableRepository } from './CachableRepository';
|
||||
import { EntityRepository } from './EntityRepository';
|
||||
|
||||
export class TenantRepository extends EntityRepository {
|
||||
|
||||
}
|
||||
export class TenantRepository extends EntityRepository {}
|
||||
|
||||
+9
-3
@@ -1,10 +1,15 @@
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('accounts', (table) => {
|
||||
return knex.schema
|
||||
.createTable('accounts', (table) => {
|
||||
table.increments('id').comment('Auto-generated id');
|
||||
table.string('name').index();
|
||||
table.string('slug');
|
||||
table.string('account_type').index();
|
||||
table.integer('parent_account_id').unsigned().references('id').inTable('accounts');
|
||||
table
|
||||
.integer('parent_account_id')
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
table.string('code', 10).index();
|
||||
table.text('description');
|
||||
table.boolean('active').defaultTo(true).index();
|
||||
@@ -13,7 +18,8 @@ exports.up = function (knex) {
|
||||
table.decimal('amount', 15, 5);
|
||||
table.string('currency_code', 3).index();
|
||||
table.timestamps();
|
||||
}).raw('ALTER TABLE `ACCOUNTS` AUTO_INCREMENT = 1000');
|
||||
})
|
||||
.raw('ALTER TABLE `ACCOUNTS` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('accounts');
|
||||
|
||||
+15
-4
@@ -1,4 +1,3 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('items_categories', (table) => {
|
||||
table.increments();
|
||||
@@ -7,9 +6,21 @@ exports.up = function (knex) {
|
||||
table.text('description');
|
||||
table.integer('user_id').unsigned().index();
|
||||
|
||||
table.integer('cost_account_id').unsigned().references('id').inTable('accounts');
|
||||
table.integer('sell_account_id').unsigned().references('id').inTable('accounts');
|
||||
table.integer('inventory_account_id').unsigned().references('id').inTable('accounts');
|
||||
table
|
||||
.integer('cost_account_id')
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
table
|
||||
.integer('sell_account_id')
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
table
|
||||
.integer('inventory_account_id')
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
|
||||
table.string('cost_method');
|
||||
table.timestamps();
|
||||
|
||||
+27
-7
@@ -1,6 +1,6 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('items', (table) => {
|
||||
return knex.schema
|
||||
.createTable('items', (table) => {
|
||||
table.increments();
|
||||
table.string('name').index();
|
||||
table.string('type').index();
|
||||
@@ -11,9 +11,23 @@ exports.up = function (knex) {
|
||||
table.decimal('cost_price', 13, 3).unsigned();
|
||||
table.string('currency_code', 3);
|
||||
table.string('picture_uri');
|
||||
table.integer('cost_account_id').nullable().unsigned().references('id').inTable('accounts');
|
||||
table.integer('sell_account_id').nullable().unsigned().references('id').inTable('accounts');
|
||||
table.integer('inventory_account_id').unsigned().references('id').inTable('accounts');
|
||||
table
|
||||
.integer('cost_account_id')
|
||||
.nullable()
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
table
|
||||
.integer('sell_account_id')
|
||||
.nullable()
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
table
|
||||
.integer('inventory_account_id')
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
table.text('sell_description').nullable();
|
||||
table.text('purchase_description').nullable();
|
||||
table.integer('quantity_on_hand');
|
||||
@@ -21,10 +35,16 @@ exports.up = function (knex) {
|
||||
|
||||
table.text('note').nullable();
|
||||
table.boolean('active');
|
||||
table.integer('category_id').unsigned().index().references('id').inTable('items_categories');
|
||||
table
|
||||
.integer('category_id')
|
||||
.unsigned()
|
||||
.index()
|
||||
.references('id')
|
||||
.inTable('items_categories');
|
||||
table.integer('user_id').unsigned().index();
|
||||
table.timestamps();
|
||||
}).raw('ALTER TABLE `ITEMS` AUTO_INCREMENT = 1000');
|
||||
})
|
||||
.raw('ALTER TABLE `ITEMS` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('items');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('views', (table) => {
|
||||
return knex.schema
|
||||
.createTable('views', (table) => {
|
||||
table.increments();
|
||||
table.string('name').index();
|
||||
table.string('slug').index();
|
||||
@@ -9,7 +9,8 @@ exports.up = function (knex) {
|
||||
table.boolean('favourite');
|
||||
table.string('roles_logic_expression');
|
||||
table.timestamps();
|
||||
}).raw('ALTER TABLE `VIEWS` AUTO_INCREMENT = 1000');
|
||||
})
|
||||
.raw('ALTER TABLE `VIEWS` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('views');
|
||||
|
||||
+4
-3
@@ -1,13 +1,14 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('settings', (table) => {
|
||||
return knex.schema
|
||||
.createTable('settings', (table) => {
|
||||
table.increments();
|
||||
table.integer('user_id').unsigned().index();
|
||||
table.string('group').index();
|
||||
table.string('type');
|
||||
table.string('key').index();
|
||||
table.string('value');
|
||||
}).raw('ALTER TABLE `SETTINGS` AUTO_INCREMENT = 2000');
|
||||
})
|
||||
.raw('ALTER TABLE `SETTINGS` AUTO_INCREMENT = 2000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('settings');
|
||||
|
||||
+10
-4
@@ -1,11 +1,17 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('view_has_columns', (table) => {
|
||||
return knex.schema
|
||||
.createTable('view_has_columns', (table) => {
|
||||
table.increments();
|
||||
table.integer('view_id').unsigned().index().references('id').inTable('views');
|
||||
table
|
||||
.integer('view_id')
|
||||
.unsigned()
|
||||
.index()
|
||||
.references('id')
|
||||
.inTable('views');
|
||||
table.string('field_key');
|
||||
table.integer('index').unsigned();
|
||||
}).raw('ALTER TABLE `ITEMS_CATEGORIES` AUTO_INCREMENT = 1000');
|
||||
})
|
||||
.raw('ALTER TABLE `ITEMS_CATEGORIES` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTableIfExists('view_has_columns');
|
||||
|
||||
+1
-3
@@ -1,6 +1,5 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('contacts', table => {
|
||||
return knex.schema.createTable('contacts', (table) => {
|
||||
table.increments();
|
||||
|
||||
table.string('contact_service');
|
||||
@@ -32,7 +31,6 @@ exports.up = function(knex) {
|
||||
table.string('billing_address_postcode').nullable();
|
||||
table.string('billing_address_phone').nullable();
|
||||
table.string('billing_address_state').nullable(),
|
||||
|
||||
table.string('shipping_address_1').nullable();
|
||||
table.string('shipping_address_2').nullable();
|
||||
table.string('shipping_address_city').nullable();
|
||||
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('manual_journals', (table) => {
|
||||
return knex.schema
|
||||
.createTable('manual_journals', (table) => {
|
||||
table.increments();
|
||||
table.string('journal_number').index();
|
||||
table.string('reference').index();
|
||||
@@ -13,7 +13,8 @@ exports.up = function(knex) {
|
||||
table.string('attachment_file');
|
||||
table.integer('user_id').unsigned().index();
|
||||
table.timestamps();
|
||||
}).raw('ALTER TABLE `MANUAL_JOURNALS` AUTO_INCREMENT = 1000');
|
||||
})
|
||||
.raw('ALTER TABLE `MANUAL_JOURNALS` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
|
||||
+16
-5
@@ -1,15 +1,26 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('manual_journals_entries', (table) => {
|
||||
return knex.schema
|
||||
.createTable('manual_journals_entries', (table) => {
|
||||
table.increments();
|
||||
table.decimal('credit', 13, 3);
|
||||
table.decimal('debit', 13, 3);
|
||||
table.integer('index').unsigned();
|
||||
table.integer('account_id').unsigned().index().references('id').inTable('accounts');
|
||||
table
|
||||
.integer('account_id')
|
||||
.unsigned()
|
||||
.index()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
table.integer('contact_id').unsigned().nullable().index();
|
||||
table.string('note');
|
||||
table.integer('manual_journal_id').unsigned().index().references('id').inTable('manual_journals');
|
||||
}).raw('ALTER TABLE `MANUAL_JOURNALS_ENTRIES` AUTO_INCREMENT = 1000');
|
||||
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) {
|
||||
|
||||
+4
-3
@@ -1,12 +1,13 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('currencies', table => {
|
||||
return knex.schema
|
||||
.createTable('currencies', (table) => {
|
||||
table.increments();
|
||||
table.string('currency_name').index();
|
||||
table.string('currency_code', 4).index();
|
||||
table.string('currency_sign').index();
|
||||
table.timestamps();
|
||||
}).raw('ALTER TABLE `CURRENCIES` AUTO_INCREMENT = 1000');
|
||||
})
|
||||
.raw('ALTER TABLE `CURRENCIES` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
|
||||
+4
-3
@@ -1,12 +1,13 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('exchange_rates', table => {
|
||||
return knex.schema
|
||||
.createTable('exchange_rates', (table) => {
|
||||
table.increments();
|
||||
table.string('currency_code', 4).index();
|
||||
table.decimal('exchange_rate');
|
||||
table.date('date').index();
|
||||
table.timestamps();
|
||||
}).raw('ALTER TABLE `EXCHANGE_RATES` AUTO_INCREMENT = 1000');
|
||||
})
|
||||
.raw('ALTER TABLE `EXCHANGE_RATES` AUTO_INCREMENT = 1000');
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('media', (table) => {
|
||||
table.increments();
|
||||
|
||||
+2
-3
@@ -1,11 +1,10 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('media_links', table => {
|
||||
return knex.schema.createTable('media_links', (table) => {
|
||||
table.increments();
|
||||
table.string('model_name').index();
|
||||
table.integer('media_id').unsigned().references('id').inTable('media');
|
||||
table.integer('model_id').unsigned().index();
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
|
||||
+14
-5
@@ -1,11 +1,20 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('sales_receipts', table => {
|
||||
return knex.schema.createTable('sales_receipts', (table) => {
|
||||
table.increments();
|
||||
table.decimal('amount', 13, 3);
|
||||
table.string('currency_code', 3);
|
||||
table.integer('deposit_account_id').unsigned().index().references('id').inTable('accounts');
|
||||
table.integer('customer_id').unsigned().index().references('id').inTable('contacts');
|
||||
table
|
||||
.integer('deposit_account_id')
|
||||
.unsigned()
|
||||
.index()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
table
|
||||
.integer('customer_id')
|
||||
.unsigned()
|
||||
.index()
|
||||
.references('id')
|
||||
.inTable('contacts');
|
||||
table.date('receipt_date').index();
|
||||
table.string('receipt_number').index();
|
||||
table.string('reference_no').index();
|
||||
@@ -14,7 +23,7 @@ exports.up = function(knex) {
|
||||
table.text('statement');
|
||||
table.date('closed_at').index();
|
||||
table.timestamps();
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
|
||||
+13
-7
@@ -1,11 +1,19 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('bills_payments', table => {
|
||||
return knex.schema.createTable('bills_payments', (table) => {
|
||||
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.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.date('payment_date').index();
|
||||
table.string('payment_method');
|
||||
@@ -16,6 +24,4 @@ exports.up = function(knex) {
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function(knex) {
|
||||
|
||||
};
|
||||
exports.down = function (knex) {};
|
||||
|
||||
-1
@@ -8,4 +8,3 @@ exports.up = function (knex) {
|
||||
};
|
||||
|
||||
exports.down = function (knex) {};
|
||||
|
||||
+6
-3
@@ -1,10 +1,13 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('inventory_adjustments', table => {
|
||||
return knex.schema.createTable('inventory_adjustments', (table) => {
|
||||
table.increments();
|
||||
table.date('date').index();
|
||||
table.string('type').index();
|
||||
table.integer('adjustment_account_id').unsigned().references('id').inTable('accounts');
|
||||
table
|
||||
.integer('adjustment_account_id')
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('accounts');
|
||||
table.string('reason');
|
||||
table.string('reference_no').index();
|
||||
table.string('description');
|
||||
|
||||
+4
-4
@@ -1,16 +1,16 @@
|
||||
exports.up = (knex) => {
|
||||
return knex.schema
|
||||
.raw(
|
||||
'ALTER TABLE CONTACTS CHANGE SHIPPING_ADDRESS_1 SHIPPING_ADDRESS1 VARCHAR(255)'
|
||||
'ALTER TABLE CONTACTS CHANGE SHIPPING_ADDRESS_1 SHIPPING_ADDRESS1 VARCHAR(255)',
|
||||
)
|
||||
.raw(
|
||||
'ALTER TABLE CONTACTS CHANGE SHIPPING_ADDRESS_2 SHIPPING_ADDRESS2 VARCHAR(255)'
|
||||
'ALTER TABLE CONTACTS CHANGE SHIPPING_ADDRESS_2 SHIPPING_ADDRESS2 VARCHAR(255)',
|
||||
)
|
||||
.raw(
|
||||
'ALTER TABLE CONTACTS CHANGE BILLING_ADDRESS_1 BILLING_ADDRESS1 VARCHAR(255)'
|
||||
'ALTER TABLE CONTACTS CHANGE BILLING_ADDRESS_1 BILLING_ADDRESS1 VARCHAR(255)',
|
||||
)
|
||||
.raw(
|
||||
'ALTER TABLE CONTACTS CHANGE BILLING_ADDRESS_2 BILLING_ADDRESS2 VARCHAR(255)'
|
||||
'ALTER TABLE CONTACTS CHANGE BILLING_ADDRESS_2 BILLING_ADDRESS2 VARCHAR(255)',
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ exports.up = function (knex) {
|
||||
table.boolean('categorized').defaultTo(false);
|
||||
table.string('plaid_transaction_id');
|
||||
table.timestamps();
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+8
-4
@@ -2,17 +2,21 @@
|
||||
// This migration is necessary to allow tax_amount_withheld filed to store values bigger than 999,999.99.
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.alterTable('bills', function (table) {
|
||||
return knex.schema
|
||||
.alterTable('bills', function (table) {
|
||||
table.decimal('tax_amount_withheld', 13, 2).alter();
|
||||
}).alterTable('sales_invoices', function (table) {
|
||||
})
|
||||
.alterTable('sales_invoices', function (table) {
|
||||
table.decimal('tax_amount_withheld', 13, 2).alter();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
return knex.schema.alterTable('bills', function (table) {
|
||||
return knex.schema
|
||||
.alterTable('bills', function (table) {
|
||||
table.decimal('tax_amount_withheld', 8, 2).alter();
|
||||
}).alterTable('sales_invoices', function (table) {
|
||||
})
|
||||
.alterTable('sales_invoices', function (table) {
|
||||
table.decimal('tax_amount_withheld', 8, 2).alter();
|
||||
});
|
||||
};
|
||||
+2
-3
@@ -1,12 +1,11 @@
|
||||
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.alterTable('contacts', table => {
|
||||
return knex.schema.alterTable('contacts', (table) => {
|
||||
table.string('code').nullable().unique();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
return knex.schema.alterTable('contacts', table => {
|
||||
return knex.schema.alterTable('contacts', (table) => {
|
||||
table.dropColumn('code');
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,12 +5,12 @@ import { PageProperties } from './_types';
|
||||
export class ConverterUtils {
|
||||
public static injectPageProperties(
|
||||
data: FormData,
|
||||
pageProperties: PageProperties
|
||||
pageProperties: PageProperties,
|
||||
): void {
|
||||
if (pageProperties.size) {
|
||||
GotenbergUtils.assert(
|
||||
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);
|
||||
@@ -22,7 +22,7 @@ export class ConverterUtils {
|
||||
pageProperties.margins.bottom >= 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('marginBottom', pageProperties.margins.bottom);
|
||||
@@ -32,7 +32,7 @@ export class ConverterUtils {
|
||||
if (pageProperties.preferCssPageSize) {
|
||||
data.append(
|
||||
'preferCssPageSize',
|
||||
String(pageProperties.preferCssPageSize)
|
||||
String(pageProperties.preferCssPageSize),
|
||||
);
|
||||
}
|
||||
if (pageProperties.printBackground) {
|
||||
@@ -44,7 +44,7 @@ export class ConverterUtils {
|
||||
if (pageProperties.scale) {
|
||||
GotenbergUtils.assert(
|
||||
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);
|
||||
}
|
||||
@@ -55,11 +55,11 @@ export class ConverterUtils {
|
||||
pageProperties.nativePageRanges.to > 0 &&
|
||||
pageProperties.nativePageRanges.to >=
|
||||
pageProperties.nativePageRanges.from,
|
||||
'page ranges syntax error'
|
||||
'page ranges syntax error',
|
||||
);
|
||||
data.append(
|
||||
'nativePageRanges',
|
||||
`${pageProperties.nativePageRanges.from}-${pageProperties.nativePageRanges.to}`
|
||||
`${pageProperties.nativePageRanges.from}-${pageProperties.nativePageRanges.to}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ function createDFS(edges, leavesOnly, result, circular) {
|
||||
|
||||
DepGraph.fromArray = (
|
||||
items,
|
||||
options = { itemId: 'id', parentItemId: 'parent_id' }
|
||||
options = { itemId: 'id', parentItemId: 'parent_id' },
|
||||
) => {
|
||||
const depGraph = new DepGraph();
|
||||
|
||||
@@ -227,7 +227,7 @@ function createDFS(edges, leavesOnly, result, circular) {
|
||||
this.outgoingEdges,
|
||||
leavesOnly,
|
||||
result,
|
||||
this.circular
|
||||
this.circular,
|
||||
);
|
||||
DFS(node);
|
||||
var idx = result.indexOf(node);
|
||||
@@ -253,7 +253,7 @@ function createDFS(edges, leavesOnly, result, circular) {
|
||||
this.incomingEdges,
|
||||
leavesOnly,
|
||||
result,
|
||||
this.circular
|
||||
this.circular,
|
||||
);
|
||||
DFS(node);
|
||||
var idx = result.indexOf(node);
|
||||
@@ -292,7 +292,7 @@ function createDFS(edges, leavesOnly, result, circular) {
|
||||
this.outgoingEdges,
|
||||
leavesOnly,
|
||||
result,
|
||||
this.circular
|
||||
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
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
const OperationType = {
|
||||
LOGIC: 'LOGIC',
|
||||
STRING: 'STRING',
|
||||
@@ -20,7 +19,7 @@ export class Lexer {
|
||||
'?': OperationType.LOGIC,
|
||||
':': OperationType.LOGIC,
|
||||
|
||||
'\'': OperationType.STRING,
|
||||
"'": OperationType.STRING,
|
||||
'"': OperationType.STRING,
|
||||
|
||||
'!': OperationType.COMPARISON,
|
||||
@@ -79,11 +78,13 @@ export class Lexer {
|
||||
// we must move the pos forward
|
||||
// so here we should throw error, for example `1 & 2`
|
||||
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';
|
||||
throw err;
|
||||
}
|
||||
} while (tok !== undefined)
|
||||
} while (tok !== undefined);
|
||||
|
||||
return this.tokenList;
|
||||
}
|
||||
@@ -103,7 +104,9 @@ export class Lexer {
|
||||
* @param index
|
||||
*/
|
||||
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
|
||||
if (tok) {
|
||||
this.tokenList.push(tok);
|
||||
|
||||
@@ -67,7 +67,9 @@ export class Parser {
|
||||
root.right = this.parseStatement();
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
@@ -95,10 +97,12 @@ export class Parser {
|
||||
let pre = root;
|
||||
|
||||
if (this.compare(pre.operation, operation) < 0 && !pre.grouped) {
|
||||
|
||||
while (pre.right !== null &&
|
||||
while (
|
||||
pre.right !== null &&
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -113,7 +117,7 @@ export class Parser {
|
||||
left: pre,
|
||||
right,
|
||||
operation,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,12 +153,20 @@ export class Parser {
|
||||
}
|
||||
|
||||
if (token === '!') {
|
||||
return { left: null, operation: token, right: this.parseStatement() }
|
||||
return { left: null, operation: token, right: this.parseStatement() };
|
||||
}
|
||||
|
||||
// 3 > -12 or -12 + 10
|
||||
if (token === '-' && (OPERATION[this.prevToken()] > 0 || this.prevToken() === undefined)) {
|
||||
return { left: '0', operation: token, right: this.parseStatement(), grouped: true };
|
||||
if (
|
||||
token === '-' &&
|
||||
(OPERATION[this.prevToken()] > 0 || this.prevToken() === undefined)
|
||||
) {
|
||||
return {
|
||||
left: '0',
|
||||
operation: token,
|
||||
right: this.parseStatement(),
|
||||
grouped: true,
|
||||
};
|
||||
}
|
||||
|
||||
return token;
|
||||
|
||||
@@ -22,7 +22,9 @@ export class QueryParser {
|
||||
parseNode(node) {
|
||||
if (typeof node === 'string') {
|
||||
const nodeQuery = this.getQuery(node);
|
||||
return (query) => { nodeQuery(query); };
|
||||
return (query) => {
|
||||
nodeQuery(query);
|
||||
};
|
||||
}
|
||||
if (OPERATION[node.operation] === undefined) {
|
||||
throw new Error(`unknow expression ${node.operation}`);
|
||||
@@ -34,15 +36,25 @@ export class QueryParser {
|
||||
case '&&':
|
||||
case 'AND':
|
||||
default:
|
||||
return (nodeQuery) => nodeQuery.where((query) => {
|
||||
query.where((q) => { leftQuery(q); });
|
||||
query.andWhere((q) => { rightQuery(q); });
|
||||
return (nodeQuery) =>
|
||||
nodeQuery.where((query) => {
|
||||
query.where((q) => {
|
||||
leftQuery(q);
|
||||
});
|
||||
query.andWhere((q) => {
|
||||
rightQuery(q);
|
||||
});
|
||||
});
|
||||
case '||':
|
||||
case 'OR':
|
||||
return (nodeQuery) => nodeQuery.where((query) => {
|
||||
query.where((q) => { leftQuery(q); });
|
||||
query.orWhere((q) => { rightQuery(q); });
|
||||
return (nodeQuery) =>
|
||||
nodeQuery.where((query) => {
|
||||
query.where((q) => {
|
||||
leftQuery(q);
|
||||
});
|
||||
query.orWhere((q) => {
|
||||
rightQuery(q);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
export class Seeder {
|
||||
knex: any;
|
||||
|
||||
@@ -8,4 +7,3 @@ export class Seeder {
|
||||
up(knex) {}
|
||||
down(knex) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function getMergedConfig(config, currentConfig) {
|
||||
mergedConfig.migrationSource = new FsMigrations(
|
||||
mergedConfig.directory,
|
||||
mergedConfig.sortDirsSeparately,
|
||||
mergedConfig.loadExtensions
|
||||
mergedConfig.loadExtensions,
|
||||
);
|
||||
}
|
||||
return mergedConfig;
|
||||
|
||||
@@ -35,7 +35,7 @@ export function getLockTableName(tableName: string): string {
|
||||
*/
|
||||
export function getLockTableNameWithSchema(
|
||||
tableName: string,
|
||||
schemaName = null
|
||||
schemaName = null,
|
||||
): string {
|
||||
return schemaName
|
||||
? `${schemaName} + ${getLockTableName(tableName)}`
|
||||
|
||||
@@ -54,9 +54,10 @@ export class PaginationQueryBuilder<
|
||||
// foreign key references and shouldn't prevent deletion. Only dependent records should block deletion.
|
||||
const dependentRelationNames = relationNames.filter((name) => {
|
||||
const relation = relationMappings[name];
|
||||
return relation && (
|
||||
relation.relation === Model.HasManyRelation ||
|
||||
relation.relation === Model.ManyToManyRelation
|
||||
return (
|
||||
relation &&
|
||||
(relation.relation === Model.HasManyRelation ||
|
||||
relation.relation === Model.ManyToManyRelation)
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@ import { Model } from 'objection';
|
||||
|
||||
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 {
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
@@ -36,5 +38,5 @@ export const withDateSessionMixin = <T extends Constructor<Model>>(BaseModel: T)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -23,10 +23,7 @@ export class AccountsImportable extends Importable {
|
||||
createAccountDTO: CreateAccountDTO,
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
return this.createAccountService.createAccount(
|
||||
createAccountDTO,
|
||||
trx,
|
||||
);
|
||||
return this.createAccountService.createAccount(createAccountDTO, trx);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,4 +38,3 @@ export class BulkDeleteAccountsService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 { Transform } from 'class-transformer';
|
||||
import { parseBoolean } from '@/utils/parse-boolean';
|
||||
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';
|
||||
|
||||
export class GetAccountsQueryDto {
|
||||
@@ -114,4 +125,3 @@ export class GetAccountsQueryDto {
|
||||
@Min(1)
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ACCOUNT_TYPES } from "../Accounts.constants";
|
||||
import { ACCOUNT_TYPES } from '../Accounts.constants';
|
||||
|
||||
export const AccountMeta = {
|
||||
defaultFilterField: 'name',
|
||||
|
||||
+3
-5
@@ -14,11 +14,9 @@ export class MutateBaseCurrencyAccountsSubscriber {
|
||||
* of the organization is mutated.
|
||||
*/
|
||||
@OnEvent(events.organization.baseCurrencyUpdated)
|
||||
async updateAccountsCurrencyOnBaseCurrencyMutated({
|
||||
organizationDTO,
|
||||
}) {
|
||||
async updateAccountsCurrencyOnBaseCurrencyMutated({ organizationDTO }) {
|
||||
await this.mutateBaseCurrencyAccounts.mutateAllAccountsCurrency(
|
||||
organizationDTO.baseCurrency
|
||||
organizationDTO.baseCurrency,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ describe('AppController', () => {
|
||||
});
|
||||
|
||||
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],
|
||||
useFactory: (configService: ConfigService) => {
|
||||
// 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) {
|
||||
return {
|
||||
@@ -65,5 +67,3 @@ import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis'
|
||||
],
|
||||
})
|
||||
export class AppThrottleModule {}
|
||||
|
||||
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { Module } from '@nestjs/common';
|
||||
import * as multerS3 from 'multer-s3';
|
||||
import { S3_CLIENT, S3Module } from "../S3/S3.module";
|
||||
import { DeleteAttachment } from "./DeleteAttachment";
|
||||
import { GetAttachment } from "./GetAttachment";
|
||||
import { GetAttachmentPresignedUrl } from "./GetAttachmentPresignedUrl";
|
||||
import { LinkAttachment } from "./LinkAttachment";
|
||||
import { UnlinkAttachment } from "./UnlinkAttachment";
|
||||
import { ValidateAttachments } from "./ValidateAttachments";
|
||||
import { AttachmentsOnBillPayments } from "./events/AttachmentsOnPaymentsMade";
|
||||
import { AttachmentsOnBills } from "./events/AttachmentsOnBills";
|
||||
import { AttachmentsOnCreditNote } from "./events/AttachmentsOnCreditNote";
|
||||
import { AttachmentsOnExpenses } from "./events/AttachmentsOnExpenses";
|
||||
import { AttachmentsOnPaymentsReceived } from "./events/AttachmentsOnPaymentsReceived";
|
||||
import { AttachmentsOnManualJournals } from "./events/AttachmentsOnManualJournals";
|
||||
import { AttachmentsOnVendorCredits } from "./events/AttachmentsOnVendorCredits";
|
||||
import { AttachmentsOnSaleInvoiceCreated } from "./events/AttachmentsOnSaleInvoice";
|
||||
import { AttachmentsOnSaleReceipt } from "./events/AttachmentsOnSaleReceipts";
|
||||
import { AttachmentsOnSaleEstimates } from "./events/AttachmentsOnSaleEstimates";
|
||||
import { AttachmentsController } from "./Attachments.controller";
|
||||
import { RegisterTenancyModel } from "../Tenancy/TenancyModels/Tenancy.module";
|
||||
import { DocumentModel } from "./models/Document.model";
|
||||
import { DocumentLinkModel } from "./models/DocumentLink.model";
|
||||
import { AttachmentsApplication } from "./AttachmentsApplication";
|
||||
import { UploadDocument } from "./UploadDocument";
|
||||
import { AttachmentUploadPipeline } from "./S3UploadPipeline";
|
||||
import { MULTER_MODULE_OPTIONS } from "@/common/constants/files.constants";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { S3Client } from "@aws-sdk/client-s3";
|
||||
import { S3_CLIENT, S3Module } from '../S3/S3.module';
|
||||
import { DeleteAttachment } from './DeleteAttachment';
|
||||
import { GetAttachment } from './GetAttachment';
|
||||
import { GetAttachmentPresignedUrl } from './GetAttachmentPresignedUrl';
|
||||
import { LinkAttachment } from './LinkAttachment';
|
||||
import { UnlinkAttachment } from './UnlinkAttachment';
|
||||
import { ValidateAttachments } from './ValidateAttachments';
|
||||
import { AttachmentsOnBillPayments } from './events/AttachmentsOnPaymentsMade';
|
||||
import { AttachmentsOnBills } from './events/AttachmentsOnBills';
|
||||
import { AttachmentsOnCreditNote } from './events/AttachmentsOnCreditNote';
|
||||
import { AttachmentsOnExpenses } from './events/AttachmentsOnExpenses';
|
||||
import { AttachmentsOnPaymentsReceived } from './events/AttachmentsOnPaymentsReceived';
|
||||
import { AttachmentsOnManualJournals } from './events/AttachmentsOnManualJournals';
|
||||
import { AttachmentsOnVendorCredits } from './events/AttachmentsOnVendorCredits';
|
||||
import { AttachmentsOnSaleInvoiceCreated } from './events/AttachmentsOnSaleInvoice';
|
||||
import { AttachmentsOnSaleReceipt } from './events/AttachmentsOnSaleReceipts';
|
||||
import { AttachmentsOnSaleEstimates } from './events/AttachmentsOnSaleEstimates';
|
||||
import { AttachmentsController } from './Attachments.controller';
|
||||
import { RegisterTenancyModel } from '../Tenancy/TenancyModels/Tenancy.module';
|
||||
import { DocumentModel } from './models/Document.model';
|
||||
import { DocumentLinkModel } from './models/DocumentLink.model';
|
||||
import { AttachmentsApplication } from './AttachmentsApplication';
|
||||
import { UploadDocument } from './UploadDocument';
|
||||
import { AttachmentUploadPipeline } from './S3UploadPipeline';
|
||||
import { MULTER_MODULE_OPTIONS } from '@/common/constants/files.constants';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { S3Client } from '@aws-sdk/client-s3';
|
||||
|
||||
const models = [
|
||||
RegisterTenancyModel(DocumentModel),
|
||||
@@ -76,10 +76,10 @@ const models = [
|
||||
const aclValue = true ? 'public-read' : 'private';
|
||||
// Set ACL based on the isPublic flag
|
||||
cb(null, aclValue);
|
||||
}
|
||||
},
|
||||
}),
|
||||
})
|
||||
}
|
||||
]
|
||||
}),
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AttachmentsModule {}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Transformer } from "../Transformer/Transformer";
|
||||
import { Transformer } from '../Transformer/Transformer';
|
||||
|
||||
export class AttachmentTransformer extends Transformer {
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Transformer } from "../Transformer/Transformer";
|
||||
import { Transformer } from '../Transformer/Transformer';
|
||||
|
||||
export class AttachmentTransformer extends Transformer {
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,9 @@ export class GetAttachmentPresignedUrl {
|
||||
Key: key,
|
||||
ResponseContentDisposition,
|
||||
});
|
||||
const signedUrl = await getSignedUrl(this.s3Client, command, { expiresIn: 300 });
|
||||
const signedUrl = await getSignedUrl(this.s3Client, command, {
|
||||
expiresIn: 300,
|
||||
});
|
||||
|
||||
return signedUrl;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AttachmentUploadPipeline {
|
||||
constructor(
|
||||
private readonly configService: ConfigService
|
||||
) {}
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
const config = this.configService.get('s3');
|
||||
|
||||
if (
|
||||
!config.region ||
|
||||
!config.accessKeyId ||
|
||||
!config.secretAccessKey
|
||||
) {
|
||||
if (!config.region || !config.accessKeyId || !config.secretAccessKey) {
|
||||
const missingKeys = [];
|
||||
if (!config.region) missingKeys.push('region');
|
||||
if (!config.accessKeyId) missingKeys.push('accessKeyId');
|
||||
|
||||
@@ -47,10 +47,13 @@ export class UnlinkAttachment {
|
||||
const foundLinkModel = await LinkModel().query(trx).findById(modelId);
|
||||
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.
|
||||
await this.documentLinkModel().query(trx)
|
||||
await this.documentLinkModel()
|
||||
.query(trx)
|
||||
.where('modelRef', modelRef)
|
||||
.where('modelId', modelId)
|
||||
.where('documentId', document.id)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export enum ERRORS {
|
||||
DOCUMENT_LINK_REF_INVALID = 'DOCUMENT_LINK_REF_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() {
|
||||
return attachableModelsMap;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class AttachmentLinkDto {
|
||||
@IsString()
|
||||
@@ -8,12 +7,10 @@ export class AttachmentLinkDto {
|
||||
key: string;
|
||||
}
|
||||
|
||||
|
||||
export class UnlinkAttachmentDto {
|
||||
@IsNotEmpty()
|
||||
modelRef: string;
|
||||
|
||||
|
||||
@IsNotEmpty()
|
||||
modelId: number;
|
||||
}
|
||||
@@ -22,7 +19,6 @@ export class LinkAttachmentDto {
|
||||
@IsNotEmpty()
|
||||
modelRef: string;
|
||||
|
||||
|
||||
@IsNotEmpty()
|
||||
modelId: number;
|
||||
}
|
||||
|
||||
@@ -52,12 +52,7 @@ export class AttachmentsOnExpenses {
|
||||
|
||||
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
||||
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
keys,
|
||||
'Expense',
|
||||
expense.id,
|
||||
trx,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(keys, 'Expense', expense.id, trx);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,7 +52,8 @@ export class AuthController {
|
||||
@ApiBody({ type: AuthSigninDto })
|
||||
@ApiResponse({
|
||||
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) },
|
||||
})
|
||||
async signin(
|
||||
@@ -73,7 +74,10 @@ export class AuthController {
|
||||
@Post('/signup')
|
||||
@ApiOperation({ summary: 'Sign up a new user' })
|
||||
@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) {
|
||||
return this.authApp.signUp(signupDto);
|
||||
}
|
||||
@@ -89,14 +93,20 @@ export class AuthController {
|
||||
@Post('/send_reset_password')
|
||||
@ApiOperation({ summary: 'Send reset password email' })
|
||||
@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) {
|
||||
return this.authApp.sendResetPassword(body.email);
|
||||
}
|
||||
|
||||
@Post('/reset_password/: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 })
|
||||
@ApiResponse({ status: 200, description: 'Password reset successfully.' })
|
||||
resetPassword(
|
||||
|
||||
@@ -72,11 +72,7 @@ export class AuthenticationMailMesssages {
|
||||
}
|
||||
|
||||
sendSignupVerificationMail(email: string, fullName: string, token: string) {
|
||||
const mail = this.signupVerificationMail(
|
||||
email,
|
||||
fullName,
|
||||
token,
|
||||
);
|
||||
const mail = this.signupVerificationMail(email, fullName, token);
|
||||
return this.mailTransporter.send(mail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,7 @@ export class AuthSendResetPasswordService {
|
||||
* @param {string} email - Email address.
|
||||
*/
|
||||
async sendResetPassword(email: string): Promise<void> {
|
||||
const user = await this.systemUserModel
|
||||
.query()
|
||||
.findOne({ email });
|
||||
const user = await this.systemUserModel.query().findOne({ email });
|
||||
|
||||
if (!user) return;
|
||||
|
||||
|
||||
@@ -11,5 +11,3 @@ export class InvalidEmailPasswordException extends UnauthorizedException {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
|
||||
export class GetApiKeysTransformer extends Transformer {
|
||||
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['token'];
|
||||
};
|
||||
|
||||
@@ -4,12 +4,7 @@ import { IAuthGetMetaPOJO } from '../Auth.interfaces';
|
||||
|
||||
@Injectable()
|
||||
export class GetAuthMetaService {
|
||||
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
|
||||
}
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
/**
|
||||
* Retrieves the authentication meta for SPA.
|
||||
* @returns {Promise<IAuthGetMetaPOJO>}
|
||||
|
||||
@@ -22,9 +22,7 @@ export class BankRulesApplication {
|
||||
* @param {ICreateBankRuleDTO} createRuleDTO - Bank rule data.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public createBankRule(
|
||||
createRuleDTO: CreateBankRuleDto,
|
||||
): Promise<BankRule> {
|
||||
public createBankRule(createRuleDTO: CreateBankRuleDto): Promise<BankRule> {
|
||||
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 {
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Transform } from 'class-transformer';
|
||||
import { IsArray, IsBoolean, IsEnum, IsInt, IsOptional, IsString, Min } from 'class-validator';
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsInt,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Min,
|
||||
} from 'class-validator';
|
||||
import { ToNumber } from '@/common/decorators/Validators';
|
||||
import { IFilterRole, ISortOrder } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
|
||||
import {
|
||||
IFilterRole,
|
||||
ISortOrder,
|
||||
} from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
|
||||
import { parseBoolean } from '@/utils/parse-boolean';
|
||||
|
||||
export class BankAccountsQueryDto {
|
||||
|
||||
@@ -32,7 +32,8 @@ export class GetBankAccountsService {
|
||||
...filterDTO,
|
||||
};
|
||||
// Parsees accounts list filter DTO.
|
||||
const filter = this.dynamicListService.parseStringifiedFilter<BankAccountsQueryDto>(
|
||||
const filter =
|
||||
this.dynamicListService.parseStringifiedFilter<BankAccountsQueryDto>(
|
||||
_filterDto,
|
||||
);
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ export const ERRORS = {
|
||||
BANK_ACCOUNT_FEEDS_ALREADY_RESUMED: 'BANK_ACCOUNT_FEEDS_ALREADY_RESUMED',
|
||||
};
|
||||
|
||||
|
||||
export interface ICashflowAccountsFilter extends IDynamicListFilter {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
|
||||
@@ -53,12 +53,8 @@ export class BankingCategorizeController {
|
||||
@Query('uncategorizedTransactionIds')
|
||||
uncategorizedTransactionIds: number[] | number,
|
||||
) {
|
||||
const ids = castArray(uncategorizedTransactionIds).map((id) =>
|
||||
Number(id),
|
||||
);
|
||||
return this.bankingCategorizeApplication.uncategorizeTransactionsBulk(
|
||||
ids,
|
||||
);
|
||||
const ids = castArray(uncategorizedTransactionIds).map((id) => Number(id));
|
||||
return this.bankingCategorizeApplication.uncategorizeTransactionsBulk(ids);
|
||||
}
|
||||
|
||||
@Delete('/:id')
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ import { UncategorizeBankTransactionService } from './UncategorizeBankTransactio
|
||||
@Injectable()
|
||||
export class UncategorizeBankTransactionsBulk {
|
||||
constructor(
|
||||
private readonly uncategorizeTransactionService: UncategorizeBankTransactionService
|
||||
private readonly uncategorizeTransactionService: UncategorizeBankTransactionService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@ export class UncategorizeBankTransactionsBulk {
|
||||
* @param {number | Array<number>} uncategorizedTransactionId
|
||||
*/
|
||||
public async uncategorizeBulk(
|
||||
uncategorizedTransactionId: number | Array<number>
|
||||
uncategorizedTransactionId: number | Array<number>,
|
||||
) {
|
||||
const uncategorizedTransactionIds = castArray(uncategorizedTransactionId);
|
||||
|
||||
@@ -22,7 +22,7 @@ export class UncategorizeBankTransactionsBulk {
|
||||
.for(uncategorizedTransactionIds)
|
||||
.process(async (_uncategorizedTransactionId: number, index, pool) => {
|
||||
await this.uncategorizeTransactionService.uncategorize(
|
||||
_uncategorizedTransactionId
|
||||
_uncategorizedTransactionId,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Transformer } from "../../Transformer/Transformer";
|
||||
import { Transformer } from '../../Transformer/Transformer';
|
||||
|
||||
export class UncategorizedTransactionTransformer extends Transformer {
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BankTransaction } from "@/modules/BankingTransactions/models/BankTransaction";
|
||||
import { UncategorizedBankTransaction } from "@/modules/BankingTransactions/models/UncategorizedBankTransaction";
|
||||
import { Knex } from "knex";
|
||||
import { BankTransaction } from '@/modules/BankingTransactions/models/BankTransaction';
|
||||
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export interface ICashflowTransactionCategorizedPayload {
|
||||
uncategorizedTransactions: Array<UncategorizedBankTransaction>;
|
||||
@@ -36,7 +36,6 @@ export interface ICategorizeCashflowTransactioDTO {
|
||||
branchId: number;
|
||||
}
|
||||
|
||||
|
||||
export interface IUncategorizedTransactionCreatingEventPayload {
|
||||
tenantId: number;
|
||||
createUncategorizedTransactionDTO: CreateUncategorizedTransactionDTO;
|
||||
|
||||
@@ -6,7 +6,15 @@ import {
|
||||
ApiTags,
|
||||
getSchemaPath,
|
||||
} from '@nestjs/swagger';
|
||||
import { Body, Controller, Get, Param, Patch, Post, Query } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { BankingMatchingApplication } from './BankingMatchingApplication';
|
||||
import { MatchBankTransactionDto } from './dtos/MatchBankTransaction.dto';
|
||||
import { GetMatchedTransactionsQueryDto } from './dtos/GetMatchedTransactionsQuery.dto';
|
||||
|
||||
@@ -7,35 +7,32 @@ import { ServiceError } from '../Items/ServiceError';
|
||||
export const sortClosestMatchTransactions = (
|
||||
amount: number,
|
||||
date: Date,
|
||||
matches: MatchedTransactionPOJO[]
|
||||
matches: MatchedTransactionPOJO[],
|
||||
) => {
|
||||
return R.sortWith([
|
||||
// Sort by amount difference (closest to uncategorized transaction amount first)
|
||||
R.ascend((match: MatchedTransactionPOJO) =>
|
||||
Math.abs(match.amount - amount)
|
||||
Math.abs(match.amount - amount),
|
||||
),
|
||||
// Sort by date difference (closest to uncategorized transaction date first)
|
||||
R.ascend((match: MatchedTransactionPOJO) =>
|
||||
Math.abs(moment(match.date).diff(moment(date), 'days'))
|
||||
Math.abs(moment(match.date).diff(moment(date), 'days')),
|
||||
),
|
||||
])(matches);
|
||||
};
|
||||
|
||||
export const sumMatchTranasctions = (transactions: Array<any>) => {
|
||||
const total = transactions.reduce(
|
||||
(sum, item) => {
|
||||
const total = transactions.reduce((sum, item) => {
|
||||
const amount = parseFloat(item.amount) || 0;
|
||||
const multiplier = item.transactionNormal === 'debit' ? 1 : -1;
|
||||
return sum + multiplier * amount;
|
||||
},
|
||||
0
|
||||
);
|
||||
}, 0);
|
||||
// Round to 2 decimal places to avoid floating-point precision issues
|
||||
return round(total, 2);
|
||||
};
|
||||
|
||||
export const sumUncategorizedTransactions = (
|
||||
uncategorizedTransactions: Array<any>
|
||||
uncategorizedTransactions: Array<any>,
|
||||
) => {
|
||||
const total = sumBy(uncategorizedTransactions, 'amount');
|
||||
// Round to 2 decimal places to avoid floating-point precision issues
|
||||
@@ -43,10 +40,10 @@ export const sumUncategorizedTransactions = (
|
||||
};
|
||||
|
||||
export const validateUncategorizedTransactionsNotMatched = (
|
||||
uncategorizedTransactions: any
|
||||
uncategorizedTransactions: any,
|
||||
) => {
|
||||
const matchedTransactions = uncategorizedTransactions.filter(
|
||||
(trans) => !isEmpty(trans.matchedBankTransactions)
|
||||
(trans) => !isEmpty(trans.matchedBankTransactions),
|
||||
);
|
||||
//
|
||||
if (matchedTransactions.length > 0) {
|
||||
@@ -57,10 +54,10 @@ export const validateUncategorizedTransactionsNotMatched = (
|
||||
};
|
||||
|
||||
export const validateUncategorizedTransactionsExcluded = (
|
||||
uncategorizedTransactions: any
|
||||
uncategorizedTransactions: any,
|
||||
) => {
|
||||
const excludedTransactions = uncategorizedTransactions.filter(
|
||||
(trans) => trans.excluded
|
||||
(trans) => trans.excluded,
|
||||
);
|
||||
if (excludedTransactions.length > 0) {
|
||||
throw new ServiceError(ERRORS.CANNOT_MATCH_EXCLUDED_TRANSACTION, '', {
|
||||
|
||||
@@ -102,7 +102,9 @@ export class MatchBankTransactions {
|
||||
// uncategorized transaction amount.
|
||||
// Use tolerance-based comparison to handle floating-point precision issues
|
||||
const tolerance = 0.01; // Allow 0.01 difference for floating-point precision
|
||||
const difference = Math.abs(totalUncategorizedTransactions - totalMatchedTranasctions);
|
||||
const difference = Math.abs(
|
||||
totalUncategorizedTransactions - totalMatchedTranasctions,
|
||||
);
|
||||
if (difference > tolerance) {
|
||||
throw new ServiceError(ERRORS.TOTAL_MATCHING_TRANSACTIONS_INVALID);
|
||||
}
|
||||
@@ -115,7 +117,9 @@ export class MatchBankTransactions {
|
||||
*/
|
||||
public async matchTransaction(
|
||||
uncategorizedTransactionId: number | Array<number>,
|
||||
matchedTransactionsDto: MatchTransactionEntryDto | Array<MatchTransactionEntryDto>,
|
||||
matchedTransactionsDto:
|
||||
| MatchTransactionEntryDto
|
||||
| Array<MatchTransactionEntryDto>,
|
||||
): Promise<void> {
|
||||
const uncategorizedTransactionIds = castArray(uncategorizedTransactionId);
|
||||
const matchedTransactions = castArray(matchedTransactionsDto);
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ export class MatchTransactionsTypesRegistry {
|
||||
*/
|
||||
public register(
|
||||
resource: string,
|
||||
importable: GetMatchedTransactionsByType
|
||||
importable: GetMatchedTransactionsByType,
|
||||
): void {
|
||||
const _resource = this.sanitizeResourceName(resource);
|
||||
this.importables[_resource] = importable;
|
||||
|
||||
@@ -3,7 +3,10 @@ import { IsOptional, IsString, IsNumber, Min, Max } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class GetMatchedTransactionsQueryDto {
|
||||
@ApiPropertyOptional({ description: 'Filter from date', example: '2024-01-01' })
|
||||
@ApiPropertyOptional({
|
||||
description: 'Filter from date',
|
||||
example: '2024-01-01',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
fromDate?: string;
|
||||
|
||||
@@ -5,7 +5,6 @@ export class MatchedBankTransaction extends BaseModel {
|
||||
public referenceType!: string;
|
||||
public uncategorizedTransactionId!: number;
|
||||
|
||||
|
||||
/**
|
||||
* Table name.
|
||||
*/
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
|
||||
export class GetMatchedTransactionBillsTransformer extends Transformer {
|
||||
/**
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
|
||||
export class GetMatchedTransactionCashflowTransformer extends Transformer {
|
||||
/**
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
|
||||
export class GetMatchedTransactionExpensesTransformer extends Transformer {
|
||||
/**
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
|
||||
export class GetMatchedTransactionInvoicesTransformer extends Transformer {
|
||||
/**
|
||||
@@ -19,7 +19,7 @@ export class GetMatchedTransactionInvoicesTransformer extends Transformer {
|
||||
'transsactionTypeFormatted',
|
||||
'transactionNormal',
|
||||
'referenceType',
|
||||
'referenceId'
|
||||
'referenceId',
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
+5
-2
@@ -1,5 +1,9 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { GetMatchedTransactionsFilter, MatchedTransactionPOJO, MatchedTransactionsPOJO } from '../types';
|
||||
import {
|
||||
GetMatchedTransactionsFilter,
|
||||
MatchedTransactionPOJO,
|
||||
MatchedTransactionsPOJO,
|
||||
} from '../types';
|
||||
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
||||
import { GetMatchedTransactionExpensesTransformer } from './GetMatchedTransactionExpensesTransformer';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
@@ -22,7 +26,6 @@ export class GetMatchedTransactionsByExpenses extends GetMatchedTransactionsByTy
|
||||
|
||||
@Inject('TENANT_MODELS_INIT')
|
||||
private readonly tenantModelsInit: () => Promise<boolean>,
|
||||
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -12,9 +12,7 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
export abstract class GetMatchedTransactionsByType {
|
||||
@Inject(MatchedBankTransaction.name)
|
||||
matchedBankTransactionModel: TenantModelProxy<
|
||||
typeof MatchedBankTransaction
|
||||
>;
|
||||
matchedBankTransactionModel: TenantModelProxy<typeof MatchedBankTransaction>;
|
||||
|
||||
/**
|
||||
* Retrieves the matched transactions.
|
||||
|
||||
@@ -17,15 +17,8 @@ export class BankingPlaidWebhooksController {
|
||||
@Post('webhooks')
|
||||
@ApiOperation({ summary: 'Listen to Plaid webhooks' })
|
||||
webhooks(@Body() { itemId, webhookType, webhookCode }: PlaidWebhookDto) {
|
||||
return this.setupPlaidItemTenantService.setupPlaidTenant(
|
||||
itemId,
|
||||
() => {
|
||||
return this.plaidApplication.webhooks(
|
||||
itemId,
|
||||
webhookType,
|
||||
webhookCode,
|
||||
);
|
||||
},
|
||||
);
|
||||
return this.setupPlaidItemTenantService.setupPlaidTenant(itemId, () => {
|
||||
return this.plaidApplication.webhooks(itemId, webhookType, webhookCode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -21,10 +21,9 @@ export class RecognizeSyncedBankTranasctionsSubscriber {
|
||||
trx,
|
||||
}: IPlaidTransactionsSyncedEventPayload) {
|
||||
runAfterTransaction(trx, async () => {
|
||||
await this.recognizeTranasctionsService.recognizeTransactions(
|
||||
null,
|
||||
{ batch }
|
||||
);
|
||||
await this.recognizeTranasctionsService.recognizeTransactions(null, {
|
||||
batch,
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ export const UpdateBankingPlaidTransitionsJob =
|
||||
export const UpdateBankingPlaidTransitionsQueueJob =
|
||||
'update-banking-plaid-transitions-query';
|
||||
|
||||
|
||||
export interface PlaidFetchTransitonsEventPayload {
|
||||
plaidItemId: string;
|
||||
}
|
||||
+3
-1
@@ -65,7 +65,9 @@ export class BankingRecognizedTransactionsController {
|
||||
properties: {
|
||||
data: {
|
||||
type: 'array',
|
||||
items: { $ref: getSchemaPath(GetRecognizedTransactionResponseDto) },
|
||||
items: {
|
||||
$ref: getSchemaPath(GetRecognizedTransactionResponseDto),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+5
-2
@@ -11,7 +11,9 @@ export class GetRecognizedTransactionsService {
|
||||
private readonly transformer: TransformerInjectable,
|
||||
|
||||
@Inject(UncategorizedBankTransaction.name)
|
||||
private readonly uncategorizedBankTransactionModel: TenantModelProxy<typeof UncategorizedBankTransaction>,
|
||||
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
|
||||
typeof UncategorizedBankTransaction
|
||||
>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -26,7 +28,8 @@ export class GetRecognizedTransactionsService {
|
||||
...filter,
|
||||
};
|
||||
const { results, pagination } =
|
||||
await this.uncategorizedBankTransactionModel().query()
|
||||
await this.uncategorizedBankTransactionModel()
|
||||
.query()
|
||||
.onBuild((q) => {
|
||||
q.withGraphFetched('recognizedTransaction.assignAccount');
|
||||
q.withGraphFetched('recognizedTransaction.bankRule');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TenantJobPayload } from "@/interfaces/Tenant";
|
||||
import { TenantJobPayload } from '@/interfaces/Tenant';
|
||||
|
||||
export interface RevertRecognizedTransactionsCriteria {
|
||||
batch?: string;
|
||||
@@ -15,8 +15,9 @@ export const RecognizeUncategorizedTransactionsJob =
|
||||
export const RecognizeUncategorizedTransactionsQueue =
|
||||
'recognize-uncategorized-transactions-queue';
|
||||
|
||||
export interface RecognizeUncategorizedTransactionsJobPayload extends TenantJobPayload {
|
||||
ruleId: number,
|
||||
export interface RecognizeUncategorizedTransactionsJobPayload
|
||||
extends TenantJobPayload {
|
||||
ruleId: number;
|
||||
transactionsCriteria?: RecognizeTransactionsCriteria;
|
||||
/**
|
||||
* When true, first reverts recognized transactions before recognizing again.
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { lowerCase } from 'lodash';
|
||||
import { UncategorizedBankTransaction } from '../BankingTransactions/models/UncategorizedBankTransaction';
|
||||
import { BankRuleApplyIfTransactionType, BankRuleConditionComparator, BankRuleConditionType, IBankRuleCondition } from '../BankRules/types';
|
||||
import {
|
||||
BankRuleApplyIfTransactionType,
|
||||
BankRuleConditionComparator,
|
||||
BankRuleConditionType,
|
||||
IBankRuleCondition,
|
||||
} from '../BankRules/types';
|
||||
import { BankRule } from '../BankRules/models/BankRule';
|
||||
import { BankRuleCondition } from '../BankRules/models/BankRuleCondition';
|
||||
|
||||
const conditionsMatch = (
|
||||
transaction: UncategorizedBankTransaction,
|
||||
conditions: BankRuleCondition[],
|
||||
conditionsType: BankRuleConditionType = BankRuleConditionType.And
|
||||
conditionsType: BankRuleConditionType = BankRuleConditionType.And,
|
||||
) => {
|
||||
const method =
|
||||
conditionsType === BankRuleConditionType.And ? 'every' : 'some';
|
||||
@@ -26,7 +31,7 @@ const conditionsMatch = (
|
||||
|
||||
const matchNumberCondition = (
|
||||
transaction: UncategorizedBankTransaction,
|
||||
condition: BankRuleCondition
|
||||
condition: BankRuleCondition,
|
||||
) => {
|
||||
const conditionValue = parseFloat(condition.value);
|
||||
const transactionAmount =
|
||||
@@ -58,7 +63,7 @@ const matchNumberCondition = (
|
||||
|
||||
const matchTextCondition = (
|
||||
transaction: UncategorizedBankTransaction,
|
||||
condition: BankRuleCondition
|
||||
condition: BankRuleCondition,
|
||||
): boolean => {
|
||||
const transactionValue = transaction[condition.field] as string;
|
||||
|
||||
@@ -80,7 +85,7 @@ const matchTextCondition = (
|
||||
|
||||
const matchTransactionType = (
|
||||
bankRule: BankRule,
|
||||
transaction: UncategorizedBankTransaction
|
||||
transaction: UncategorizedBankTransaction,
|
||||
): boolean => {
|
||||
return (
|
||||
(transaction.isDepositTransaction &&
|
||||
@@ -94,7 +99,7 @@ const matchTransactionType = (
|
||||
|
||||
export const bankRulesMatchTransaction = (
|
||||
transaction: UncategorizedBankTransaction,
|
||||
bankRules: BankRule[]
|
||||
bankRules: BankRule[],
|
||||
) => {
|
||||
return bankRules.find((rule) => {
|
||||
return (
|
||||
|
||||
-1
@@ -55,7 +55,6 @@ export class TriggerRecognizedTransactionsSubscriber {
|
||||
oldBankRule,
|
||||
bankRule,
|
||||
}: IBankRuleEventEditedPayload) {
|
||||
|
||||
// Cannot continue if the new and old bank rule values are the same,
|
||||
// after excluding `createdAt` and `updatedAt` dates.
|
||||
if (
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
|
||||
export class GetRecognizedTransactionTransformer extends Transformer {
|
||||
/**
|
||||
@@ -48,7 +48,7 @@ export class GetRecognizedTransactionTransformer extends Transformer {
|
||||
*/
|
||||
public uncategorizedTransactionId = (transaction): number => {
|
||||
return transaction.id;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the reference number of the transaction.
|
||||
@@ -174,7 +174,7 @@ export class GetRecognizedTransactionTransformer extends Transformer {
|
||||
* @returns {string}
|
||||
*/
|
||||
public assignedCategoryFormatted() {
|
||||
return 'Other Income'
|
||||
return 'Other Income';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -82,7 +82,7 @@ const models = [
|
||||
...models,
|
||||
RemovePendingUncategorizedTransaction,
|
||||
CommandBankTransactionValidator,
|
||||
CreateBankTransactionService
|
||||
CreateBankTransactionService,
|
||||
],
|
||||
})
|
||||
export class BankingTransactionsModule {}
|
||||
|
||||
+3
-1
@@ -11,7 +11,9 @@ export class BankTransactionGLEntriesService {
|
||||
private readonly ledgerStorage: LedgerStorageService,
|
||||
|
||||
@Inject(BankTransaction.name)
|
||||
private readonly bankTransactionModel: TenantModelProxy<typeof BankTransaction>,
|
||||
private readonly bankTransactionModel: TenantModelProxy<
|
||||
typeof BankTransaction
|
||||
>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
||||
+8
-8
@@ -19,14 +19,14 @@ export class CommandBankTransactionValidator {
|
||||
*/
|
||||
public validateCreditAccountWithCashflowType = (
|
||||
creditAccount: Account,
|
||||
cashflowTransactionType: CASHFLOW_TRANSACTION_TYPE
|
||||
cashflowTransactionType: CASHFLOW_TRANSACTION_TYPE,
|
||||
): void => {
|
||||
const transactionTypeMeta = getCashflowTransactionType(
|
||||
cashflowTransactionType
|
||||
cashflowTransactionType,
|
||||
);
|
||||
const noneCashflowAccount = !includes(
|
||||
transactionTypeMeta.creditType,
|
||||
creditAccount.accountType
|
||||
creditAccount.accountType,
|
||||
);
|
||||
if (noneCashflowAccount) {
|
||||
throw new ServiceError(ERRORS.CREDIT_ACCOUNTS_HAS_INVALID_TYPE);
|
||||
@@ -40,7 +40,7 @@ export class CommandBankTransactionValidator {
|
||||
*/
|
||||
public validateCashflowTransactionType = (transactionType: string) => {
|
||||
const transformedType = upperFirst(
|
||||
camelCase(transactionType)
|
||||
camelCase(transactionType),
|
||||
) as CASHFLOW_TRANSACTION_TYPE;
|
||||
|
||||
// Retrieve the given transaction type meta.
|
||||
@@ -58,7 +58,7 @@ export class CommandBankTransactionValidator {
|
||||
* @param {CashflowTransaction} cashflowTransaction
|
||||
*/
|
||||
public validateTransactionShouldCategorized(
|
||||
cashflowTransaction: BankTransaction
|
||||
cashflowTransaction: BankTransaction,
|
||||
) {
|
||||
if (!cashflowTransaction.uncategorize) {
|
||||
throw new ServiceError(ERRORS.TRANSACTION_ALREADY_CATEGORIZED);
|
||||
@@ -70,7 +70,7 @@ export class CommandBankTransactionValidator {
|
||||
* @param {CashflowTransaction} cashflowTransaction
|
||||
*/
|
||||
public validateTransactionsShouldNotCategorized(
|
||||
cashflowTransactions: Array<UncategorizedBankTransaction>
|
||||
cashflowTransactions: Array<UncategorizedBankTransaction>,
|
||||
) {
|
||||
const categorized = cashflowTransactions.filter((t) => t.categorized);
|
||||
|
||||
@@ -89,14 +89,14 @@ export class CommandBankTransactionValidator {
|
||||
*/
|
||||
public validateUncategorizeTransactionType(
|
||||
uncategorizeTransactions: Array<UncategorizedBankTransaction>,
|
||||
transactionType: string
|
||||
transactionType: string,
|
||||
) {
|
||||
const amount = sumBy(uncategorizeTransactions, 'amount');
|
||||
const isDepositTransaction = amount > 0;
|
||||
const isWithdrawalTransaction = amount <= 0;
|
||||
|
||||
const type = getCashflowTransactionType(
|
||||
transactionType as CASHFLOW_TRANSACTION_TYPE
|
||||
transactionType as CASHFLOW_TRANSACTION_TYPE,
|
||||
);
|
||||
if (
|
||||
(type.direction === CASHFLOW_DIRECTION.IN && isDepositTransaction) ||
|
||||
|
||||
+3
-1
@@ -34,7 +34,9 @@ export class BankingUncategorizedTransactionsController {
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Returns autofill values for categorize transactions',
|
||||
schema: { $ref: getSchemaPath(GetAutofillCategorizeTransactionResponseDto) },
|
||||
schema: {
|
||||
$ref: getSchemaPath(GetAutofillCategorizeTransactionResponseDto),
|
||||
},
|
||||
})
|
||||
async getAutofillCategorizeTransaction(
|
||||
@Query('uncategorizedTransactionIds')
|
||||
|
||||
+28
-7
@@ -13,7 +13,10 @@ export class GetAutofillCategorizeTransactionResponseDto {
|
||||
})
|
||||
debitAccountId?: number | null;
|
||||
|
||||
@ApiProperty({ description: 'Total amount of uncategorized transactions', example: -150.5 })
|
||||
@ApiProperty({
|
||||
description: 'Total amount of uncategorized transactions',
|
||||
example: -150.5,
|
||||
})
|
||||
amount: number;
|
||||
|
||||
@ApiProperty({ description: 'Formatted amount', example: '$150.50' })
|
||||
@@ -25,25 +28,43 @@ export class GetAutofillCategorizeTransactionResponseDto {
|
||||
@ApiProperty({ description: 'Formatted date', example: 'Jan 15, 2024' })
|
||||
formattedDate: string;
|
||||
|
||||
@ApiProperty({ description: 'Whether the transaction is recognized by a rule', example: true })
|
||||
@ApiProperty({
|
||||
description: 'Whether the transaction is recognized by a rule',
|
||||
example: true,
|
||||
})
|
||||
isRecognized: boolean;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Bank rule ID that recognized the transaction', example: 1 })
|
||||
@ApiPropertyOptional({
|
||||
description: 'Bank rule ID that recognized the transaction',
|
||||
example: 1,
|
||||
})
|
||||
recognizedByRuleId?: number | null;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Bank rule name that recognized the transaction', example: 'Salary Rule' })
|
||||
@ApiPropertyOptional({
|
||||
description: 'Bank rule name that recognized the transaction',
|
||||
example: 'Salary Rule',
|
||||
})
|
||||
recognizedByRuleName?: string | null;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Reference number', example: 'REF-001' })
|
||||
referenceNo?: string | null;
|
||||
|
||||
@ApiProperty({ description: 'Transaction type (category)', example: 'other_expense' })
|
||||
@ApiProperty({
|
||||
description: 'Transaction type (category)',
|
||||
example: 'other_expense',
|
||||
})
|
||||
transactionType: string;
|
||||
|
||||
@ApiProperty({ description: 'Whether this is a deposit transaction', example: false })
|
||||
@ApiProperty({
|
||||
description: 'Whether this is a deposit transaction',
|
||||
example: false,
|
||||
})
|
||||
isDepositTransaction: boolean;
|
||||
|
||||
@ApiProperty({ description: 'Whether this is a withdrawal transaction', example: true })
|
||||
@ApiProperty({
|
||||
description: 'Whether this is a withdrawal transaction',
|
||||
example: true,
|
||||
})
|
||||
isWithdrawalTransaction: boolean;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Assigned payee from recognition' })
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user