mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +00:00
add server to monorepo.
This commit is contained in:
158
packages/server/src/repositories/AccountRepository.ts
Normal file
158
packages/server/src/repositories/AccountRepository.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { Account } from 'models';
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
import { IAccount } from '@/interfaces';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export default class AccountRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return Account.bindKnex(this.knex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve accounts dependency graph.
|
||||
* @returns {}
|
||||
*/
|
||||
async getDependencyGraph(withRelation, trx) {
|
||||
const accounts = await this.all(withRelation, trx);
|
||||
|
||||
return this.model.toDependencyGraph(accounts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve.
|
||||
* @param {string} slug
|
||||
* @return {Promise<IAccount>}
|
||||
*/
|
||||
findBySlug(slug: string) {
|
||||
return this.findOne({ slug });
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes account balance.
|
||||
* @param {number} accountId
|
||||
* @param {number} amount
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async balanceChange(accountId: number, amount: number): Promise<void> {
|
||||
const method: string = amount < 0 ? 'decrement' : 'increment';
|
||||
|
||||
await this.model.query().where('id', accountId)[method]('amount', amount);
|
||||
this.flushCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate user by the given id.
|
||||
* @param {number} userId - User id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
activateById(userId: number): Promise<IAccount> {
|
||||
return super.update({ active: 1 }, { id: userId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Inactivate user by the given id.
|
||||
* @param {number} userId - User id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
inactivateById(userId: number): Promise<void> {
|
||||
return super.update({ active: 0 }, { id: userId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate user by the given id.
|
||||
* @param {number} userId - User id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async activateByIds(userIds: number[], trx): Promise<IAccount> {
|
||||
const results = await this.model
|
||||
.query(trx)
|
||||
.whereIn('id', userIds)
|
||||
.patch({ active: true });
|
||||
|
||||
this.flushCache();
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inactivate user by the given id.
|
||||
* @param {number} userId - User id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async inactivateByIds(userIds: number[], trx): Promise<IAccount> {
|
||||
const results = await this.model
|
||||
.query(trx)
|
||||
.whereIn('id', userIds)
|
||||
.patch({ active: false });
|
||||
|
||||
this.flushCache();
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} currencyCode
|
||||
* @param extraAttrs
|
||||
* @param trx
|
||||
* @returns
|
||||
*/
|
||||
findOrCreateAccountReceivable = async (
|
||||
currencyCode: string = '',
|
||||
extraAttrs = {},
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
let result = await this.model
|
||||
.query(trx)
|
||||
.onBuild((query) => {
|
||||
if (currencyCode) {
|
||||
query.where('currencyCode', currencyCode);
|
||||
}
|
||||
query.where('accountType', 'accounts-receivable');
|
||||
})
|
||||
.first();
|
||||
|
||||
if (!result) {
|
||||
result = await this.model.query(trx).insertAndFetch({
|
||||
name: this.i18n.__('account.accounts_receivable.currency', {
|
||||
currency: currencyCode
|
||||
}),
|
||||
accountType: 'accounts-receivable',
|
||||
currencyCode,
|
||||
active: 1,
|
||||
...extraAttrs,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
findOrCreateAccountsPayable = async (
|
||||
currencyCode: string = '',
|
||||
extraAttrs = {},
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
let result = await this.model
|
||||
.query(trx)
|
||||
.onBuild((query) => {
|
||||
if (currencyCode) {
|
||||
query.where('currencyCode', currencyCode);
|
||||
}
|
||||
query.where('accountType', 'accounts-payable');
|
||||
})
|
||||
.first();
|
||||
|
||||
if (!result) {
|
||||
result = await this.model.query(trx).insertAndFetch({
|
||||
name: this.i18n.__('account.accounts_payable.currency', {
|
||||
currency: currencyCode,
|
||||
}),
|
||||
accountType: 'accounts-payable',
|
||||
currencyCode,
|
||||
active: 1,
|
||||
...extraAttrs,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { isEmpty, castArray } from 'lodash';
|
||||
import { AccountTransaction } from 'models';
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
|
||||
interface IJournalTransactionsFilter {
|
||||
fromDate: string | Date;
|
||||
toDate: string | Date;
|
||||
accountsIds: number[];
|
||||
sumationCreditDebit: boolean;
|
||||
fromAmount: number;
|
||||
toAmount: number;
|
||||
contactsIds?: number[];
|
||||
contactType?: string;
|
||||
referenceType?: string[];
|
||||
referenceId?: number[];
|
||||
index: number | number[];
|
||||
indexGroup: number | number[];
|
||||
branchesIds: number | number[];
|
||||
}
|
||||
|
||||
export default class AccountTransactionsRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return AccountTransaction.bindKnex(this.knex);
|
||||
}
|
||||
|
||||
journal(filter: IJournalTransactionsFilter) {
|
||||
return this.model
|
||||
.query()
|
||||
.modify('filterAccounts', filter.accountsIds)
|
||||
.modify('filterDateRange', filter.fromDate, filter.toDate)
|
||||
.withGraphFetched('account')
|
||||
.onBuild((query) => {
|
||||
if (filter.sumationCreditDebit) {
|
||||
query.modify('sumationCreditDebit');
|
||||
}
|
||||
if (filter.fromAmount || filter.toAmount) {
|
||||
query.modify('filterAmountRange', filter.fromAmount, filter.toAmount);
|
||||
}
|
||||
if (filter.contactsIds) {
|
||||
query.modify('filterContactIds', filter.contactsIds);
|
||||
}
|
||||
if (filter.contactType) {
|
||||
query.where('contact_type', filter.contactType);
|
||||
}
|
||||
if (filter.referenceType && filter.referenceType.length > 0) {
|
||||
query.whereIn('reference_type', filter.referenceType);
|
||||
}
|
||||
if (filter.referenceId && filter.referenceId.length > 0) {
|
||||
query.whereIn('reference_id', filter.referenceId);
|
||||
}
|
||||
if (filter.index) {
|
||||
if (Array.isArray(filter.index)) {
|
||||
query.whereIn('index', filter.index);
|
||||
} else {
|
||||
query.where('index', filter.index);
|
||||
}
|
||||
}
|
||||
if (filter.indexGroup) {
|
||||
if (Array.isArray(filter.indexGroup)) {
|
||||
query.whereIn('index_group', filter.indexGroup);
|
||||
} else {
|
||||
query.where('index_group', filter.indexGroup);
|
||||
}
|
||||
}
|
||||
if (!isEmpty(filter.branchesIds)) {
|
||||
query.modify('filterByBranches', filter.branchesIds);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openingBalance(fromDate) {
|
||||
return AccountTransaction.query().modify('openingBalance', fromDate);
|
||||
}
|
||||
|
||||
closingOpening(toDate) {
|
||||
return AccountTransaction.query().modify('closingBalance', toDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts the jouranl entries.
|
||||
* @param {number|number[]} referenceId - Reference id.
|
||||
* @param {string} referenceType - Reference type.
|
||||
*/
|
||||
public getTransactionsByReference = async (
|
||||
referenceId: number | number[],
|
||||
referenceType: string | string[]
|
||||
) => {
|
||||
const transactions = await this.model
|
||||
.query()
|
||||
.whereIn('reference_type', castArray(referenceType))
|
||||
.whereIn('reference_id', castArray(referenceId))
|
||||
.withGraphFetched('account');
|
||||
|
||||
return transactions;
|
||||
};
|
||||
}
|
||||
5
packages/server/src/repositories/BaseModelRepository.ts
Normal file
5
packages/server/src/repositories/BaseModelRepository.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
export default class BaseModelRepository {
|
||||
|
||||
}
|
||||
30
packages/server/src/repositories/BillRepository.ts
Normal file
30
packages/server/src/repositories/BillRepository.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import moment from 'moment';
|
||||
import { Bill } from 'models';
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
|
||||
export default class BillRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return Bill.bindKnex(this.knex);
|
||||
}
|
||||
|
||||
dueBills(asDate = moment().format('YYYY-MM-DD'), withRelations) {
|
||||
return this.model
|
||||
.query()
|
||||
.modify('dueBills')
|
||||
.modify('notOverdue')
|
||||
.modify('fromDate', asDate)
|
||||
.withGraphFetched(withRelations);
|
||||
}
|
||||
|
||||
overdueBills(asDate = moment().format('YYYY-MM-DD'), withRelations) {
|
||||
return this.model
|
||||
.query()
|
||||
.modify('dueBills')
|
||||
.modify('overdue', asDate)
|
||||
.modify('fromDate', asDate)
|
||||
.withGraphFetched(withRelations);
|
||||
}
|
||||
}
|
||||
261
packages/server/src/repositories/CachableRepository.ts
Normal file
261
packages/server/src/repositories/CachableRepository.ts
Normal file
@@ -0,0 +1,261 @@
|
||||
import hashObject from 'object-hash';
|
||||
import EntityRepository from './EntityRepository';
|
||||
|
||||
export default class CachableRepository extends EntityRepository {
|
||||
repositoryName: string;
|
||||
cache: any;
|
||||
i18n: any;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {Knex} knex
|
||||
* @param {Cache} cache
|
||||
*/
|
||||
constructor(knex, cache, i18n) {
|
||||
super(knex);
|
||||
|
||||
this.cache = cache;
|
||||
this.i18n = i18n;
|
||||
this.repositoryName = this.constructor.name;
|
||||
}
|
||||
|
||||
getByCache(key, callback) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cache key of the method name and arguments.
|
||||
* @param {string} method
|
||||
* @param {...any} args
|
||||
* @return {string}
|
||||
*/
|
||||
getCacheKey(method, ...args) {
|
||||
const hashArgs = hashObject({ ...args });
|
||||
const repositoryName = this.repositoryName;
|
||||
|
||||
return `${repositoryName}-${method}-${hashArgs}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all entries with specified relations.
|
||||
* @param withRelations
|
||||
*/
|
||||
all(withRelations?, trx?) {
|
||||
const cacheKey = this.getCacheKey('all', withRelations);
|
||||
|
||||
return this.getByCache(cacheKey, () => {
|
||||
return super.all(withRelations, trx);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.getByCache(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.getByCache(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.getByCache(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.getByCache(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.getByCache(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, trx?) {
|
||||
const result = await super.create(entity, trx);
|
||||
|
||||
// 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?, trx?) {
|
||||
const result = await super.update(entity, whereAttributes, trx);
|
||||
|
||||
// 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, trx?) {
|
||||
const result = await super.deleteBy(attributeValues, trx);
|
||||
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, trx?) {
|
||||
const result = super.deleteById(id, trx);
|
||||
|
||||
// Flushes the repository cache after insert operation.
|
||||
this.flushCache();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string|number[]} values -
|
||||
*/
|
||||
async deleteWhereIn(field: string, values: string | number[]) {
|
||||
const result = await super.deleteWhereIn(field, values);
|
||||
|
||||
// Flushes the repository cache after delete operation.
|
||||
this.flushCache();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string|number[]} values
|
||||
*/
|
||||
async deleteWhereIdIn(values: string | number[], trx?) {
|
||||
const result = await super.deleteWhereIdIn(values, trx);
|
||||
|
||||
// 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, trx?) {
|
||||
const result = await super.changeNumber(
|
||||
whereAttributes,
|
||||
field,
|
||||
amount,
|
||||
trx
|
||||
);
|
||||
|
||||
// Flushes the repository cache after update operation.
|
||||
this.flushCache();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush repository cache.
|
||||
*/
|
||||
flushCache(): void {
|
||||
this.cache.delStartWith(this.repositoryName);
|
||||
}
|
||||
}
|
||||
12
packages/server/src/repositories/ContactRepository.ts
Normal file
12
packages/server/src/repositories/ContactRepository.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
import { Contact } from 'models'
|
||||
|
||||
|
||||
export default class ContactRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return Contact.bindKnex(this.knex);
|
||||
}
|
||||
}
|
||||
46
packages/server/src/repositories/CustomerRepository.ts
Normal file
46
packages/server/src/repositories/CustomerRepository.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import TenantRepository from "./TenantRepository";
|
||||
import { Customer } from 'models';
|
||||
|
||||
export default class CustomerRepository extends TenantRepository {
|
||||
/**
|
||||
* Contact repository.
|
||||
*/
|
||||
constructor(knex, cache, i18n) {
|
||||
super(knex, cache, i18n);
|
||||
this.repositoryName = 'CustomerRepository';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return Customer.bindKnex(this.knex);
|
||||
}
|
||||
|
||||
changeBalance(vendorId: number, amount: number) {
|
||||
return super.changeNumber({ id: vendorId }, 'balance', amount);
|
||||
}
|
||||
|
||||
async changeDiffBalance(
|
||||
vendorId: number,
|
||||
amount: number,
|
||||
oldAmount: number,
|
||||
oldVendorId?: number,
|
||||
) {
|
||||
const diffAmount = amount - oldAmount;
|
||||
const asyncOpers = [];
|
||||
const _oldVendorId = oldVendorId || vendorId;
|
||||
|
||||
if (vendorId != _oldVendorId) {
|
||||
const oldCustomerOper = this.changeBalance(_oldVendorId, (oldAmount * -1));
|
||||
const customerOper = this.changeBalance(vendorId, amount);
|
||||
|
||||
asyncOpers.push(customerOper);
|
||||
asyncOpers.push(oldCustomerOper);
|
||||
} else {
|
||||
const balanceChangeOper = this.changeBalance(vendorId, diffAmount);
|
||||
asyncOpers.push(balanceChangeOper);
|
||||
}
|
||||
await Promise.all(asyncOpers);
|
||||
}
|
||||
}
|
||||
241
packages/server/src/repositories/EntityRepository.ts
Normal file
241
packages/server/src/repositories/EntityRepository.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
import { cloneDeep, forOwn, isString } from 'lodash';
|
||||
import ModelEntityNotFound from 'exceptions/ModelEntityNotFound';
|
||||
|
||||
function applyGraphFetched(withRelations, builder) {
|
||||
const relations = Array.isArray(withRelations)
|
||||
? withRelations
|
||||
: typeof withRelations === 'string'
|
||||
? withRelations.split(',').map((relation) => relation.trim())
|
||||
: [];
|
||||
|
||||
relations.forEach((relation) => {
|
||||
builder.withGraphFetched(relation);
|
||||
});
|
||||
}
|
||||
|
||||
export default class EntityRepository {
|
||||
idColumn: string;
|
||||
knex: any;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {Knex} knex
|
||||
*/
|
||||
constructor(knex) {
|
||||
this.knex = knex;
|
||||
this.idColumn = 'id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the repository model binded it to knex instance.
|
||||
*/
|
||||
get model() {
|
||||
throw new Error("The repository's model is not defined.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all entries with specified relations.
|
||||
*
|
||||
* @param withRelations
|
||||
*/
|
||||
all(withRelations?, trx?) {
|
||||
const builder = this.model.query(trx);
|
||||
applyGraphFetched(withRelations, builder);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 builder = this.model.query().where(attributeValues);
|
||||
|
||||
applyGraphFetched(withRelations, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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?) {
|
||||
const builder = this.model.query().whereNot(attributeValues);
|
||||
|
||||
applyGraphFetched(withRelations, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 commonBuilder = (builder) => {
|
||||
applyGraphFetched(withRelations, builder);
|
||||
};
|
||||
if (isString(searchParam)) {
|
||||
return this.model
|
||||
.query()
|
||||
.whereIn(searchParam, attributeValues)
|
||||
.onBuild(commonBuilder);
|
||||
} else {
|
||||
const builder = this.model.query(this.knex).onBuild(commonBuilder);
|
||||
|
||||
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, trx?) {
|
||||
// Keep the input parameter immutable
|
||||
const instanceDTO = cloneDeep(entity);
|
||||
|
||||
return this.model.query(trx).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?, trx?) {
|
||||
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(trx)
|
||||
.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, trx?) {
|
||||
return this.model.query(trx).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, trx?) {
|
||||
return this.deleteBy(
|
||||
{
|
||||
[this.idColumn]: id,
|
||||
},
|
||||
trx
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given entries in the array on the specific field.
|
||||
* @param {string} field -
|
||||
* @param {number|string} values -
|
||||
*/
|
||||
deleteWhereIn(field: string, values: string | number[], trx) {
|
||||
return this.model.query(trx).whereIn(field, values).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string|number[]} values
|
||||
*/
|
||||
deleteWhereIdIn(values: string | number[], trx?) {
|
||||
return this.deleteWhereIn(this.idColumn, values, trx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Arbitrary relation graphs can be upserted (insert + update + delete)
|
||||
* using the upsertGraph method.
|
||||
* @param graph
|
||||
* @param options
|
||||
*/
|
||||
upsertGraph(graph, options) {
|
||||
// Keep the input grpah immutable
|
||||
const graphCloned = cloneDeep(graph);
|
||||
return this.model.query().upsertGraph(graphCloned, options);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} whereAttributes
|
||||
* @param {string} field
|
||||
* @param amount
|
||||
*/
|
||||
changeNumber(whereAttributes, field: string, amount: number, trx) {
|
||||
const changeMethod = amount > 0 ? 'increment' : 'decrement';
|
||||
|
||||
return this.model
|
||||
.query(trx)
|
||||
.where(whereAttributes)
|
||||
[changeMethod](field, Math.abs(amount));
|
||||
}
|
||||
}
|
||||
11
packages/server/src/repositories/ExpenseEntryRepository.ts
Normal file
11
packages/server/src/repositories/ExpenseEntryRepository.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import TenantRepository from "./TenantRepository";
|
||||
import { ExpenseCategory } from 'models';
|
||||
|
||||
export default class ExpenseEntryRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return ExpenseCategory.bindKnex(this.knex);
|
||||
}
|
||||
}
|
||||
35
packages/server/src/repositories/ExpenseRepository.ts
Normal file
35
packages/server/src/repositories/ExpenseRepository.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import TenantRepository from "./TenantRepository";
|
||||
import moment from "moment";
|
||||
import { Expense } from 'models';
|
||||
|
||||
export default class ExpenseRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return Expense.bindKnex(this.knex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the given expense.
|
||||
* @param {number} expenseId
|
||||
*/
|
||||
publish(expenseId: number): Promise<void> {
|
||||
return super.update({
|
||||
id: expenseId,
|
||||
publishedAt: moment().toMySqlDateTime(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes the given expenses in bulk.
|
||||
* @param {number[]} expensesIds
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async whereIdInPublish(expensesIds: number): Promise<void> {
|
||||
await this.model.query().whereIn('id', expensesIds).patch({
|
||||
publishedAt: moment().toMySqlDateTime(),
|
||||
});
|
||||
this.flushCache();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
import { InventoryTransaction } from 'models';
|
||||
|
||||
export default class InventoryTransactionRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return InventoryTransaction.bindKnex(this.knex);
|
||||
}
|
||||
}
|
||||
12
packages/server/src/repositories/ItemRepository.ts
Normal file
12
packages/server/src/repositories/ItemRepository.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
import { Item } from "models";
|
||||
import TenantRepository from "./TenantRepository";
|
||||
|
||||
export default class ItemRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return Item.bindKnex(this.knex);
|
||||
}
|
||||
}
|
||||
11
packages/server/src/repositories/JournalRepository.ts
Normal file
11
packages/server/src/repositories/JournalRepository.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ManualJournal } from 'models';
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
|
||||
export default class JournalRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return ManualJournal.bindKnex(this.knex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { PaymentReceiveEntry } from 'models';
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
|
||||
export default class PaymentReceiveEntryRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return PaymentReceiveEntry.bindKnex(this.knex);
|
||||
}
|
||||
}
|
||||
11
packages/server/src/repositories/PaymentReceiveRepository.ts
Normal file
11
packages/server/src/repositories/PaymentReceiveRepository.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { PaymentReceive } from 'models';
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
|
||||
export default class PaymentReceiveRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return PaymentReceive.bindKnex(this.knex);
|
||||
}
|
||||
}
|
||||
30
packages/server/src/repositories/SaleInvoiceRepository.ts
Normal file
30
packages/server/src/repositories/SaleInvoiceRepository.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import moment from 'moment';
|
||||
import { SaleInvoice } from 'models';
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
|
||||
export default class SaleInvoiceRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return SaleInvoice.bindKnex(this.knex);
|
||||
}
|
||||
|
||||
dueInvoices(asDate = moment().format('YYYY-MM-DD'), withRelations) {
|
||||
return this.model
|
||||
.query()
|
||||
.modify('dueInvoices')
|
||||
.modify('notOverdue', asDate)
|
||||
.modify('fromDate', asDate)
|
||||
.withGraphFetched(withRelations);
|
||||
}
|
||||
|
||||
overdueInvoices(asDate = moment().format('YYYY-MM-DD'), withRelations) {
|
||||
return this.model
|
||||
.query()
|
||||
.modify('dueInvoices')
|
||||
.modify('overdue', asDate)
|
||||
.modify('fromDate', asDate)
|
||||
.withGraphFetched(withRelations);
|
||||
}
|
||||
}
|
||||
11
packages/server/src/repositories/SettingRepository.ts
Normal file
11
packages/server/src/repositories/SettingRepository.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
import Setting from 'models/Setting';
|
||||
|
||||
export default class SettingRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return Setting.bindKnex(this.knex);
|
||||
}
|
||||
}
|
||||
15
packages/server/src/repositories/TenantRepository.ts
Normal file
15
packages/server/src/repositories/TenantRepository.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Container } from 'typedi';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import CachableRepository from './CachableRepository';
|
||||
|
||||
export default class TenantRepository extends CachableRepository {
|
||||
repositoryName: string;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {number} tenantId
|
||||
*/
|
||||
constructor(knex, cache, i18n) {
|
||||
super(knex, cache, i18n);
|
||||
}
|
||||
}
|
||||
50
packages/server/src/repositories/VendorRepository.ts
Normal file
50
packages/server/src/repositories/VendorRepository.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Vendor } from "models";
|
||||
import TenantRepository from "./TenantRepository";
|
||||
|
||||
export default class VendorRepository extends TenantRepository {
|
||||
/**
|
||||
* Contact repository.
|
||||
*/
|
||||
constructor(knex, cache, i18n) {
|
||||
super(knex, cache, i18n);
|
||||
this.repositoryName = 'VendorRepository';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return Vendor.bindKnex(this.knex);
|
||||
}
|
||||
|
||||
unpaid() {
|
||||
|
||||
}
|
||||
|
||||
changeBalance(vendorId: number, amount: number) {
|
||||
return super.changeNumber({ id: vendorId }, 'balance', amount);
|
||||
}
|
||||
|
||||
async changeDiffBalance(
|
||||
vendorId: number,
|
||||
amount: number,
|
||||
oldAmount: number,
|
||||
oldVendorId?: number,
|
||||
) {
|
||||
const diffAmount = amount - oldAmount;
|
||||
const asyncOpers = [];
|
||||
const _oldVendorId = oldVendorId || vendorId;
|
||||
|
||||
if (vendorId != _oldVendorId) {
|
||||
const oldCustomerOper = this.changeBalance(_oldVendorId, (oldAmount * -1));
|
||||
const customerOper = this.changeBalance(vendorId, amount);
|
||||
|
||||
asyncOpers.push(customerOper);
|
||||
asyncOpers.push(oldCustomerOper);
|
||||
} else {
|
||||
const balanceChangeOper = this.changeBalance(vendorId, diffAmount);
|
||||
asyncOpers.push(balanceChangeOper);
|
||||
}
|
||||
await Promise.all(asyncOpers);
|
||||
}
|
||||
}
|
||||
18
packages/server/src/repositories/ViewRepository.ts
Normal file
18
packages/server/src/repositories/ViewRepository.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { View } from 'models';
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
|
||||
export default class ViewRepository extends TenantRepository {
|
||||
/**
|
||||
* Gets the repository's model.
|
||||
*/
|
||||
get model() {
|
||||
return View.bindKnex(this.knex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all views of the given resource id.
|
||||
*/
|
||||
allByResource(resourceModel: string, withRelations?) {
|
||||
return super.find({ resource_model: resourceModel }, withRelations);
|
||||
}
|
||||
}
|
||||
6
packages/server/src/repositories/ViewRoleRepository.ts
Normal file
6
packages/server/src/repositories/ViewRoleRepository.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { omit } from 'lodash';
|
||||
import TenantRepository from '@/repositories/TenantRepository';
|
||||
|
||||
export default class ViewRoleRepository extends TenantRepository {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user