feat: rewrite repositories with base entity repository class.

feat: sales and purchases status.
feat: sales and purchases auto-increment number.
fix: settings find query with extra columns.
This commit is contained in:
Ahmed Bouhuolia
2020-12-13 19:50:59 +02:00
parent e9e4ddaee0
commit 188e411f02
78 changed files with 1634 additions and 869 deletions

View File

@@ -1,124 +1,28 @@
import { Account } from 'models';
import TenantRepository from 'repositories/TenantRepository';
import { IAccount } from 'interfaces';
export default class AccountRepository extends TenantRepository {
/**
* Constructor method.
*/
constructor(knex, cache) {
super(knex, cache);
this.model = Account;
}
/**
* Retrieve accounts dependency graph.
* @returns {}
*/
async getDependencyGraph() {
const { Account } = this.models;
const accounts = await this.allAccounts();
const cacheKey = this.getCacheKey('accounts.depGraph');
async getDependencyGraph(withRelation) {
const accounts = await this.all(withRelation);
const cacheKey = this.getCacheKey('accounts.depGraph', withRelation);
return this.cache.get(cacheKey, async () => {
return Account.toDependencyGraph(accounts);
return this.model.toDependencyGraph(accounts);
});
}
/**
* Retrieve all accounts on the storage.
* @return {IAccount[]}
*/
allAccounts(withRelations?: string|string[]): IAccount[] {
const { Account } = this.models;
const cacheKey = this.getCacheKey('accounts.depGraph', withRelations);
return this.cache.get(cacheKey, async () => {
return Account.query()
.withGraphFetched(withRelations);
});
}
/**
* Retrieve account of the given account slug.
* @param {string} slug
* @return {IAccount}
*/
getBySlug(slug: string): IAccount {
const { Account } = this.models;
const cacheKey = this.getCacheKey('accounts.slug', slug);
return this.cache.get(cacheKey, () => {
return Account.query().findOne('slug', slug);
});
}
/**
* Retrieve the account by the given id.
* @param {number} id - Account id.
* @return {IAccount}
*/
findById(id: number): IAccount {
const { Account } = this.models;
const cacheKey = this.getCacheKey('accounts.id', id);
return this.cache.get(cacheKey, () => {
return Account.query().findById(id);
});
}
/**
* Retrieve accounts by the given ids.
* @param {number[]} ids -
* @return {IAccount[]}
*/
findByIds(accountsIds: number[]) {
const { Account } = this.models;
const cacheKey = this.getCacheKey('accounts.id', accountsIds);
return this.cache.get(cacheKey, () => {
return Account.query().whereIn('id', accountsIds);
});
}
/**
* Activate the given account.
* @param {number} accountId -
* @return {void}
*/
async activate(accountId: number): Promise<void> {
const { Account } = this.models;
await Account.query().findById(accountId).patch({ active: 1 })
this.flushCache();
}
/**
* Inserts a new accounts to the storage.
* @param {IAccount} account
*/
async insert(accountInput: IAccount): Promise<void> {
const { Account } = this.models;
const account = await Account.query().insertAndFetch({ ...accountInput });
this.flushCache();
return account;
}
/**
* Updates account of the given account.
* @param {number} accountId - Account id.
* @param {IAccount} account
* @return {void}
*/
async edit(accountId: number, accountInput: IAccount): Promise<void> {
const { Account } = this.models;
const account = await Account.query().patchAndFetchById(accountId, { ...accountInput });
this.flushCache();
return account;
}
/**
* Deletes the given account by id.
* @param {number} accountId - Account id.
*/
async deleteById(accountId: number): Promise<void> {
const { Account } = this.models;
await Account.query().deleteById(accountId);
this.flushCache();
}
/**
* Changes account balance.
* @param {number} accountId
@@ -126,17 +30,9 @@ export default class AccountRepository extends TenantRepository {
* @return {Promise<void>}
*/
async balanceChange(accountId: number, amount: number): Promise<void> {
const { Account } = this.models;
const method: string = (amount < 0) ? 'decrement' : 'increment';
await Account.query().where('id', accountId)[method]('amount', amount);
await this.model.query().where('id', accountId)[method]('amount', amount);
this.flushCache();
}
/**
* Flush repository cache.
*/
flushCache(): void {
this.cache.delStartWith(this.repositoryName);
}
}

View File

@@ -1,7 +1,4 @@
import { QueryBuilder } from 'knex';
import { AccountTransaction } from 'models';
import hashObject from 'object-hash';
import TenantRepository from 'repositories/TenantRepository';
@@ -17,13 +14,19 @@ interface IJournalTransactionsFilter {
};
export default class AccountTransactionsRepository extends TenantRepository {
/**
* Constructor method.
*/
constructor(knex, cache) {
super(knex, cache);
this.model = AccountTransaction;
}
journal(filter: IJournalTransactionsFilter) {
const { AccountTransaction } = this.models;
const cacheKey = this.getCacheKey('transactions.journal', filter);
return this.cache.get(cacheKey, () => {
return AccountTransaction.query()
return this.model.query()
.modify('filterAccounts', filter.accountsIds)
.modify('filterDateRange', filter.fromDate, filter.toDate)
.withGraphFetched('account.type')

View File

@@ -1,81 +1,49 @@
import TenantRepository from 'repositories/TenantRepository';
import { IAccountType } from 'interfaces';
import { AccountType } from 'models';
export default class AccountTypeRepository extends TenantRepository {
/**
* Retrieve all accounts types.
* @return {IAccountType[]}
* Constructor method.
*/
all() {
const { AccountType } = this.models;
return this.cache.get('accountType.all', () => {
return AccountType.query();
});
}
/**
* Retrieve account type meta.
* @param {number} accountTypeId
* @return {IAccountType}
*/
getTypeMeta(accountTypeId: number): IAccountType {
const { AccountType } = this.models;
return this.cache.get(`accountType.id.${accountTypeId}`, () => {
return AccountType.query().findById(accountTypeId);
});
constructor(knex, cache) {
super(knex, cache);
this.model = AccountType;
}
/**
* Retrieve accounts types of the given keys.
* @param {string[]} keys
* @return {IAccountType[]}
* @return {Promise<IAccountType[]>}
*/
getByKeys(keys: string[]): IAccountType[] {
const { AccountType } = this.models;
return this.cache.get(`accountType.keys.${keys.join(',')}`, () => {
return AccountType.query().whereIn('key', keys);
});
getByKeys(keys: string[]): Promise<IAccountType[]> {
return super.findWhereIn('key', keys);
}
/**
* Retrieve account tpy eof the given key.
* @param {string} key
* @return {IAccountType}
* @return {Promise<IAccountType>}
*/
getByKey(key: string): IAccountType {
const { AccountType } = this.models;
return this.cache.get(`accountType.key.${key}`, () => {
return AccountType.query().findOne('key', key);
});
getByKey(key: string): Promise<IAccountType> {
return super.findOne({ key });
}
/**
* Retrieve accounts types of the given root type.
* @param {string} rootType
* @return {IAccountType[]}
* @return {Promise<IAccountType[]>}
*/
getByRootType(rootType: string): Promise<IAccountType[]> {
const { AccountType } = this.models;
return this.cache.get(`accountType.rootType.${rootType}`, () => {
return AccountType.query().where('root_type', rootType);
});
return super.find({ root_type: rootType });
}
/**
* Retrieve accounts types of the given child type.
* @param {string} childType
* @return {Promise<IAccountType[]>}
*/
getByChildType(childType: string): Promise<IAccountType[]> {
const { AccountType } = this.models;
return this.cache.get(`accountType.childType.${childType}`, () => {
return AccountType.query().where('child_type', childType);
});
}
/**
* Flush repository cache.
*/
flushCache() {
this.cache.delStartWith('accountType');
return super.find({ child_type: childType });
}
}

View File

@@ -2,10 +2,4 @@
export default class BaseModelRepository {
isExists(modelIdOrArray) {
const ids = Array.isArray(modelIdOrArray) ? modelIdOrArray : [modelIdOrArray];
const foundModels = this.model.tenant().query().whereIn('id', ids);
return foundModels.length > 0;
}
}

View File

@@ -0,0 +1,12 @@
import { Bill } from 'models';
import TenantRepository from 'repositories/TenantRepository';
export default class BillRepository extends TenantRepository {
/**
* Constructor method.
*/
constructor(knex, cache) {
super(knex, cache);
this.model = Bill;
}
}

View File

@@ -1,9 +1,20 @@
import hashObject from 'object-hash';
import EntityRepository from './EntityRepository';
export default class CachableRepository {
export default class CachableRepository extends EntityRepository{
repositoryName: string;
cache: any;
/**
* Constructor method.
* @param {Knex} knex
* @param {Cache} cache
*/
constructor(knex, cache) {
super(knex);
this.cache = cache;
}
/**
* Retrieve the cache key of the method name and arguments.
* @param {string} method
@@ -16,4 +27,197 @@ export default class CachableRepository {
return `${repositoryName}-${method}-${hashArgs}`;
}
/**
* Retrieve all entries with specified relations.
* @param withRelations
*/
all(withRelations?) {
const cacheKey = this.getCacheKey('all', withRelations);
return this.cache.get(cacheKey, () => {
return super.all(withRelations);
});
}
/**
* Finds list of entities with specified attributes
* @param {Object} attributeValues - values to filter retrieved entities by
* @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve.
* @returns {Promise<Object[]>} - query builder. You can chain additional methods to it or call "await" or then() on it to execute
*/
find(attributeValues = {}, withRelations?) {
const cacheKey = this.getCacheKey('find', attributeValues, withRelations);
return this.cache.get(cacheKey, () => {
return super.find(attributeValues, withRelations);
});
}
/**
* Finds list of entities with attribute values that are different from specified ones
* @param {Object} attributeValues - values to filter retrieved entities by
* @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings()
* @returns {Promise<Object[]>} - query builder. You can chain additional methods to it or call "await" or then() on it to execute
*/
findWhereNot(attributeValues = {}, withRelations?) {
const cacheKey = this.getCacheKey('findWhereNot', attributeValues, withRelations);
return this.cache.get(cacheKey, () => {
return super.findWhereNot(attributeValues, withRelations);
});
}
/**
* Finds list of entities with specified attributes (any of multiple specified values)
* Supports both ('attrName', ['value1', 'value2]) and ({attrName: ['value1', 'value2']} formats)
*
* @param {string|Object} searchParam - attribute name or search criteria object
* @param {*[]} [attributeValues] - attribute values to filter retrieved entities by
* @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings()
* @returns {PromiseLike<Object[]>} - query builder. You can chain additional methods to it or call "await" or then() on it to execute
*/
findWhereIn(searchParam, attributeValues, withRelations?) {
const cacheKey = this.getCacheKey('findWhereIn', attributeValues, withRelations);
return this.cache.get(cacheKey, () => {
return super.findWhereIn(searchParam, attributeValues, withRelations);
});
}
/**
* Finds first entity by given parameters
*
* @param {Object} attributeValues - values to filter retrieved entities by
* @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings()
* @returns {Promise<Object>}
*/
findOne(attributeValues = {}, withRelations?) {
const cacheKey = this.getCacheKey('findOne', attributeValues, withRelations);
return this.cache.get(cacheKey, () => {
return super.findOne(attributeValues, withRelations);
});
}
/**
* Finds first entity by given parameters
*
* @param {string || number} id - value of id column of the entity
* @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings()
* @returns {Promise<Object>}
*/
findOneById(id, withRelations?) {
const cacheKey = this.getCacheKey('findOneById', id, withRelations);
return this.cache.get(cacheKey, () => {
return super.findOneById(id, withRelations);
});
}
/**
* Persists new entity or an array of entities.
* This method does not recursively persist related entities, use createRecursively (to be implemented) for that.
* Batch insert only works on PostgreSQL
* @param {Object} entity - model instance or parameters for a new entity
* @returns {Promise<Object>} - query builder. You can chain additional methods to it or call "await" or then() on it to execute
*/
async create(entity) {
const result = await super.create(entity);
// Flushes the repository cache after insert operation.
this.flushCache();
return result;
}
/**
* Persists updated entity. If previously set fields are not present, performs an incremental update (does not remove fields unless explicitly set to null)
*
* @param {Object} entity - single entity instance
* @param {Object} [trx] - knex transaction instance. If not specified, new implicit transaction will be used.
* @returns {Promise<integer>} number of affected rows
*/
async update(entity, whereAttributes?) {
const result = await super.update(entity, whereAttributes);
// Flushes the repository cache after update operation.
this.flushCache();
return result;
}
/**
* @param {Object} attributeValues - values to filter deleted entities by
* @param {Object} [trx]
* @returns {Promise<integer>} Query builder. After promise is resolved, returns count of deleted rows
*/
async deleteBy(attributeValues) {
const result = await super.deleteBy(attributeValues);
this.flushCache();
return result;
}
/**
* @param {string || number} id - value of id column of the entity
* @returns {Promise<integer>} Query builder. After promise is resolved, returns count of deleted rows
*/
deleteById(id: number|string) {
const result = super.deleteById(id);
// Flushes the repository cache after insert operation.
this.flushCache();
return result;
}
/**
*
* @param {string|number[]} values -
*/
async deleteWhereIn(values: string | number[]) {
const result = await super.deleteWhereIdIn(values);
// Flushes the repository cache after delete operation.
this.flushCache();
return result;
}
/**
*
* @param graph
* @param options
*/
async upsertGraph(graph, options) {
const result = await super.upsertGraph(graph, options);
// Flushes the repository cache after insert operation.
this.flushCache();
return result;
}
/**
*
* @param {} whereAttributes
* @param {string} field
* @param {number} amount
*/
async changeNumber(whereAttributes, field: string, amount: number) {
const result = await super.changeNumber(whereAttributes, field, amount);
// Flushes the repository cache after update operation.
this.flushCache();
return result;
}
/**
* Flush repository cache.
*/
flushCache(): void {
this.cache.delStartWith(this.repositoryName);
}
}

View File

@@ -1,76 +1,13 @@
import TenantRepository from 'repositories/TenantRepository';
import { IContact } from 'interfaces';
import { Contact } from 'models'
export default class ContactRepository extends TenantRepository {
/**
* Retrieve the given contact model.
* @param {number} contactId
*/
findById(contactId: number): IContact {
const { Contact } = this.models;
return this.cache.get(`contacts.id.${contactId}`, () => {
return Contact.query().findById(contactId);
})
}
/**
* Retrieve the given contacts model.
* @param {number[]} contactIds - Contacts ids.
* Constructor method.
*/
findByIds(contactIds: number[]): IContact[] {
const { Contact } = this.models;
return this.cache.get(`contacts.ids.${contactIds.join(',')}`, () => {
return Contact.query().whereIn('id', contactIds);
});
}
/**
* Inserts a new contact model.
* @param contact
*/
async insert(contactInput: IContact) {
const { Contact } = this.models;
const contact = await Contact.query().insert({ ...contactInput })
this.flushCache();
return contact;
}
/**
* Updates the contact details.
* @param {number} contactId - Contact id.
* @param {IContact} contact - Contact input.
*/
async update(contactId: number, contact: IContact) {
const { Contact } = this.models;
await Contact.query().findById(contactId).patch({ ...contact });
this.flushCache();
}
/**
* Deletes contact of the given id.
* @param {number} contactId -
* @return {Promise<void>}
*/
async deleteById(contactId: number): Promise<void> {
const { Contact } = this.models;
await Contact.query().where('id', contactId).delete();
this.flushCache();
}
/**
* Deletes contacts in bulk.
* @param {number[]} contactsIds
*/
async bulkDelete(contactsIds: number[]) {
const { Contact } = this.models;
await Contact.query().whereIn('id', contactsIds);
this.flushCache();
}
/**
* Flush contact repository cache.
*/
flushCache() {
this.cache.delStartWith(`contacts`);
constructor(knex, cache) {
super(knex, cache);
this.model = Contact;
}
}

View File

@@ -1,75 +1,16 @@
import TenantRepository from "./TenantRepository";
import { Customer } from 'models'
export default class CustomerRepository extends TenantRepository {
all() {
const { Contact } = this.models;
return this.cache.get('customers', () => {
return Contact.query().modify('customer');
});
}
/**
* Retrieve customer details of the given id.
* @param {number} customerId - Customer id.
* Constructor method.
*/
getById(customerId: number) {
const { Contact } = this.models;
return this.cache.get(`customers.id.${customerId}`, () => {
return Contact.query().modifier('customer').findById(customerId);
});
}
/**
* Detarmines the given customer exists.
* @param {number} customerId
* @returns {boolean}
*/
isExists(customerId: number) {
return !!this.getById(customerId);
}
/**
* Retrieve the sales invoices that assocaited to the given customer.
* @param {number} customerId
*/
getSalesInvoices(customerId: number) {
const { SaleInvoice } = this.models;
return this.cache.get(`customers.invoices.${customerId}`, () => {
return SaleInvoice.query().where('customer_id', customerId);
});
}
/**
* Retrieve customers details of the given ids.
* @param {number[]} customersIds - Customers ids.
* @return {IContact[]}
*/
customers(customersIds: number[]) {
const { Contact } = this.models;
return Contact.query().modifier('customer').whereIn('id', customersIds);
}
/**
* Retrieve customers of the given ids with associated sales invoices.
* @param {number[]} customersIds - Customers ids.
*/
customersWithSalesInvoices(customersIds: number[]) {
const { Contact } = this.models;
return Contact.query().modify('customer')
.whereIn('id', customersIds)
.withGraphFetched('salesInvoices');
constructor(knex, cache) {
super(knex, cache);
this.model = Customer;
}
changeBalance(vendorId: number, amount: number) {
const { Contact } = this.models;
const changeMethod = (amount > 0) ? 'increment' : 'decrement';
return Contact.query()
.where('id', vendorId)
[changeMethod]('balance', Math.abs(amount));
return super.changeNumber({ id: vendorId }, 'balance', amount);
}
async changeDiffBalance(

View File

@@ -0,0 +1,231 @@
import { cloneDeep, cloneDeepWith, forOwn, isString } from 'lodash';
import ModelEntityNotFound from 'exceptions/ModelEntityNotFound';
export default class EntityRepository {
modelInstance: any;
idColumn: string;
knex: any;
/**
* Constructor method.
* @param {Knex} knex
*/
constructor(knex) {
this.knex = knex;
this.idColumn = 'id';
}
/**
* Sets the model to the repository and bind it to knex instance.
*/
set model(model) {
if (!this.modelInstance) {
this.modelInstance = model.bindKnex(this.knex);
}
}
/**
* Retrieve the repository model binded it to knex instance.
*/
get model() {
return this.modelInstance;
}
/**
* Retrieve all entries with specified relations.
*
* @param withRelations
*/
all(withRelations?) {
return this.model.query().withGraphFetched(withRelations);
}
/**
* Finds list of entities with specified attributes
*
* @param {Object} attributeValues - values to filter retrieved entities by
* @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve.
* @returns {Promise<Object[]>} - query builder. You can chain additional methods to it or call "await" or then() on it to execute
*/
find(attributeValues = {}, withRelations?) {
return this.model
.query()
.where(attributeValues)
.withGraphFetched(withRelations);
}
/**
* Finds list of entities with attribute values that are different from specified ones
*
* @param {Object} attributeValues - values to filter retrieved entities by
* @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings()
* @returns {PromiseLike<Object[]>} - query builder. You can chain additional methods to it or call "await" or then() on it to execute
*/
findWhereNot(attributeValues = {}, withRelations?) {
return this.model
.query()
.whereNot(attributeValues)
.withGraphFetched(withRelations);
}
/**
* Finds list of entities with specified attributes (any of multiple specified values)
* Supports both ('attrName', ['value1', 'value2]) and ({attrName: ['value1', 'value2']} formats)
*
* @param {string|Object} searchParam - attribute name or search criteria object
* @param {*[]} [attributeValues] - attribute values to filter retrieved entities by
* @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings()
* @returns {PromiseLike<Object[]>} - query builder. You can chain additional methods to it or call "await" or then() on it to execute
*/
findWhereIn(searchParam, attributeValues, withRelations?) {
if (isString(searchParam)) {
return this.model
.query()
.whereIn(searchParam, attributeValues)
.withGraphFetched(withRelations);
} else {
const builder = this.model.query(this.knex).withGraphFetched(withRelations);
forOwn(searchParam, (value, key) => {
if (Array.isArray(value)) {
builder.whereIn(key, value);
} else {
builder.where(key, value);
}
});
return builder;
}
}
/**
* Finds first entity by given parameters
*
* @param {Object} attributeValues - values to filter retrieved entities by
* @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings()
* @returns {Promise<Object>}
*/
async findOne(attributeValues = {}, withRelations?) {
const results = await this.find(attributeValues, withRelations);
return results[0] || null;
}
/**
* Finds first entity by given parameters
*
* @param {string || number} id - value of id column of the entity
* @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings()
* @returns {Promise<Object>}
*/
findOneById(id, withRelations?) {
return this.findOne({ [this.idColumn]: id }, withRelations);
}
/**
* Persists new entity or an array of entities.
* This method does not recursively persist related entities, use createRecursively (to be implemented) for that.
* Batch insert only works on PostgreSQL
*
* @param {Object} entity - model instance or parameters for a new entity
* @returns {Promise<Object>} - query builder. You can chain additional methods to it or call "await" or then() on it to execute
*/
create(entity) {
// Keep the input parameter immutable
const instanceDTO = cloneDeep(entity);
return this.model.query().insert(instanceDTO);
}
/**
* Persists updated entity. If previously set fields are not present, performs an incremental update (does not remove fields unless explicitly set to null)
*
* @param {Object} entity - single entity instance
* @returns {Promise<integer>} number of affected rows
*/
async update(entity, whereAttributes?) {
const entityDto = cloneDeep(entity);
const identityClause = {};
if (Array.isArray(this.idColumn)) {
this.idColumn.forEach((idColumn) => (identityClause[idColumn] = entityDto[idColumn]));
} else {
identityClause[this.idColumn] = entityDto[this.idColumn];
}
const whereConditions = (whereAttributes || identityClause);
const modifiedEntitiesCount = await this.model
.query()
.where(whereConditions)
.update(entityDto);
if (modifiedEntitiesCount === 0) {
throw new ModelEntityNotFound(entityDto[this.idColumn]);
}
return modifiedEntitiesCount;
}
/**
*
* @param {Object} attributeValues - values to filter deleted entities by
* @param {Object} [trx]
* @returns {Promise<integer>} Query builder. After promise is resolved, returns count of deleted rows
*/
deleteBy(attributeValues) {
return this.model
.query()
.delete()
.where(attributeValues);
}
/**
* @param {string || number} id - value of id column of the entity
* @returns {Promise<integer>} Query builder. After promise is resolved, returns count of deleted rows
*/
deleteById(id: number|string) {
return this.deleteBy({
[this.idColumn]: id
});
}
/**
*
* @param {string} field -
* @param {number|string} values -
*/
deleteWhereIn(field: string, values: string|number[]) {
return this.model
.query()
.whereIn(field, values)
.delete();
}
/**
*
* @param {string|number[]} values
*/
deleteWhereIdIn(values: string|number[]) {
return this.deleteWhereIn(this.idColumn, values);
}
/**
*
* @param graph
* @param options
*/
upsertGraph(graph, options) {
// Keep the input grpah immutable
const graphCloned = cloneDeep(graph);
return this.model.upsertGraph(graphCloned)
}
/**
*
* @param {object} whereAttributes
* @param {string} field
* @param amount
*/
changeNumber(whereAttributes, field: string, amount: number) {
const changeMethod = (amount > 0) ? 'increment' : 'decrement';
return this.model.query()
.where(whereAttributes)
[changeMethod](field, Math.abs(amount));
}
}

View File

@@ -0,0 +1,12 @@
import TenantRepository from "./TenantRepository";
import { ExpenseCategory } from 'models';
export default class ExpenseEntyRepository extends TenantRepository {
/**
* Constructor method.
*/
constructor(knex, cache) {
super(knex, cache);
this.model = ExpenseCategory;
}
}

View File

@@ -1,42 +1,13 @@
import TenantRepository from "./TenantRepository";
import { IExpense } from 'interfaces';
import moment from "moment";
import { Expense } from 'models';
export default class ExpenseRepository extends TenantRepository {
/**
* Retrieve the given expense by id.
* @param {number} expenseId
* @return {Promise<IExpense>}
* Constructor method.
*/
getById(expenseId: number) {
const { Expense } = this.models;
return this.cache.get(`expense.id.${expenseId}`, () => {
return Expense.query().findById(expenseId).withGraphFetched('categories');
});
}
/**
* Inserts a new expense object.
* @param {IExpense} expense -
*/
async create(expenseInput: IExpense): Promise<void> {
const { Expense } = this.models;
const expense = await Expense.query().insertGraph({ ...expenseInput });
this.flushCache();
return expense;
}
/**
* Updates the given expense details.
* @param {number} expenseId
* @param {IExpense} expense
*/
async update(expenseId: number, expense: IExpense) {
const { Expense } = this.models;
await Expense.query().findById(expenseId).patch({ ...expense });
this.flushCache();
constructor(knex, cache) {
super(knex, cache);
this.model = Expense;
}
/**
@@ -44,38 +15,10 @@ export default class ExpenseRepository extends TenantRepository {
* @param {number} expenseId
*/
async publish(expenseId: number): Promise<void> {
const { Expense } = this.models;
await Expense.query().findById(expenseId).patch({
super.update({
id: expenseId,
publishedAt: moment().toMySqlDateTime(),
});
this.flushCache();
}
/**
* Deletes the given expense.
* @param {number} expenseId
*/
async delete(expenseId: number): Promise<void> {
const { Expense, ExpenseCategory } = this.models;
await ExpenseCategory.query().where('expense_id', expenseId).delete();
await Expense.query().where('id', expenseId).delete();
this.flushCache();
}
/**
* Deletes expenses in bulk.
* @param {number[]} expensesIds
*/
async bulkDelete(expensesIds: number[]): Promise<void> {
const { Expense, ExpenseCategory } = this.models;
await ExpenseCategory.query().whereIn('expense_id', expensesIds).delete();
await Expense.query().whereIn('id', expensesIds).delete();
this.flushCache();
}
/**
@@ -83,18 +26,10 @@ export default class ExpenseRepository extends TenantRepository {
* @param {number[]} expensesIds
* @return {Promise<void>}
*/
async bulkPublish(expensesIds: number): Promise<void> {
const { Expense } = this.models;
await Expense.query().whereIn('id', expensesIds).patch({
async whereIdInPublish(expensesIds: number): Promise<void> {
await this.model.query().whereIn('id', expensesIds).patch({
publishedAt: moment().toMySqlDateTime(),
});
this.flushCache();
}
/**
* Flushes repository cache.
*/
flushCache() {
this.cache.delStartWith(`expense`);
}
}

View File

@@ -0,0 +1,13 @@
import { Item } from "models";
import TenantRepository from "./TenantRepository";
export default class ItemRepository extends TenantRepository {
/**
* Constructor method.
*/
constructor(knex, cache) {
super(knex, cache);
this.model = Item;
}
}

View File

@@ -1,18 +1,12 @@
import { IBalanceSheetQuery } from 'interfaces';
import { ManualJournal } from 'models';
import TenantRepository from 'repositories/TenantRepository';
export default class JournalRepository extends TenantRepository {
balanceSheet(query: IBalanceSheetQuery) {
// Accounts dependency graph.
const accountsGraph = Account.toDependencyGraph(balanceSheetAccounts);
// Load all entries that associated to the given accounts.
const journalEntriesCollected = Account.collectJournalEntries(balanceSheetAccounts);
const journalEntries = new JournalPoster(accountsGraph);
journalEntries.loadEntries(journalEntriesCollected);
/**
* Constructor method.
*/
constructor(knex, cache) {
super(knex, cache);
this.model = ManualJournal;
}
}

View File

@@ -1,55 +0,0 @@
import { omit } from 'lodash';
import BaseModelRepository from 'repositories/BaseModelRepository';
import { PaymentReceiveEntry } from 'models';
export default class PaymentReceiveEntryRepository extends BaseModelRepository {
/**
* Insert payment receive entries in bulk.
* @param {Array} entries
* @param {Integr} paymentReceiveId
* @return {Promise}
*/
static insertBulk(entries, paymentReceiveId) {
const opers = [];
entries.forEach((entry) => {
const insertOper = PaymentReceiveEntry.tenant()
.query()
.insert({
payment_receive_id: paymentReceiveId,
...entry,
});
opers.push(insertOper);
});
return Promise.all(opers);
}
/**
* Update payment receive entries in bulk.
* @param {Array} entries
* @return {Promise}
*/
static updateBulk(entries) {
const opers = [];
entries.forEach((entry) => {
const updateOper = PaymentReceiveEntry.tenant()
.query()
.patchAndFetchById(entry.id, {
...omit(entry, ['id', 'index']),
});
opers.push(updateOper);
});
return Promise.all(opers);
}
/**
* Deletes the given payment receive entries ids in bulk.
* @param {Array} entriesIds
* @return {Promise}
*/
static deleteBulk(entriesIds) {
return PaymentReceiveEntry.tenant()
.query()
.whereIn('id', entriesIds)
.delete();
}
}

View File

@@ -0,0 +1,12 @@
import { PaymentReceiveEntry } from 'models';
import TenantRepository from 'repositories/TenantRepository';
export default class PaymentReceiveEntryRepository extends TenantRepository {
/**
* Constructor method.
*/
constructor(knex, cache) {
super(knex, cache);
this.model = PaymentReceiveEntry;
}
}

View File

@@ -1,7 +0,0 @@
import { omit } from 'lodash';
import { PaymentReceiveEntry } from 'models';
import BaseModelRepository from 'repositories/BaseModelRepository';
export default class PaymentReceiveRepository extends BaseModelRepository {
}

View File

@@ -0,0 +1,12 @@
import { PaymentReceive } from 'models';
import TenantRepository from 'repositories/TenantRepository';
export default class PaymentReceiveRepository extends TenantRepository {
/**
* Constructor method.
*/
constructor(knex, cache) {
super(knex, cache);
this.model = PaymentReceive;
}
}

View File

@@ -1,7 +0,0 @@
export default class SaleInvoiceRepository {
}

View File

@@ -0,0 +1,12 @@
import { SaleInvoice } from 'models';
import TenantRepository from 'repositories/TenantRepository';
export default class SaleInvoiceRepository extends TenantRepository {
/**
* Constructor method.
*/
constructor(knex, cache) {
super(knex, cache);
this.model = SaleInvoice;
}
}

View File

@@ -0,0 +1,12 @@
import TenantRepository from 'repositories/TenantRepository';
import Setting from 'models/Setting';
export default class SettingRepository extends TenantRepository {
/**
* Constructor method.
*/
constructor(knex, cache) {
super(knex, cache);
this.model = Setting;
}
}

View File

@@ -4,42 +4,13 @@ import CachableRepository from './CachableRepository';
export default class TenantRepository extends CachableRepository {
repositoryName: string;
tenantId: number;
tenancy: TenancyService;
modelsInstance: any;
repositoriesInstance: any;
cacheInstance: any;
/**
* Constructor method.
* @param {number} tenantId
*/
constructor(tenantId: number) {
super();
this.tenantId = tenantId;
this.tenancy = Container.get(TenancyService);
constructor(knex, cache) {
super(knex, cache);
this.repositoryName = this.constructor.name;
}
get models() {
if (!this.modelsInstance) {
this.modelsInstance = this.tenancy.models(this.tenantId);
}
return this.modelsInstance;
}
get repositories() {
if (!this.repositoriesInstance) {
this.repositoriesInstance = this.tenancy.repositories(this.tenantId);
}
return this.repositoriesInstance;
}
get cache() {
if (!this.cacheInstance) {
this.cacheInstance = this.tenancy.cache(this.tenantId);
}
return this.cacheInstance;
}
}

View File

@@ -1,58 +1,17 @@
import { IVendor } from "interfaces";
import { Vendor } from "models";
import TenantRepository from "./TenantRepository";
export default class VendorRepository extends TenantRepository {
/**
* Retrieve vendor details of the given id.
* @param {number} vendorId - Vendor id.
* Constructor method.
*/
findById(vendorId: number) {
const { Contact } = this.models;
return Contact.query().findById(vendorId);
}
/**
* Retrieve the bill that associated to the given vendor id.
* @param {number} vendorId - Vendor id.
*/
getBills(vendorId: number) {
const { Bill } = this.models;
return this.cache.get(`vendors.bills.${vendorId}`, () => {
return Bill.query().where('vendor_id', vendorId);
});
}
/**
* Retrieve all the given vendors.
* @param {numner[]} vendorsIds
* @return {IVendor}
*/
vendors(vendorsIds: number[]): IVendor[] {
const { Contact } = this.models;
return Contact.query().modifier('vendor').whereIn('id', vendorsIds);
}
/**
* Retrieve vendors with associated bills.
* @param {number[]} vendorIds
*/
vendorsWithBills(vendorIds: number[]) {
const { Contact } = this.models;
return Contact.query().modify('vendor')
.whereIn('id', vendorIds)
.withGraphFetched('bills');
constructor(knex, cache) {
super(knex, cache);
this.model = Vendor;
}
changeBalance(vendorId: number, amount: number) {
const { Contact } = this.models;
const changeMethod = (amount > 0) ? 'increment' : 'decrement';
return Contact.query()
.where('id', vendorId)
[changeMethod]('balance', Math.abs(amount));
return super.changeNumber({ id: vendorId }, 'balance', amount);
}
async changeDiffBalance(

View File

@@ -1,60 +1,19 @@
import { IView } from 'interfaces';
import { View } from 'models';
import TenantRepository from 'repositories/TenantRepository';
export default class ViewRepository extends TenantRepository {
/**
* Retrieve view model by the given id.
* @param {number} id -
* Constructor method.
*/
getById(id: number) {
const { View } = this.models;
return this.cache.get(`customView.id.${id}`, () => {
return View.query().findById(id)
.withGraphFetched('columns')
.withGraphFetched('roles');
});
constructor(knex, cache) {
super(knex, cache);
this.model = View;
}
/**
* Retrieve all views of the given resource id.
*/
allByResource(resourceModel: string) {
const { View } = this.models;
return this.cache.get(`customView.resourceModel.${resourceModel}`, () => {
return View.query().where('resource_model', resourceModel)
.withGraphFetched('columns')
.withGraphFetched('roles');
});
}
/**
* Inserts a new view to the storage.
* @param {IView} view
*/
async insert(view: IView): Promise<IView> {
const { View } = this.models;
const insertedView = await View.query().insertGraph({ ...view });
this.flushCache();
return insertedView;
}
async update(viewId: number, view: IView): Promise<IView> {
const { View } = this.models;
const updatedView = await View.query().upsertGraph({
id: viewId,
...view
});
this.flushCache();
return updatedView;
}
/**
* Flushes repository cache.
*/
flushCache() {
this.cache.delStartWith('customView');
allByResource(resourceModel: string, withRelations?) {
return super.find({ resource_mode: resourceModel }, withRelations);
}
}

View File

@@ -3,10 +3,4 @@ import TenantRepository from 'repositories/TenantRepository';
export default class ViewRoleRepository extends TenantRepository {
allByView(viewId: number) {
const { ViewRole } = this.models;
return this.cache.get(`viewRole.view.${viewId}`, async () => {
return ViewRole.query().where('view_id', viewId);
});
}
}

View File

@@ -1,5 +0,0 @@
import ResourceRepository from './ResourceRepository';
export {
ResourceRepository,
};