mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 22:30:31 +00:00
refactor: dynamic list to nestjs
This commit is contained in:
@@ -105,7 +105,8 @@
|
||||
"ts-loader": "^9.4.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.1.3"
|
||||
"typescript": "^5.1.3",
|
||||
"mustache": "^3.0.3"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
|
||||
@@ -39,6 +39,8 @@ export interface IModelMetaFieldCommon {
|
||||
order?: number;
|
||||
unique?: number;
|
||||
dataTransferObjectKey?: string;
|
||||
filterCustomQuery?: Function;
|
||||
sortCustomQuery?: Function;
|
||||
}
|
||||
|
||||
export interface IModelMetaFieldText {
|
||||
|
||||
@@ -7,6 +7,10 @@ const OperationType = {
|
||||
};
|
||||
|
||||
export class Lexer {
|
||||
public currentIndex: number;
|
||||
public input: string;
|
||||
public tokenList: string[];
|
||||
|
||||
// operation table
|
||||
static get optable() {
|
||||
return {
|
||||
@@ -26,7 +26,10 @@ export const OPERATION = {
|
||||
// grouped?: boolean;
|
||||
// };
|
||||
|
||||
export default class Parser {
|
||||
export class Parser {
|
||||
public index: number;
|
||||
public blockLevel: number;
|
||||
public token: string[];
|
||||
|
||||
constructor(token) {
|
||||
this.index = -1;
|
||||
@@ -1,6 +1,9 @@
|
||||
import { OPERATION } from './Parser';
|
||||
|
||||
export default class QueryParser {
|
||||
export class QueryParser {
|
||||
public tree: any;
|
||||
public queries: any;
|
||||
public query: any;
|
||||
|
||||
constructor(tree, queries) {
|
||||
this.tree = tree;
|
||||
@@ -1,30 +1,30 @@
|
||||
export const OtherExpensesAccount = {
|
||||
name: 'Other Expenses',
|
||||
slug: 'other-expenses',
|
||||
account_type: 'other-expense',
|
||||
accountType: 'other-expense',
|
||||
code: '40011',
|
||||
description: '',
|
||||
active: 1,
|
||||
active: true,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
predefined: true,
|
||||
};
|
||||
|
||||
export const TaxPayableAccount = {
|
||||
name: 'Tax Payable',
|
||||
slug: 'tax-payable',
|
||||
account_type: 'tax-payable',
|
||||
accountType: 'tax-payable',
|
||||
code: '20006',
|
||||
description: '',
|
||||
active: 1,
|
||||
active: true,
|
||||
index: 1,
|
||||
predefined: 1,
|
||||
predefined: true,
|
||||
};
|
||||
|
||||
export const UnearnedRevenueAccount = {
|
||||
name: 'Unearned Revenue',
|
||||
slug: 'unearned-revenue',
|
||||
account_type: 'other-current-liability',
|
||||
parent_account_id: null,
|
||||
accountType: 'other-current-liability',
|
||||
parentAccountId: null,
|
||||
code: '50005',
|
||||
active: true,
|
||||
index: 1,
|
||||
@@ -34,8 +34,8 @@ export const UnearnedRevenueAccount = {
|
||||
export const PrepardExpenses = {
|
||||
name: 'Prepaid Expenses',
|
||||
slug: 'prepaid-expenses',
|
||||
account_type: 'other-current-asset',
|
||||
parent_account_id: null,
|
||||
accountType: 'other-current-asset',
|
||||
parentAccountId: null,
|
||||
code: '100010',
|
||||
active: true,
|
||||
index: 1,
|
||||
@@ -45,8 +45,8 @@ export const PrepardExpenses = {
|
||||
export const StripeClearingAccount = {
|
||||
name: 'Stripe Clearing',
|
||||
slug: 'stripe-clearing',
|
||||
account_type: 'other-current-asset',
|
||||
parent_account_id: null,
|
||||
accountType: 'other-current-asset',
|
||||
parentAccountId: null,
|
||||
code: '100020',
|
||||
active: true,
|
||||
index: 1,
|
||||
@@ -56,7 +56,7 @@ export const StripeClearingAccount = {
|
||||
export const DiscountExpenseAccount = {
|
||||
name: 'Discount',
|
||||
slug: 'discount',
|
||||
account_type: 'other-income',
|
||||
accountType: 'other-income',
|
||||
code: '40008',
|
||||
active: true,
|
||||
index: 1,
|
||||
@@ -66,7 +66,7 @@ export const DiscountExpenseAccount = {
|
||||
export const PurchaseDiscountAccount = {
|
||||
name: 'Purchase Discount',
|
||||
slug: 'purchase-discount',
|
||||
account_type: 'other-expense',
|
||||
accountType: 'other-expense',
|
||||
code: '40009',
|
||||
active: true,
|
||||
index: 1,
|
||||
@@ -76,7 +76,7 @@ export const PurchaseDiscountAccount = {
|
||||
export const OtherChargesAccount = {
|
||||
name: 'Other Charges',
|
||||
slug: 'other-charges',
|
||||
account_type: 'other-income',
|
||||
accountType: 'other-income',
|
||||
code: '40010',
|
||||
active: true,
|
||||
index: 1,
|
||||
@@ -87,7 +87,7 @@ export const SeedAccounts = [
|
||||
{
|
||||
name: 'Bank Account',
|
||||
slug: 'bank-account',
|
||||
account_type: 'bank',
|
||||
accountType: 'bank',
|
||||
code: '10001',
|
||||
description: '',
|
||||
active: 1,
|
||||
|
||||
@@ -41,7 +41,7 @@ export class DeleteAccount {
|
||||
await this.accountModel
|
||||
.query(trx)
|
||||
.whereIn('parent_account_id', accountsIds)
|
||||
.patch({ parent_account_id: null });
|
||||
.patch({ parentAccountId: null });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,10 +13,10 @@ export class GetAccountsService {
|
||||
constructor(
|
||||
private readonly dynamicListService: DynamicListService,
|
||||
private readonly transformerService: TransformerInjectable,
|
||||
private readonly accountRepository: AccountRepository,
|
||||
|
||||
@Inject(Account.name)
|
||||
private readonly accountModel: typeof Account,
|
||||
private readonly accountRepository: AccountRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,32 +13,29 @@ import { TenantModel } from '@/modules/System/models/TenantModel';
|
||||
import { AccountTypesUtils } from '@/libs/accounts-utils/AccountTypesUtils';
|
||||
import { Model } from 'objection';
|
||||
import { PlaidItem } from '@/modules/BankingPlaid/models/PlaidItem';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
// import AccountSettings from './Account.Settings';
|
||||
// import { DEFAULT_VIEWS } from '@/modules/Accounts/constants';
|
||||
// import { buildFilterQuery, buildSortColumnQuery } from '@/lib/ViewRolesBuilder';
|
||||
// import { flatToNestedArray } from 'utils';
|
||||
|
||||
// @ts-expect-error
|
||||
// export class Account extends mixin(TenantModel, [
|
||||
// ModelSettings,
|
||||
// CustomViewBaseModel,
|
||||
// SearchableModel,
|
||||
// ]) {
|
||||
|
||||
export class Account extends TenantModel {
|
||||
export class Account extends TenantBaseModel {
|
||||
public name!: string;
|
||||
public slug!: string;
|
||||
public code!: string;
|
||||
public index!: number;
|
||||
public accountType!: string;
|
||||
public parentAccountId!: number | null;
|
||||
public predefined!: boolean;
|
||||
public currencyCode!: string;
|
||||
public active!: boolean;
|
||||
public bankBalance!: number;
|
||||
public lastFeedsUpdatedAt!: string | null;
|
||||
public lastFeedsUpdatedAt!: string | Date | null;
|
||||
public amount!: number;
|
||||
public plaidItemId!: number;
|
||||
|
||||
public plaidAccountId!: string | null;
|
||||
public isFeedsActive!: boolean;
|
||||
public plaidItem!: PlaidItem;
|
||||
|
||||
/**
|
||||
@@ -73,11 +70,11 @@ export class Account extends TenantModel {
|
||||
/**
|
||||
* Account normal.
|
||||
*/
|
||||
get accountNormal() {
|
||||
get accountNormal(): string {
|
||||
return AccountTypesUtils.getType(this.accountType, 'normal');
|
||||
}
|
||||
|
||||
get accountNormalFormatted() {
|
||||
get accountNormalFormatted(): string {
|
||||
const paris = {
|
||||
credit: 'Credit',
|
||||
debit: 'Debit',
|
||||
@@ -88,35 +85,35 @@ export class Account extends TenantModel {
|
||||
/**
|
||||
* Retrieve account type label.
|
||||
*/
|
||||
get accountTypeLabel() {
|
||||
get accountTypeLabel(): string {
|
||||
return AccountTypesUtils.getType(this.accountType, 'label');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve account parent type.
|
||||
*/
|
||||
get accountParentType() {
|
||||
get accountParentType(): string {
|
||||
return AccountTypesUtils.getType(this.accountType, 'parentType');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve account root type.
|
||||
*/
|
||||
get accountRootType() {
|
||||
get accountRootType(): string {
|
||||
return AccountTypesUtils.getType(this.accountType, 'rootType');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether the account is balance sheet account.
|
||||
*/
|
||||
get isBalanceSheetAccount() {
|
||||
get isBalanceSheetAccount(): boolean {
|
||||
return this.isBalanceSheet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether the account is profit/loss sheet account.
|
||||
*/
|
||||
get isPLSheet() {
|
||||
get isPLSheet(): boolean {
|
||||
return this.isProfitLossSheet();
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -147,7 +147,7 @@ export class AccountRepository extends TenantRepository {
|
||||
}),
|
||||
accountType: 'accounts-receivable',
|
||||
currencyCode,
|
||||
active: 1,
|
||||
active: true,
|
||||
...extraAttrs,
|
||||
});
|
||||
}
|
||||
@@ -199,7 +199,7 @@ export class AccountRepository extends TenantRepository {
|
||||
}),
|
||||
accountType: 'accounts-payable',
|
||||
currencyCode,
|
||||
active: 1,
|
||||
active: true,
|
||||
...extraAttrs,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ export class PlaidSyncDb {
|
||||
|
||||
/**
|
||||
* Syncs the Plaid bank account.
|
||||
* @param {number} tenantId
|
||||
* @param {IAccountCreateDTO} createBankAccountDTO
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns {Promise<void>}
|
||||
@@ -70,7 +69,6 @@ export class PlaidSyncDb {
|
||||
|
||||
/**
|
||||
* Syncs the plaid accounts to the system accounts.
|
||||
* @param {number} tenantId Tenant ID.
|
||||
* @param {PlaidAccount[]} plaidAccounts
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@@ -94,7 +92,6 @@ export class PlaidSyncDb {
|
||||
|
||||
/**
|
||||
* Synsc the Plaid transactions to the system GL entries.
|
||||
* @param {number} tenantId - Tenant ID.
|
||||
* @param {number} plaidAccountId - Plaid account ID.
|
||||
* @param {PlaidTransaction[]} plaidTranasctions - Plaid transactions
|
||||
* @return {Promise<void>}
|
||||
@@ -136,7 +133,6 @@ export class PlaidSyncDb {
|
||||
|
||||
/**
|
||||
* Syncs the accounts transactions in paraller under controlled concurrency.
|
||||
* @param {number} tenantId
|
||||
* @param {PlaidTransaction[]} plaidTransactions
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
@@ -188,7 +184,6 @@ export class PlaidSyncDb {
|
||||
|
||||
/**
|
||||
* Syncs the Plaid item last transaction cursor.
|
||||
* @param {number} tenantId - Tenant ID.
|
||||
* @param {string} itemId - Plaid item ID.
|
||||
* @param {string} lastCursor - Last transaction cursor.
|
||||
* @return {Promise<void>}
|
||||
@@ -206,8 +201,7 @@ export class PlaidSyncDb {
|
||||
|
||||
/**
|
||||
* Updates the last feeds updated at of the given Plaid accounts ids.
|
||||
* @param {number} tenantId
|
||||
* @param {string[]} plaidAccountIds
|
||||
* @param {string[]} plaidAccountIds - Plaid accounts ids.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async updateLastFeedsUpdatedAt(
|
||||
@@ -224,9 +218,8 @@ export class PlaidSyncDb {
|
||||
|
||||
/**
|
||||
* Updates the accounts feed active status of the given Plaid accounts ids.
|
||||
* @param {number} tenantId
|
||||
* @param {number[]} plaidAccountIds
|
||||
* @param {boolean} isFeedsActive
|
||||
* @param {number[]} plaidAccountIds - Plaid accounts ids.
|
||||
* @param {boolean} isFeedsActive - Feeds active status.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async updateAccountsFeedsActive(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable global-require */
|
||||
import { mixin, Model } from 'objection';
|
||||
import { Model } from 'objection';
|
||||
import { castArray } from 'lodash';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { AccountTypesUtils } from '@/libs/accounts-utils/AccountTypesUtils';
|
||||
import { PlaidItem } from '@/modules/BankingPlaid/models/PlaidItem';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
export class BankAccount extends BaseModel {
|
||||
export class BankAccount extends TenantBaseModel {
|
||||
public name!: string;
|
||||
public slug!: string;
|
||||
public code!: string;
|
||||
|
||||
@@ -48,7 +48,9 @@ export class CashflowAccountTransformer extends Transformer {
|
||||
* @returns {string}
|
||||
*/
|
||||
protected lastFeedsUpdatedAtFormatted(account: Account): string {
|
||||
return this.formatDate(account.lastFeedsUpdatedAt);
|
||||
return account.lastFeedsUpdatedAt
|
||||
? this.formatDate(account.lastFeedsUpdatedAt)
|
||||
: '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,6 +59,8 @@ export class CashflowAccountTransformer extends Transformer {
|
||||
* @returns {string}
|
||||
*/
|
||||
protected lastFeedsUpdatedFromNow(account: Account): string {
|
||||
return this.formatDateFromNow(account.lastFeedsUpdatedAt);
|
||||
return account.lastFeedsUpdatedAt
|
||||
? this.formatDateFromNow(account.lastFeedsUpdatedAt)
|
||||
: '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export class GetBankAccountsService {
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
BankAccount,
|
||||
this.bankAccountModel,
|
||||
filter,
|
||||
);
|
||||
// Retrieve accounts model based on the given query.
|
||||
|
||||
@@ -164,7 +164,7 @@ export class Bill extends BaseModel {
|
||||
* Adjustment amount in local currency.
|
||||
* @returns {number | null}
|
||||
*/
|
||||
get adjustmentLocal() {
|
||||
get adjustmentLocal(): number | null {
|
||||
return this.adjustment ? this.adjustment * this.exchangeRate : null;
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ export class Bill extends BaseModel {
|
||||
* Invoice total. (Tax included)
|
||||
* @returns {number}
|
||||
*/
|
||||
get total() {
|
||||
get total(): number {
|
||||
const adjustmentAmount = defaultTo(this.adjustment, 0);
|
||||
|
||||
return R.compose(
|
||||
@@ -186,7 +186,7 @@ export class Bill extends BaseModel {
|
||||
* Invoice total in local currency. (Tax included)
|
||||
* @returns {number}
|
||||
*/
|
||||
get totalLocal() {
|
||||
get totalLocal(): number {
|
||||
return this.total * this.exchangeRate;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export class GetBillsService {
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
Bill,
|
||||
this.billModel,
|
||||
filter,
|
||||
);
|
||||
const { results, pagination } = await this.billModel
|
||||
|
||||
@@ -15,8 +15,10 @@ import {
|
||||
ICreditNotesQueryDTO,
|
||||
} from './types/CreditNotes.types';
|
||||
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@Controller('credit-notes')
|
||||
@ApiTags('credit-notes')
|
||||
@PublicRoute()
|
||||
export class CreditNotesController {
|
||||
/**
|
||||
|
||||
@@ -2,18 +2,12 @@ import { DiscountType } from '@/common/types/Discount';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { Branch } from '@/modules/Branches/models/Branch.model';
|
||||
import { Customer } from '@/modules/Customers/models/Customer';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
|
||||
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
|
||||
import { mixin, Model, raw } from 'objection';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import ModelSetting from './ModelSetting';
|
||||
// import CustomViewBaseModel from './CustomViewBaseModel';
|
||||
// import { DEFAULT_VIEWS } from '@/services/CreditNotes/constants';
|
||||
// import ModelSearchable from './ModelSearchable';
|
||||
// import CreditNoteMeta from './CreditNote.Meta';
|
||||
// import { DiscountType } from '@/interfaces';
|
||||
|
||||
export class CreditNote extends BaseModel {
|
||||
export class CreditNote extends TenantBaseModel {
|
||||
public amount: number;
|
||||
public exchangeRate: number;
|
||||
public openedAt: Date;
|
||||
|
||||
@@ -39,7 +39,7 @@ export class GetCreditNotesService {
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
CreditNote,
|
||||
this.creditNoteModel,
|
||||
filter,
|
||||
);
|
||||
const { results, pagination } = await this.creditNoteModel
|
||||
@@ -47,8 +47,9 @@ export class GetCreditNotesService {
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('entries.item');
|
||||
builder.withGraphFetched('customer');
|
||||
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
creditNotesQuery?.filterQuery && creditNotesQuery?.filterQuery(builder);
|
||||
creditNotesQuery?.filterQuery?.(builder as any);
|
||||
})
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types';
|
||||
import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types';
|
||||
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
|
||||
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
|
||||
import { ILedgerEntry } from '@/modules/Ledger/types/Ledger.types';
|
||||
|
||||
export interface ICreditNoteEntryNewDTO extends IItemEntryDTO {}
|
||||
|
||||
@@ -97,6 +98,7 @@ export interface ICreditNotesQueryDTO extends IDynamicListFilter {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
searchKeyword?: string;
|
||||
filterQuery?: (builder: Knex.QueryBuilder) => void;
|
||||
}
|
||||
|
||||
export interface ICreditNoteRefundDTO {
|
||||
@@ -109,22 +111,22 @@ export interface ICreditNoteRefundDTO {
|
||||
branchId?: number;
|
||||
}
|
||||
|
||||
// export type ICreditNoteGLCommonEntry = Pick<
|
||||
// ILedgerEntry,
|
||||
// | 'date'
|
||||
// | 'userId'
|
||||
// | 'currencyCode'
|
||||
// | 'exchangeRate'
|
||||
// | 'transactionType'
|
||||
// | 'transactionId'
|
||||
// | 'transactionNumber'
|
||||
// | 'referenceNumber'
|
||||
// | 'createdAt'
|
||||
// | 'indexGroup'
|
||||
// | 'credit'
|
||||
// | 'debit'
|
||||
// | 'branchId'
|
||||
// >;
|
||||
export type ICreditNoteGLCommonEntry = Pick<
|
||||
ILedgerEntry,
|
||||
| 'date'
|
||||
| 'userId'
|
||||
| 'currencyCode'
|
||||
| 'exchangeRate'
|
||||
| 'transactionType'
|
||||
| 'transactionId'
|
||||
| 'transactionNumber'
|
||||
| 'referenceNumber'
|
||||
| 'createdAt'
|
||||
| 'indexGroup'
|
||||
| 'credit'
|
||||
| 'debit'
|
||||
| 'branchId'
|
||||
>;
|
||||
|
||||
export interface GetCreditNotesResponse {
|
||||
creditNotes: CreditNote[];
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
import { BaseModel } from "@/models/Model";
|
||||
;
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { IView } from '../Views/Views.types';
|
||||
|
||||
type GConstructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
export const CustomViewBaseModelMixin = <T extends GConstructor<BaseModel>>(Model: T) =>
|
||||
export interface ICustomViewBaseModel {
|
||||
defaultViews: IView[];
|
||||
getDefaultViewBySlug(viewSlug: string): IView | null;
|
||||
getDefaultViews(): IView[];
|
||||
}
|
||||
|
||||
export const CustomViewBaseModelMixin = <T extends GConstructor<BaseModel>>(
|
||||
Model: T,
|
||||
) =>
|
||||
class CustomViewBaseModel extends Model {
|
||||
/**
|
||||
* Retrieve the default custom views, roles and columns.
|
||||
@@ -18,6 +27,10 @@ export const CustomViewBaseModelMixin = <T extends GConstructor<BaseModel>>(Mode
|
||||
return this.defaultViews.find((view) => view.slug === viewSlug) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the default views.
|
||||
* @returns {IView[]}
|
||||
*/
|
||||
static getDefaultViews() {
|
||||
return this.defaultViews;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Model, mixin } from 'objection';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import PaginationQueryBuilder from './Pagination';
|
||||
// import ModelSetting from './ModelSetting';
|
||||
@@ -20,7 +19,7 @@ import { BaseModel } from '@/models/Model';
|
||||
// }
|
||||
// }
|
||||
|
||||
export class Customer extends BaseModel{
|
||||
export class Customer extends TenantBaseModel{
|
||||
contactService: string;
|
||||
contactType: string;
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ export class GetCustomers {
|
||||
const filter = this.parseCustomersListFilterDTO(filterDTO);
|
||||
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
Customer,
|
||||
this.customerModel,
|
||||
filter,
|
||||
);
|
||||
const { results, pagination } = await this.customerModel
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { forEach } from 'lodash';
|
||||
import { DynamicFilterAbstractor } from './DynamicFilterAbstractor';
|
||||
import { IDynamicFilter, IFilterRole } from './DynamicFilter.types';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { IFilterRole } from './DynamicFilter.types';
|
||||
import { DynamicFilterRoleAbstractor } from './DynamicFilterRoleAbstractor';
|
||||
import { MetableModel } from '../types/DynamicList.types';
|
||||
|
||||
export class DynamicFilter<R extends {}> extends DynamicFilterAbstractor {
|
||||
public model: MetableModel;
|
||||
public dynamicFilters: DynamicFilterRoleAbstractor[];
|
||||
|
||||
export class DynamicFilter extends DynamicFilterAbstractor {
|
||||
/**
|
||||
* Constructor.
|
||||
* @param {String} tableName -
|
||||
* @param {MetableModel} model - Metable model.
|
||||
*/
|
||||
constructor(model: typeof BaseModel) {
|
||||
constructor(model: MetableModel) {
|
||||
super();
|
||||
|
||||
this.model = model;
|
||||
@@ -29,7 +32,7 @@ export class DynamicFilter extends DynamicFilterAbstractor {
|
||||
|
||||
/**
|
||||
* Retrieve dynamic filter build queries.
|
||||
* @returns
|
||||
* @returns {Function[]}
|
||||
*/
|
||||
private dynamicFiltersBuildQuery = () => {
|
||||
return this.dynamicFilters.map((filter) => {
|
||||
@@ -72,16 +75,16 @@ export class DynamicFilter extends DynamicFilterAbstractor {
|
||||
/**
|
||||
* Retrieve response metadata from all filters adapters.
|
||||
*/
|
||||
public getResponseMeta = () => {
|
||||
public getResponseMeta = (): R => {
|
||||
const responseMeta = {};
|
||||
|
||||
this.dynamicFilters.forEach((filter) => {
|
||||
const { responseMeta: filterMeta } = filter;
|
||||
const filterMeta = filter.getResponseMeta();
|
||||
|
||||
forEach(filterMeta, (value, key) => {
|
||||
responseMeta[key] = value;
|
||||
});
|
||||
});
|
||||
return responseMeta;
|
||||
return responseMeta as R;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface IDynamicListFilter {
|
||||
customViewId?: number;
|
||||
filterRoles?: IFilterRole[];
|
||||
columnSortBy: ISortOrder;
|
||||
sortOrder: string;
|
||||
sortOrder: ISortOrder;
|
||||
stringifiedFilterRoles?: string;
|
||||
searchKeyword?: string;
|
||||
viewSlug?: string;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { IDynamicFilter } from './DynamicFilter.types';
|
||||
import { MetableModel } from '../types/DynamicList.types';
|
||||
|
||||
export class DynamicFilterAbstractor {
|
||||
public model: typeof BaseModel;
|
||||
public model: MetableModel;
|
||||
public dynamicFilters: IDynamicFilter[];
|
||||
|
||||
/**
|
||||
* Extract relation table name from relation.
|
||||
* @param {String} column -
|
||||
* @param {String} column - Column name
|
||||
* @return {String} - join relation table.
|
||||
*/
|
||||
protected getTableFromRelationColumn = (column: string) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import { OPERATION } from '@/libs/logic-evaluation/Parser';
|
||||
|
||||
export default class QueryParser {
|
||||
export class DynamicFilterQueryParser {
|
||||
constructor(tree, queries) {
|
||||
this.tree = tree;
|
||||
this.queries = queries;
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
import moment from 'moment';
|
||||
import * as R from 'ramda';
|
||||
import { IFilterRole, IDynamicFilter } from './DynamicFilter.types';
|
||||
import Parser from '@/libs/logic-evaluation/Parser';
|
||||
import { Parser } from '@/libs/logic-evaluation/Parser';
|
||||
import { Lexer } from '@/libs/logic-evaluation/Lexer';
|
||||
import DynamicFilterQueryParser from './DynamicFilterQueryParser';
|
||||
import { DynamicFilterQueryParser } from './DynamicFilterQueryParser';
|
||||
import { COMPARATOR_TYPE, FIELD_TYPE } from './constants';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { IMetadataModel } from '../models/MetadataModel';
|
||||
|
||||
type MetadataModel = typeof BaseModel & IMetadataModel;
|
||||
import { MetableModel } from '../types/DynamicList.types';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export abstract class DynamicFilterRoleAbstractor implements IDynamicFilter {
|
||||
protected filterRoles: IFilterRole[] = [];
|
||||
protected tableName: string;
|
||||
protected model: MetadataModel;
|
||||
protected responseMeta: { [key: string]: any } = {};
|
||||
public filterRoles: IFilterRole[] = [];
|
||||
public tableName: string;
|
||||
public model: MetableModel;
|
||||
public responseMeta: { [key: string]: any } = {};
|
||||
public relationFields = [];
|
||||
|
||||
/**
|
||||
* Sets model the dynamic filter service.
|
||||
* @param {IModel} model
|
||||
*/
|
||||
public setModel(model: MetadataModel) {
|
||||
public setModel(model: MetableModel) {
|
||||
this.model = model;
|
||||
this.tableName = model.tableName;
|
||||
}
|
||||
@@ -118,7 +117,7 @@ export abstract class DynamicFilterRoleAbstractor implements IDynamicFilter {
|
||||
* @param {IModel} model -
|
||||
* @param {} -
|
||||
*/
|
||||
private getFieldComparatorColumn = (field) => {
|
||||
protected getFieldComparatorColumn = (field) => {
|
||||
return field.fieldType === FIELD_TYPE.RELATION
|
||||
? this.getFieldComparatorRelationColumn(field)
|
||||
: `${this.tableName}.${field.column}`;
|
||||
@@ -129,7 +128,7 @@ export abstract class DynamicFilterRoleAbstractor implements IDynamicFilter {
|
||||
* @param {IModel} model -
|
||||
* @param {Object} role -
|
||||
*/
|
||||
protected buildRoleQuery = (model: MetadataModel, role: IFilterRole) => {
|
||||
protected buildRoleQuery = (model: MetableModel, role: IFilterRole) => {
|
||||
const field = model.getField(role.fieldKey);
|
||||
const comparatorColumn = this.getFieldComparatorColumn(field);
|
||||
|
||||
@@ -384,10 +383,17 @@ export abstract class DynamicFilterRoleAbstractor implements IDynamicFilter {
|
||||
*/
|
||||
onInitialize() {}
|
||||
|
||||
buildQuery(): void {
|
||||
/**
|
||||
* Builds the query.
|
||||
*/
|
||||
buildQuery(): (builder: Knex.QueryBuilder) => void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the response meta.
|
||||
*/
|
||||
getResponseMeta() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { IFilterRole } from './DynamicFilter.types';
|
||||
import { DynamicFilterFilterRoles } from './DynamicFilterFilterRoles';
|
||||
|
||||
export interface IDynamicFilterSearchResponseMeta {
|
||||
searchKeyword: string;
|
||||
}
|
||||
|
||||
export class DynamicFilterSearch extends DynamicFilterFilterRoles {
|
||||
private searchKeyword: string;
|
||||
|
||||
@@ -23,7 +27,7 @@ export class DynamicFilterSearch extends DynamicFilterFilterRoles {
|
||||
|
||||
/**
|
||||
* Retrieve the filter roles from model search roles.
|
||||
* @param {string} searchKeyword
|
||||
* @param {string} searchKeyword
|
||||
* @returns {IFilterRole[]}
|
||||
*/
|
||||
private getModelSearchFilterRoles(searchKeyword: string): IFilterRole[] {
|
||||
@@ -37,11 +41,21 @@ export class DynamicFilterSearch extends DynamicFilterFilterRoles {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets the response meta.
|
||||
*/
|
||||
setResponseMeta() {
|
||||
this.responseMeta = {
|
||||
searchKeyword: this.searchKeyword,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the response meta.
|
||||
* @returns {IDynamicFilterSearchResponseMeta}
|
||||
*/
|
||||
public getResponseMeta(): IDynamicFilterSearchResponseMeta {
|
||||
return {
|
||||
searchKeyword: this.searchKeyword,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { FIELD_TYPE } from './constants';
|
||||
import { DynamicFilterAbstractor } from './DynamicFilterAbstractor';
|
||||
import { DynamicFilterRoleAbstractor } from './DynamicFilterRoleAbstractor';
|
||||
|
||||
interface ISortRole {
|
||||
@@ -8,7 +7,10 @@ interface ISortRole {
|
||||
}
|
||||
|
||||
export class DynamicFilterSortBy extends DynamicFilterRoleAbstractor {
|
||||
private sortRole: ISortRole = {};
|
||||
private sortRole: ISortRole = {
|
||||
fieldKey: '',
|
||||
order: '',
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
@@ -22,7 +24,6 @@ export class DynamicFilterSortBy extends DynamicFilterRoleAbstractor {
|
||||
fieldKey: sortByFieldKey,
|
||||
order: sortDirection,
|
||||
};
|
||||
this.setResponseMeta();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,7 +55,7 @@ export class DynamicFilterSortBy extends DynamicFilterRoleAbstractor {
|
||||
* @param {IModel} field
|
||||
* @returns {string}
|
||||
*/
|
||||
private getFieldComparatorColumn = (field): string => {
|
||||
getFieldComparatorColumn = (field) => {
|
||||
return field.fieldType === FIELD_TYPE.RELATION
|
||||
? this.getFieldComparatorRelationColumn(field)
|
||||
: `${this.tableName}.${field.column}`;
|
||||
@@ -84,10 +85,10 @@ export class DynamicFilterSortBy extends DynamicFilterRoleAbstractor {
|
||||
/**
|
||||
* Sets response meta.
|
||||
*/
|
||||
public setResponseMeta() {
|
||||
this.responseMeta = {
|
||||
sortOrder: this.sortRole.fieldKey,
|
||||
sortBy: this.sortRole.order,
|
||||
public getResponseMeta(): ISortRole {
|
||||
return {
|
||||
fieldKey: this.sortRole.fieldKey,
|
||||
order: this.sortRole.order,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { omit } from 'lodash';
|
||||
import { DynamicFilterRoleAbstractor } from './DynamicFilterRoleAbstractor';
|
||||
import { IView } from '@/modules/Views/Views.types';
|
||||
|
||||
export class DynamicFilterViews extends DynamicFilterRoleAbstractor {
|
||||
private viewSlug: string;
|
||||
|
||||
@@ -6,7 +6,8 @@ import { DynamicListCustomView } from './DynamicListCustomView.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DynamicListFilterRoles } from './DynamicListFilterRoles.service';
|
||||
import { DynamicFilter } from './DynamicFilter';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { MetableModel } from './types/DynamicList.types';
|
||||
import { IFilterMeta } from '@/interfaces/Model';
|
||||
|
||||
@Injectable()
|
||||
export class DynamicListService {
|
||||
@@ -19,10 +20,13 @@ export class DynamicListService {
|
||||
|
||||
/**
|
||||
* Parses filter DTO.
|
||||
* @param {IMode} model -
|
||||
* @param {} filterDTO -
|
||||
* @param {MetableModel} model - Metable model.
|
||||
* @param {IDynamicListFilter} filterDTO - Dynamic list filter DTO.
|
||||
*/
|
||||
private parseFilterObject = (model, filterDTO) => {
|
||||
private parseFilterObject = (
|
||||
model: MetableModel,
|
||||
filterDTO: IDynamicListFilter,
|
||||
) => {
|
||||
return {
|
||||
// Merges the default properties with filter object.
|
||||
...(model.defaultSort
|
||||
@@ -37,15 +41,14 @@ export class DynamicListService {
|
||||
|
||||
/**
|
||||
* Dynamic listing.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {IModel} model - Model.
|
||||
* @param {IModel} model - Metable model.
|
||||
* @param {IDynamicListFilter} filter - Dynamic filter DTO.
|
||||
*/
|
||||
public dynamicList = async (
|
||||
model: typeof BaseModel,
|
||||
model: MetableModel,
|
||||
filter: IDynamicListFilter,
|
||||
) => {
|
||||
const dynamicFilter = new DynamicFilter(model);
|
||||
const dynamicFilter = new DynamicFilter<IFilterMeta>(model);
|
||||
|
||||
// Parses the filter object.
|
||||
const parsedFilter = this.parseFilterObject(model, filter);
|
||||
@@ -99,5 +102,5 @@ export class DynamicListService {
|
||||
? castArray(JSON.parse(filterRoles.stringifiedFilterRoles))
|
||||
: [],
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,22 @@ import { Injectable } from '@nestjs/common';
|
||||
import { ERRORS } from './constants';
|
||||
import { DynamicFilterViews } from './DynamicFilter';
|
||||
import { ServiceError } from '../Items/ServiceError';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { DynamicListServiceAbstract } from './DynamicListServiceAbstract';
|
||||
import { IView } from '../Views/Views.types';
|
||||
import { MetableModel } from './types/DynamicList.types';
|
||||
|
||||
@Injectable()
|
||||
export class DynamicListCustomView extends DynamicListServiceAbstract {
|
||||
/**
|
||||
* Retreive custom view or throws error not found.
|
||||
* @param {number} tenantId
|
||||
* @param {number} viewId
|
||||
* @param {string} viewSlug - View slug.
|
||||
* @param {MetableModel} model - Metable model.
|
||||
* @return {Promise<IView>}
|
||||
*/
|
||||
private getCustomViewOrThrowError = async (
|
||||
private async getCustomViewOrThrowError(
|
||||
viewSlug: string,
|
||||
model: BaseModel,
|
||||
) => {
|
||||
model: MetableModel,
|
||||
): Promise<IView> {
|
||||
// Finds the default view by the given view slug.
|
||||
const defaultView = model.getDefaultViewBySlug(viewSlug);
|
||||
|
||||
@@ -24,12 +25,12 @@ export class DynamicListCustomView extends DynamicListServiceAbstract {
|
||||
throw new ServiceError(ERRORS.VIEW_NOT_FOUND);
|
||||
}
|
||||
return defaultView;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamic list custom view.
|
||||
* @param {IModel} model
|
||||
* @param {number} customViewId
|
||||
* @param {DynamicFilter} dynamicFilter - Dynamic filter.
|
||||
* @param {string} customViewSlug - Custom view slug.
|
||||
* @returns {DynamicFilterRoleAbstractor}
|
||||
*/
|
||||
public dynamicListCustomView = async (
|
||||
|
||||
@@ -3,10 +3,10 @@ import { Injectable } from '@nestjs/common';
|
||||
import validator from 'is-my-json-valid';
|
||||
import { IFilterRole } from './DynamicFilter/DynamicFilter.types';
|
||||
import { DynamicFilterAdvancedFilter } from './DynamicFilter/DynamicFilterAdvancedFilter';
|
||||
import { ERRORS } from './constants';
|
||||
import { ServiceError } from '../Items/ServiceError';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { DynamicFilterRoleAbstractor } from './DynamicFilter/DynamicFilterRoleAbstractor';
|
||||
import { MetableModel } from './types/DynamicList.types';
|
||||
import { ServiceError } from '../Items/ServiceError';
|
||||
import { ERRORS } from './constants';
|
||||
|
||||
@Injectable()
|
||||
export class DynamicListFilterRoles extends DynamicFilterRoleAbstractor {
|
||||
@@ -34,12 +34,12 @@ export class DynamicListFilterRoles extends DynamicFilterRoleAbstractor {
|
||||
|
||||
/**
|
||||
* Retrieve filter roles fields key that not exists on the given model.
|
||||
* @param {BaseModel} model
|
||||
* @param {MetableModel} model
|
||||
* @param {IFilterRole} filterRoles
|
||||
* @returns {string[]}
|
||||
*/
|
||||
private getFilterRolesFieldsNotExist = (
|
||||
model: BaseModel,
|
||||
model: MetableModel,
|
||||
filterRoles: IFilterRole[],
|
||||
): string[] => {
|
||||
return filterRoles
|
||||
@@ -49,12 +49,12 @@ export class DynamicListFilterRoles extends DynamicFilterRoleAbstractor {
|
||||
|
||||
/**
|
||||
* Validates existance the fields of filter roles.
|
||||
* @param {BaseModel} model
|
||||
* @param {MetableModel} model
|
||||
* @param {IFilterRole[]} filterRoles
|
||||
* @throws {ServiceError}
|
||||
*/
|
||||
private validateFilterRolesFieldsExistance = (
|
||||
model: BaseModel,
|
||||
model: MetableModel,
|
||||
filterRoles: IFilterRole[],
|
||||
) => {
|
||||
const invalidFieldsKeys = this.getFilterRolesFieldsNotExist(
|
||||
@@ -82,12 +82,12 @@ export class DynamicListFilterRoles extends DynamicFilterRoleAbstractor {
|
||||
|
||||
/**
|
||||
* Dynamic list filter roles.
|
||||
* @param {BaseModel} model
|
||||
* @param {IFilterRole[]} filterRoles
|
||||
* @param {MetableModel} model - Metable model.
|
||||
* @param {IFilterRole[]} filterRoles - Filter roles.
|
||||
* @returns {DynamicFilterFilterRoles}
|
||||
*/
|
||||
public dynamicList = (
|
||||
model: BaseModel,
|
||||
model: MetableModel,
|
||||
filterRoles: IFilterRole[],
|
||||
): DynamicFilterAdvancedFilter => {
|
||||
const filterRolesParsed = R.compose(this.incrementFilterRolesIndex)(
|
||||
|
||||
@@ -4,10 +4,11 @@ import { ERRORS } from './constants';
|
||||
import { DynamicFilterSortBy } from './DynamicFilter';
|
||||
import { ServiceError } from '../Items/ServiceError';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { DynamicFilterRoleAbstractor } from './DynamicFilter/DynamicFilterRoleAbstractor';
|
||||
import { DynamicFilterAbstractor } from './DynamicFilter/DynamicFilterAbstractor';
|
||||
import { MetableModel } from './types/DynamicList.types';
|
||||
|
||||
@Injectable()
|
||||
export class DynamicListSortBy extends DynamicFilterRoleAbstractor {
|
||||
export class DynamicListSortBy extends DynamicFilterAbstractor {
|
||||
/**
|
||||
* Dynamic list sort by.
|
||||
* @param {BaseModel} model
|
||||
@@ -16,7 +17,7 @@ export class DynamicListSortBy extends DynamicFilterRoleAbstractor {
|
||||
* @returns {DynamicFilterSortBy}
|
||||
*/
|
||||
public dynamicSortBy(
|
||||
model: BaseModel,
|
||||
model: MetableModel,
|
||||
columnSortBy: string,
|
||||
sortOrder: ISortOrder,
|
||||
) {
|
||||
|
||||
@@ -11,7 +11,7 @@ const defaultModelMeta = {
|
||||
fields2: {},
|
||||
};
|
||||
|
||||
export interface IMetadataModel extends BaseModel {
|
||||
export interface IMetadataModel {
|
||||
meta: IModelMeta;
|
||||
parsedMeta: IModelMeta;
|
||||
fields: { [key: string]: IModelMetaField };
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { IModelMeta } from '@/interfaces/Model';
|
||||
import { ISearchRole } from '../DynamicFilter.types';
|
||||
import { ISearchRole } from '../DynamicFilter/DynamicFilter.types';
|
||||
|
||||
type GConstructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
export interface ISearchableBaseModel {
|
||||
searchRoles: ISearchRole[];
|
||||
}
|
||||
|
||||
export const SearchableBaseModelMixin = <T extends GConstructor<BaseModel>>(
|
||||
Model: T,
|
||||
) =>
|
||||
@@ -11,7 +15,7 @@ export const SearchableBaseModelMixin = <T extends GConstructor<BaseModel>>(
|
||||
/**
|
||||
* Searchable model.
|
||||
*/
|
||||
static get searchable(): IModelMeta {
|
||||
static get searchable(): boolean {
|
||||
throw true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { ISortOrder } from '@/interfaces/Model';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { ICustomViewBaseModel } from '@/modules/CustomViews/CustomViewBaseModel';
|
||||
import { IFilterRole } from '../DynamicFilter/DynamicFilter.types';
|
||||
import { IMetadataModel } from '../models/MetadataModel';
|
||||
import { ISearchableBaseModel } from '../models/SearchableBaseModel';
|
||||
|
||||
export interface IDynamicListFilter {
|
||||
customViewId?: number;
|
||||
@@ -8,4 +12,9 @@ export interface IDynamicListFilter {
|
||||
sortOrder: string;
|
||||
stringifiedFilterRoles: string;
|
||||
searchKeyword?: string;
|
||||
}
|
||||
}
|
||||
|
||||
export type MetableModel = typeof BaseModel &
|
||||
IMetadataModel &
|
||||
ISearchableBaseModel &
|
||||
ICustomViewBaseModel;
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
import { Model, mixin, raw } from 'objection';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import { viewRolesBuilder } from '@/lib/ViewRolesBuilder';
|
||||
// import ModelSetting from './ModelSetting';
|
||||
// import ExpenseSettings from './Expense.Settings';
|
||||
// import CustomViewBaseModel from './CustomViewBaseModel';
|
||||
// import { DEFAULT_VIEWS } from '@/services/Expenses/constants';
|
||||
// import ModelSearchable from './ModelSearchable';
|
||||
import { Model, raw } from 'objection';
|
||||
import moment from 'moment';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { ExpenseCategory } from './ExpenseCategory.model';
|
||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
export class Expense extends BaseModel {
|
||||
// ModelSetting,
|
||||
// CustomViewBaseModel,
|
||||
// ModelSearchable,
|
||||
// ]) {
|
||||
|
||||
export class Expense extends TenantBaseModel {
|
||||
totalAmount!: number;
|
||||
currencyCode!: string;
|
||||
exchangeRate!: number;
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
// import { Inject, Service } from 'typedi';
|
||||
// import { omit } from 'lodash';
|
||||
// import moment from 'moment';
|
||||
// import * as R from 'ramda';
|
||||
// import { Knex } from 'knex';
|
||||
// import { ServiceError } from '@/exceptions';
|
||||
// import {
|
||||
// IInventoryAdjustment,
|
||||
// IPaginationMeta,
|
||||
// IInventoryAdjustmentsFilter,
|
||||
// IInventoryTransaction,
|
||||
// IInventoryAdjustmentEventPublishedPayload,
|
||||
// IInventoryAdjustmentEventDeletedPayload,
|
||||
// IInventoryAdjustmentDeletingPayload,
|
||||
// IInventoryAdjustmentPublishingPayload,
|
||||
// } from '@/interfaces';
|
||||
// import events from '@/subscribers/events';
|
||||
// import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
// import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
// import InventoryService from './Inventory';
|
||||
// import UnitOfWork from '@/services/UnitOfWork';
|
||||
// import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
// import InventoryAdjustmentTransformer from './InventoryAdjustmentTransformer';
|
||||
// import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
||||
// import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform';
|
||||
// import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
// const ERRORS = {
|
||||
// INVENTORY_ADJUSTMENT_NOT_FOUND: 'INVENTORY_ADJUSTMENT_NOT_FOUND',
|
||||
// ITEM_SHOULD_BE_INVENTORY_TYPE: 'ITEM_SHOULD_BE_INVENTORY_TYPE',
|
||||
// INVENTORY_ADJUSTMENT_ALREADY_PUBLISHED:
|
||||
// 'INVENTORY_ADJUSTMENT_ALREADY_PUBLISHED',
|
||||
// };
|
||||
|
||||
// @Service()
|
||||
// export default class InventoryAdjustmentService {
|
||||
// @Inject()
|
||||
// private tenancy: HasTenancyService;
|
||||
|
||||
// @Inject()
|
||||
// private eventPublisher: EventPublisher;
|
||||
|
||||
// @Inject()
|
||||
// private inventoryService: InventoryService;
|
||||
|
||||
// @Inject()
|
||||
// private dynamicListService: DynamicListingService;
|
||||
|
||||
// @Inject()
|
||||
// private uow: UnitOfWork;
|
||||
|
||||
// @Inject()
|
||||
// private branchDTOTransform: BranchTransactionDTOTransform;
|
||||
|
||||
// @Inject()
|
||||
// private warehouseDTOTransform: WarehouseTransactionDTOTransform;
|
||||
|
||||
// @Inject()
|
||||
// private transfromer: TransformerInjectable;
|
||||
|
||||
// /**
|
||||
// * Retrieve the inventory adjustment or throw not found service error.
|
||||
// * @param {number} tenantId -
|
||||
// * @param {number} adjustmentId -
|
||||
// */
|
||||
// async getInventoryAdjustmentOrThrowError(
|
||||
// tenantId: number,
|
||||
// adjustmentId: number
|
||||
// ) {
|
||||
// const { InventoryAdjustment } = this.tenancy.models(tenantId);
|
||||
|
||||
// const inventoryAdjustment = await InventoryAdjustment.query()
|
||||
// .findById(adjustmentId)
|
||||
// .withGraphFetched('entries');
|
||||
|
||||
// if (!inventoryAdjustment) {
|
||||
// throw new ServiceError(ERRORS.INVENTORY_ADJUSTMENT_NOT_FOUND);
|
||||
// }
|
||||
// return inventoryAdjustment;
|
||||
// }
|
||||
|
||||
|
||||
// /**
|
||||
// * Parses inventory adjustments list filter DTO.
|
||||
// * @param filterDTO -
|
||||
// */
|
||||
// private parseListFilterDTO(filterDTO) {
|
||||
// return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Writes the inventory transactions from the inventory adjustment transaction.
|
||||
// * @param {number} tenantId -
|
||||
// * @param {IInventoryAdjustment} inventoryAdjustment -
|
||||
// * @param {boolean} override -
|
||||
// * @param {Knex.Transaction} trx -
|
||||
// * @return {Promise<void>}
|
||||
// */
|
||||
// public async writeInventoryTransactions(
|
||||
// tenantId: number,
|
||||
// inventoryAdjustment: IInventoryAdjustment,
|
||||
// override: boolean = false,
|
||||
// trx?: Knex.Transaction
|
||||
// ): Promise<void> {
|
||||
// const commonTransaction = {
|
||||
// direction: inventoryAdjustment.inventoryDirection,
|
||||
// date: inventoryAdjustment.date,
|
||||
// transactionType: 'InventoryAdjustment',
|
||||
// transactionId: inventoryAdjustment.id,
|
||||
// createdAt: inventoryAdjustment.createdAt,
|
||||
// costAccountId: inventoryAdjustment.adjustmentAccountId,
|
||||
|
||||
// branchId: inventoryAdjustment.branchId,
|
||||
// warehouseId: inventoryAdjustment.warehouseId,
|
||||
// };
|
||||
// const inventoryTransactions = [];
|
||||
|
||||
// inventoryAdjustment.entries.forEach((entry) => {
|
||||
// inventoryTransactions.push({
|
||||
// ...commonTransaction,
|
||||
// itemId: entry.itemId,
|
||||
// quantity: entry.quantity,
|
||||
// rate: entry.cost,
|
||||
// });
|
||||
// });
|
||||
// // Saves the given inventory transactions to the storage.
|
||||
// await this.inventoryService.recordInventoryTransactions(
|
||||
// tenantId,
|
||||
// inventoryTransactions,
|
||||
// override,
|
||||
// trx
|
||||
// );
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Reverts the inventory transactions from the inventory adjustment transaction.
|
||||
// * @param {number} tenantId
|
||||
// * @param {number} inventoryAdjustmentId
|
||||
// */
|
||||
// async revertInventoryTransactions(
|
||||
// tenantId: number,
|
||||
// inventoryAdjustmentId: number,
|
||||
// trx?: Knex.Transaction
|
||||
// ): Promise<{ oldInventoryTransactions: IInventoryTransaction[] }> {
|
||||
// return this.inventoryService.deleteInventoryTransactions(
|
||||
// tenantId,
|
||||
// inventoryAdjustmentId,
|
||||
// 'InventoryAdjustment',
|
||||
// trx
|
||||
// );
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
@@ -16,8 +16,10 @@ import {
|
||||
import { InventoryAdjustment } from './models/InventoryAdjustment';
|
||||
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||
import { IPaginationMeta } from '@/interfaces/Model';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@Controller('inventory-adjustments')
|
||||
@ApiTags('inventory-adjustments')
|
||||
@PublicRoute()
|
||||
export class InventoryAdjustmentsController {
|
||||
constructor(
|
||||
@@ -25,6 +27,7 @@ export class InventoryAdjustmentsController {
|
||||
) {}
|
||||
|
||||
@Post('quick')
|
||||
@ApiOperation({ summary: 'Create a quick inventory adjustment.' })
|
||||
public async createQuickInventoryAdjustment(
|
||||
@Body() quickAdjustmentDTO: IQuickInventoryAdjustmentDTO,
|
||||
): Promise<InventoryAdjustment> {
|
||||
@@ -34,6 +37,7 @@ export class InventoryAdjustmentsController {
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: 'Delete the given inventory adjustment.' })
|
||||
public async deleteInventoryAdjustment(
|
||||
@Param('id') inventoryAdjustmentId: number,
|
||||
): Promise<void> {
|
||||
@@ -43,6 +47,7 @@ export class InventoryAdjustmentsController {
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Retrieves the inventory adjustments.' })
|
||||
public async getInventoryAdjustments(
|
||||
@Query() filterDTO: IInventoryAdjustmentsFilter,
|
||||
): Promise<{
|
||||
@@ -55,6 +60,7 @@ export class InventoryAdjustmentsController {
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Retrieves the inventory adjustment details.' })
|
||||
public async getInventoryAdjustment(
|
||||
@Param('id') inventoryAdjustmentId: number,
|
||||
): Promise<InventoryAdjustment> {
|
||||
@@ -64,6 +70,7 @@ export class InventoryAdjustmentsController {
|
||||
}
|
||||
|
||||
@Put(':id/publish')
|
||||
@ApiOperation({ summary: 'Publish the given inventory adjustment.' })
|
||||
public async publishInventoryAdjustment(
|
||||
@Param('id') inventoryAdjustmentId: number,
|
||||
): Promise<void> {
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { Model } from 'objection';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import InventoryAdjustmentSettings from './InventoryAdjustment.Settings';
|
||||
// import ModelSetting from './ModelSetting';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { InventoryAdjustmentEntry } from './InventoryAdjustmentEntry';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
export class InventoryAdjustment extends BaseModel {
|
||||
export class InventoryAdjustment extends TenantBaseModel {
|
||||
date!: string;
|
||||
type!: string;
|
||||
adjustmentAccountId!: number;
|
||||
@@ -32,28 +29,28 @@ export class InventoryAdjustment extends BaseModel {
|
||||
/**
|
||||
* Timestamps columns.
|
||||
*/
|
||||
get timestamps() {
|
||||
get timestamps(): Array<string> {
|
||||
return ['created_at'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual attributes.
|
||||
*/
|
||||
static get virtualAttributes() {
|
||||
static get virtualAttributes(): Array<string> {
|
||||
return ['formattedType', 'inventoryDirection', 'isPublished'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve formatted adjustment type.
|
||||
*/
|
||||
get formattedType() {
|
||||
get formattedType(): string {
|
||||
return InventoryAdjustment.getFormattedType(this.type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve formatted reference type.
|
||||
*/
|
||||
get inventoryDirection() {
|
||||
get inventoryDirection(): string {
|
||||
return InventoryAdjustment.getInventoryDirection(this.type);
|
||||
}
|
||||
|
||||
@@ -61,7 +58,7 @@ export class InventoryAdjustment extends BaseModel {
|
||||
* Detarmines whether the adjustment is published.
|
||||
* @return {boolean}
|
||||
*/
|
||||
get isPublished() {
|
||||
get isPublished(): boolean {
|
||||
return !!this.publishedAt;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export class GetInventoryAdjustmentsService {
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
InventoryAdjustment,
|
||||
this.inventoryAdjustmentModel,
|
||||
filter,
|
||||
);
|
||||
const { results, pagination } = await this.inventoryAdjustmentModel
|
||||
|
||||
@@ -27,12 +27,8 @@ import InventoryCostMethod from './InventoryCostMethod';
|
||||
export class InventoryService {
|
||||
constructor(
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly itemsEntriesService: ItemsEntriesService,
|
||||
private readonly uow: UnitOfWork,
|
||||
|
||||
@Inject(Item.name)
|
||||
private readonly itemModel: typeof Item,
|
||||
|
||||
@Inject(InventoryTransaction.name)
|
||||
private readonly inventoryTransactionModel: typeof InventoryTransaction,
|
||||
|
||||
@@ -340,7 +336,7 @@ export class InventoryService {
|
||||
|
||||
/**
|
||||
* Mark item cost computing is running.
|
||||
* @param {boolean} isRunning -
|
||||
* @param {boolean} isRunning -
|
||||
*/
|
||||
async markItemsCostComputeRunning(isRunning: boolean = true) {
|
||||
this.settings.set({
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InventoryCostGLBeforeWriteSubscriber } from './subscribers/InventoryCostGLBeforeWriteSubscriber';
|
||||
import { InventoryCostGLStorage } from './InventoryCostGLStorage.service';
|
||||
import { RegisterTenancyModel } from '../Tenancy/TenancyModels/Tenancy.module';
|
||||
import { InventoryCostLotTracker } from './models/InventoryCostLotTracker';
|
||||
import { InventoryTransaction } from './models/InventoryTransaction';
|
||||
import { InventoryCostGLBeforeWriteSubscriber } from './subscribers/InventoryCostGLBeforeWriteSubscriber';
|
||||
import { InventoryItemsQuantitySyncService } from './InventoryItemsQuantitySync.service';
|
||||
import { InventoryCostMethod } from './InventoryCostMethod';
|
||||
|
||||
const models = [
|
||||
RegisterTenancyModel(InventoryCostLotTracker),
|
||||
@@ -15,6 +17,8 @@ const models = [
|
||||
...models,
|
||||
InventoryCostGLBeforeWriteSubscriber,
|
||||
InventoryCostGLStorage,
|
||||
InventoryItemsQuantitySyncService,
|
||||
InventoryCostMethod,
|
||||
],
|
||||
exports: [...models],
|
||||
})
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { IInventoryItemCostMeta } from '@/interfaces';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InventoryItemCostService } from './InventoryCosts.service';
|
||||
import { IInventoryItemCostMeta } from './types/InventoryCost.types';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class InventoryCostApplication {
|
||||
@Inject()
|
||||
inventoryCost: InventoryItemCostService;
|
||||
constructor(
|
||||
private readonly inventoryCost: InventoryItemCostService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the items inventory valuation list.
|
||||
* @param {number} tenantId
|
||||
* @param {number[]} itemsId
|
||||
* @param {Date} date
|
||||
* @param {number[]} itemsId
|
||||
* @param {Date} date
|
||||
* @returns {Promise<IInventoryItemCostMeta[]>}
|
||||
*/
|
||||
public getItemsInventoryValuationList = async (
|
||||
@@ -19,7 +19,6 @@ export class InventoryCostApplication {
|
||||
date: Date
|
||||
): Promise<IInventoryItemCostMeta[]> => {
|
||||
const itemsMap = await this.inventoryCost.getItemsInventoryValuation(
|
||||
tenantId,
|
||||
itemsId,
|
||||
date
|
||||
);
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
import { omit } from 'lodash';
|
||||
import { Container } from 'typedi';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { IInventoryLotCost } from '@/interfaces';
|
||||
import { InventoryCostLotTracker } from './models/InventoryCostLotTracker';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export default class InventoryCostMethod {
|
||||
tenancy: TenancyService;
|
||||
tenantModels: any;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {number} tenantId - The given tenant id.
|
||||
*/
|
||||
constructor(tenantId: number, startingDate: Date, itemId: number) {
|
||||
const tenancyService = Container.get(TenancyService);
|
||||
|
||||
this.tenantModels = tenancyService.models(tenantId);
|
||||
}
|
||||
export class InventoryCostMethod {
|
||||
constructor(
|
||||
@Inject(InventoryCostLotTracker.name)
|
||||
private readonly inventoryCostLotTracker: typeof InventoryCostLotTracker
|
||||
) {}
|
||||
|
||||
|
||||
/**
|
||||
* Stores the inventory lots costs transactions in bulk.
|
||||
@@ -23,19 +16,19 @@ export default class InventoryCostMethod {
|
||||
* @return {Promise[]}
|
||||
*/
|
||||
public storeInventoryLotsCost(
|
||||
costLotsTransactions: IInventoryLotCost[]
|
||||
costLotsTransactions: InventoryCostLotTracker[],
|
||||
trx: Knex.Transaction
|
||||
): Promise<object> {
|
||||
const { InventoryCostLotTracker } = this.tenantModels;
|
||||
const opers: any = [];
|
||||
|
||||
costLotsTransactions.forEach((transaction: any) => {
|
||||
if (transaction.lotTransId && transaction.decrement) {
|
||||
const decrementOper = InventoryCostLotTracker.query(this.trx)
|
||||
const decrementOper = this.inventoryCostLotTracker.query(trx)
|
||||
.where('id', transaction.lotTransId)
|
||||
.decrement('remaining', transaction.decrement);
|
||||
opers.push(decrementOper);
|
||||
} else if (!transaction.lotTransId) {
|
||||
const operation = InventoryCostLotTracker.query(this.trx).insert({
|
||||
const operation = this.inventoryCostLotTracker.query(trx).insert({
|
||||
...omit(transaction, ['decrement', 'invTransId', 'lotTransId']),
|
||||
});
|
||||
opers.push(operation);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { toSafeInteger } from 'lodash';
|
||||
import { IInventoryTransaction, IItemsQuantityChanges } from '@/interfaces';
|
||||
import { IItemsQuantityChanges } from './types/InventoryCost.types';
|
||||
import { Knex } from 'knex';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { Item } from '../Items/models/Item';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InventoryTransaction } from './models/InventoryTransaction';
|
||||
|
||||
/**
|
||||
* Syncs the inventory transactions with inventory items quantity.
|
||||
@@ -11,8 +12,7 @@ import { Injectable } from '@nestjs/common';
|
||||
@Injectable()
|
||||
export class InventoryItemsQuantitySyncService {
|
||||
constructor(
|
||||
@Inject(Item.name)
|
||||
private readonly itemModel: typeof Item,
|
||||
@Inject(Item.name) private readonly itemModel: typeof Item,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -21,12 +21,14 @@ export class InventoryItemsQuantitySyncService {
|
||||
* @return {IInventoryTransaction[]}
|
||||
*/
|
||||
public reverseInventoryTransactions(
|
||||
inventroyTransactions: IInventoryTransaction[],
|
||||
): IInventoryTransaction[] {
|
||||
return inventroyTransactions.map((transaction) => ({
|
||||
...transaction,
|
||||
direction: transaction.direction === 'OUT' ? 'IN' : 'OUT',
|
||||
}));
|
||||
inventroyTransactions: InventoryTransaction[],
|
||||
): InventoryTransaction[] {
|
||||
return inventroyTransactions.map((transaction) => {
|
||||
const cloned = transaction.$clone();
|
||||
cloned.direction = cloned.direction === 'OUT' ? 'IN' : 'OUT';
|
||||
|
||||
return cloned;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,7 +37,7 @@ export class InventoryItemsQuantitySyncService {
|
||||
* @return {IItemsQuantityChanges[]}
|
||||
*/
|
||||
public getReverseItemsQuantityChanges(
|
||||
inventroyTransactions: IInventoryTransaction[],
|
||||
inventroyTransactions: InventoryTransaction[],
|
||||
): IItemsQuantityChanges[] {
|
||||
const reversedTransactions = this.reverseInventoryTransactions(
|
||||
inventroyTransactions,
|
||||
@@ -49,12 +51,12 @@ export class InventoryItemsQuantitySyncService {
|
||||
* @return {IItemsQuantityChanges[]}
|
||||
*/
|
||||
public getItemsQuantityChanges(
|
||||
inventroyTransactions: IInventoryTransaction[],
|
||||
inventroyTransactions: InventoryTransaction[],
|
||||
): IItemsQuantityChanges[] {
|
||||
const balanceMap: { [itemId: number]: number } = {};
|
||||
|
||||
inventroyTransactions.forEach(
|
||||
(inventoryTransaction: IInventoryTransaction) => {
|
||||
(inventoryTransaction: InventoryTransaction) => {
|
||||
const { itemId, direction, quantity } = inventoryTransaction;
|
||||
|
||||
if (!balanceMap[itemId]) {
|
||||
|
||||
@@ -55,7 +55,16 @@ export class InventoryCostLotTracker extends BaseModel {
|
||||
query.groupBy('date');
|
||||
query.groupBy('item_id');
|
||||
},
|
||||
filterDateRange(query, startDate, endDate, type: unitOfTime.StartOf = 'day') {
|
||||
|
||||
/**
|
||||
* Filters transactions by the given date range.
|
||||
*/
|
||||
filterDateRange(
|
||||
query,
|
||||
startDate,
|
||||
endDate,
|
||||
type: unitOfTime.StartOf = 'day',
|
||||
) {
|
||||
const dateFormat = 'YYYY-MM-DD';
|
||||
const fromDate = moment(startDate).startOf(type).format(dateFormat);
|
||||
const toDate = moment(endDate).endOf(type).format(dateFormat);
|
||||
@@ -71,7 +80,7 @@ export class InventoryCostLotTracker extends BaseModel {
|
||||
/**
|
||||
* Filters transactions by the given branches.
|
||||
*/
|
||||
filterByBranches(query, branchesIds) {
|
||||
filterByBranches(query, branchesIds: number | Array<number>) {
|
||||
const formattedBranchesIds = castArray(branchesIds);
|
||||
|
||||
query.whereIn('branchId', formattedBranchesIds);
|
||||
@@ -80,7 +89,7 @@ export class InventoryCostLotTracker extends BaseModel {
|
||||
/**
|
||||
* Filters transactions by the given warehosues.
|
||||
*/
|
||||
filterByWarehouses(query, branchesIds) {
|
||||
filterByWarehouses(query, branchesIds: number | Array<number>) {
|
||||
const formattedWarehousesIds = castArray(branchesIds);
|
||||
|
||||
query.whereIn('warehouseId', formattedWarehousesIds);
|
||||
@@ -92,15 +101,17 @@ export class InventoryCostLotTracker extends BaseModel {
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
const Item = require('models/Item');
|
||||
const SaleInvoice = require('models/SaleInvoice');
|
||||
const ItemEntry = require('models/ItemEntry');
|
||||
const SaleReceipt = require('models/SaleReceipt');
|
||||
const { Item } = require('../../Items/models/Item');
|
||||
const { SaleInvoice } = require('../../SaleInvoices/models/SaleInvoice');
|
||||
const {
|
||||
ItemEntry,
|
||||
} = require('../../TransactionItemEntry/models/ItemEntry');
|
||||
const { SaleReceipt } = require('../../SaleReceipts/models/SaleReceipt');
|
||||
|
||||
return {
|
||||
item: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: Item.default,
|
||||
modelClass: Item,
|
||||
join: {
|
||||
from: 'inventory_cost_lot_tracker.itemId',
|
||||
to: 'items.id',
|
||||
@@ -108,7 +119,7 @@ export class InventoryCostLotTracker extends BaseModel {
|
||||
},
|
||||
invoice: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: SaleInvoice.default,
|
||||
modelClass: SaleInvoice,
|
||||
join: {
|
||||
from: 'inventory_cost_lot_tracker.transactionId',
|
||||
to: 'sales_invoices.id',
|
||||
@@ -116,7 +127,7 @@ export class InventoryCostLotTracker extends BaseModel {
|
||||
},
|
||||
itemEntry: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: ItemEntry.default,
|
||||
modelClass: ItemEntry,
|
||||
join: {
|
||||
from: 'inventory_cost_lot_tracker.entryId',
|
||||
to: 'items_entries.id',
|
||||
@@ -124,7 +135,7 @@ export class InventoryCostLotTracker extends BaseModel {
|
||||
},
|
||||
receipt: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: SaleReceipt.default,
|
||||
modelClass: SaleReceipt,
|
||||
join: {
|
||||
from: 'inventory_cost_lot_tracker.transactionId',
|
||||
to: 'sales_receipts.id',
|
||||
|
||||
@@ -106,10 +106,12 @@ export class InventoryTransaction extends BaseModel {
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
const Item = require('models/Item');
|
||||
const ItemEntry = require('models/ItemEntry');
|
||||
const InventoryTransactionMeta = require('models/InventoryTransactionMeta');
|
||||
const InventoryCostLots = require('models/InventoryCostLotTracker');
|
||||
const { Item } = require('../../Items/models/Item');
|
||||
const {
|
||||
ItemEntry,
|
||||
} = require('../../TransactionItemEntry/models/ItemEntry');
|
||||
const { InventoryTransactionMeta } = require('./InventoryTransactionMeta');
|
||||
const { InventoryCostLotTracker } = require('./InventoryCostLotTracker');
|
||||
|
||||
return {
|
||||
// Transaction meta.
|
||||
@@ -124,7 +126,7 @@ export class InventoryTransaction extends BaseModel {
|
||||
// Item cost aggregated.
|
||||
itemCostAggregated: {
|
||||
relation: Model.HasOneRelation,
|
||||
modelClass: InventoryCostLots.default,
|
||||
modelClass: InventoryCostLotTracker,
|
||||
join: {
|
||||
from: 'inventory_transactions.itemId',
|
||||
to: 'inventory_cost_lot_tracker.itemId',
|
||||
@@ -138,7 +140,7 @@ export class InventoryTransaction extends BaseModel {
|
||||
},
|
||||
costLotAggregated: {
|
||||
relation: Model.HasOneRelation,
|
||||
modelClass: InventoryCostLots.default,
|
||||
modelClass: InventoryCostLotTracker,
|
||||
join: {
|
||||
from: 'inventory_transactions.id',
|
||||
to: 'inventory_cost_lot_tracker.inventoryTransactionId',
|
||||
@@ -151,7 +153,7 @@ export class InventoryTransaction extends BaseModel {
|
||||
},
|
||||
item: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: Item.default,
|
||||
modelClass: Item,
|
||||
join: {
|
||||
from: 'inventory_transactions.itemId',
|
||||
to: 'items.id',
|
||||
@@ -159,7 +161,7 @@ export class InventoryTransaction extends BaseModel {
|
||||
},
|
||||
itemEntry: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: ItemEntry.default,
|
||||
modelClass: ItemEntry,
|
||||
join: {
|
||||
from: 'inventory_transactions.entryId',
|
||||
to: 'items_entries.id',
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { Model, raw } from 'objection';
|
||||
|
||||
export class InventoryTransactionMeta extends BaseModel {
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
static get tableName() {
|
||||
return 'inventory_transaction_meta';
|
||||
}
|
||||
|
||||
/**
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
const { InventoryTransaction } = require('./InventoryTransaction');
|
||||
|
||||
return {
|
||||
inventoryTransaction: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: InventoryTransaction,
|
||||
join: {
|
||||
from: 'inventory_transaction_meta.inventoryTransactionId',
|
||||
to: 'inventory_transactions.inventoryTransactionId'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { Model, mixin } from 'objection';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import ModelSetting from './ModelSetting';
|
||||
// import ItemCategorySettings from './ItemCategory.Settings';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
import { Model } from 'objection';
|
||||
|
||||
export class ItemCategory extends BaseModel {
|
||||
export class ItemCategory extends TenantBaseModel {
|
||||
name!: string;
|
||||
description!: string;
|
||||
|
||||
|
||||
@@ -1,19 +1,7 @@
|
||||
import * as R from 'ramda';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
|
||||
import { CustomViewBaseModelMixin } from '@/modules/CustomViews/CustomViewBaseModel';
|
||||
import { SearchableBaseModelMixin } from '@/modules/DynamicListing/models/SearchableBaseModel';
|
||||
import { ResourceableModelMixin } from '@/modules/Resource/models/ResourcableModel';
|
||||
import { MetadataModelMixin } from '@/modules/DynamicListing/models/MetadataModel';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
const ExtendedItem = R.pipe(
|
||||
CustomViewBaseModelMixin,
|
||||
SearchableBaseModelMixin,
|
||||
ResourceableModelMixin,
|
||||
MetadataModelMixin
|
||||
)(BaseModel);
|
||||
|
||||
export class Item extends ExtendedItem {
|
||||
export class Item extends TenantBaseModel{
|
||||
public readonly quantityOnHand: number;
|
||||
public readonly name: string;
|
||||
public readonly active: boolean;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Transporter } from 'nodemailer';
|
||||
import { Mail } from './Mail';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { MAIL_TRANSPORTER_PROVIDER } from './Mail.constants';
|
||||
|
||||
@Injectable()
|
||||
export class MailTransporter {
|
||||
constructor(
|
||||
@Inject(MAIL_TRANSPORTER_PROVIDER)
|
||||
|
||||
@@ -4,6 +4,7 @@ import { MailTenancy } from '../MailTenancy/MailTenancy.service';
|
||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
import { Customer } from '../Customers/models/Customer';
|
||||
import { CommonMailOptions } from './MailNotification.types';
|
||||
import { formatMessage } from '@/utils/format-message';
|
||||
|
||||
@Injectable()
|
||||
export class ContactMailNotification {
|
||||
@@ -56,8 +57,8 @@ export class ContactMailNotification {
|
||||
...commonFormatArgs,
|
||||
...formatterArgs,
|
||||
};
|
||||
const subjectFormatted = formatSmsMessage(mailOptions?.subject, formatArgs);
|
||||
const messageFormatted = formatSmsMessage(mailOptions?.message, formatArgs);
|
||||
const subjectFormatted = formatMessage(mailOptions?.subject, formatArgs);
|
||||
const messageFormatted = formatMessage(mailOptions?.message, formatArgs);
|
||||
|
||||
return {
|
||||
...mailOptions,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { castArray, isEmpty } from 'lodash';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { CommonMailOptions } from '@/interfaces';
|
||||
import { ERRORS } from './constants';
|
||||
import { CommonMailOptions } from './MailNotification.types';
|
||||
import { ServiceError } from '../Items/ServiceError';
|
||||
|
||||
/**
|
||||
* Merges the mail options with incoming options.
|
||||
|
||||
@@ -7,10 +7,10 @@ import { Model, mixin } from 'objection';
|
||||
// import { DEFAULT_VIEWS } from '@/services/ManualJournals/constants';
|
||||
// import ModelSearchable from './ModelSearchable';
|
||||
import { ManualJournalEntry } from './ManualJournalEntry';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { Document } from '@/modules/ChromiumlyTenancy/models/Document';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
export class ManualJournal extends BaseModel {
|
||||
export class ManualJournal extends TenantBaseModel {
|
||||
date: Date;
|
||||
journalNumber: string;
|
||||
journalType: string;
|
||||
|
||||
@@ -41,7 +41,7 @@ export class GetManualJournals {
|
||||
|
||||
// Dynamic service.
|
||||
const dynamicService = await this.dynamicListService.dynamicList(
|
||||
ManualJournal,
|
||||
this.manualJournalModel,
|
||||
filter,
|
||||
);
|
||||
const { results, pagination } = await this.manualJournalModel
|
||||
|
||||
@@ -13,10 +13,13 @@ import { PaymentReceivesApplication } from './PaymentReceived.application';
|
||||
import {
|
||||
IPaymentReceivedCreateDTO,
|
||||
IPaymentReceivedEditDTO,
|
||||
IPaymentsReceivedFilter,
|
||||
} from './types/PaymentReceived.types';
|
||||
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@Controller('payments-received')
|
||||
@ApiTags('payments-received')
|
||||
@PublicRoute()
|
||||
export class PaymentReceivesController {
|
||||
constructor(private paymentReceivesApplication: PaymentReceivesApplication) {}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
DEFAULT_PAYMENT_MAIL_CONTENT,
|
||||
DEFAULT_PAYMENT_MAIL_SUBJECT,
|
||||
} from '../constants';
|
||||
import { transformPaymentReceivedToMailDataArgs } from './utils';
|
||||
import { transformPaymentReceivedToMailDataArgs } from '../utils';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { ContactMailNotification } from '@/modules/MailNotification/ContactMailNotification';
|
||||
@@ -14,16 +14,19 @@ import { PaymentReceiveMailOptsDTO } from '../types/PaymentReceived.types';
|
||||
import { PaymentReceiveMailOpts } from '../types/PaymentReceived.types';
|
||||
import { PaymentReceiveMailPresendEvent } from '../types/PaymentReceived.types';
|
||||
import { SendInvoiceMailDTO } from '@/modules/SaleInvoices/SaleInvoice.types';
|
||||
import { Mail } from '@/modules/Mail/Mail';
|
||||
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
|
||||
|
||||
@Injectable()
|
||||
export class SendPaymentReceiveMailNotification {
|
||||
constructor(
|
||||
private getPaymentService: GetPaymentReceivedService,
|
||||
private contactMailNotification: ContactMailNotification,
|
||||
private eventEmitter: EventEmitter2,
|
||||
private readonly getPaymentService: GetPaymentReceivedService,
|
||||
private readonly contactMailNotification: ContactMailNotification,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly mailTransport: MailTransporter,
|
||||
|
||||
@Inject(PaymentReceived.name)
|
||||
private paymentReceiveModel: typeof PaymentReceived,
|
||||
private readonly paymentReceiveModel: typeof PaymentReceived,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -148,7 +151,7 @@ export class SendPaymentReceiveMailNotification {
|
||||
events.paymentReceive.onMailSend,
|
||||
eventPayload,
|
||||
);
|
||||
await mail.send();
|
||||
await this.mailTransport.send(mail);
|
||||
|
||||
// Triggers `onPaymentReceiveMailSent` event.
|
||||
await this.eventEmitter.emitAsync(
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import { Model, mixin } from 'objection';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import ModelSetting from './ModelSetting';
|
||||
// import PaymentReceiveSettings from './PaymentReceive.Settings';
|
||||
// import CustomViewBaseModel from './CustomViewBaseModel';
|
||||
// import { DEFAULT_VIEWS } from '@/services/Sales/PaymentReceived/constants';
|
||||
// import ModelSearchable from './ModelSearchable';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { Model } from 'objection';
|
||||
import { PaymentReceivedEntry } from './PaymentReceivedEntry';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
export class PaymentReceived extends BaseModel {
|
||||
export class PaymentReceived extends TenantBaseModel {
|
||||
customerId: number;
|
||||
paymentDate: string;
|
||||
amount: number;
|
||||
@@ -69,7 +63,9 @@ export class PaymentReceived extends BaseModel {
|
||||
*/
|
||||
static get relationMappings() {
|
||||
const { PaymentReceivedEntry } = require('./PaymentReceivedEntry');
|
||||
const { AccountTransaction } = require('../../Accounts/models/AccountTransaction.model');
|
||||
const {
|
||||
AccountTransaction,
|
||||
} = require('../../Accounts/models/AccountTransaction.model');
|
||||
const { Customer } = require('../../Customers/models/Customer');
|
||||
const { Account } = require('../../Accounts/models/Account.model');
|
||||
const { Branch } = require('../../Branches/models/Branch.model');
|
||||
|
||||
@@ -31,7 +31,7 @@ export class GetPaymentsReceivedService {
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
PaymentReceive,
|
||||
PaymentReceived,
|
||||
filter,
|
||||
);
|
||||
const { results, pagination } = await this.paymentReceivedModel
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import moment from 'moment';
|
||||
import { Model } from 'objection';
|
||||
import { ItemEntry } from '../../TransactionItemEntry/models/ItemEntry';
|
||||
import { Customer } from '../../Customers/models/Customer';
|
||||
import { Branch } from '../../Branches/models/Branch.model';
|
||||
import { Warehouse } from '../../Warehouses/models/Warehouse.model';
|
||||
import { Document } from '../../ChromiumlyTenancy/models/Document';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class SaleEstimate extends BaseModel {
|
||||
export class SaleEstimate extends TenantBaseModel {
|
||||
exchangeRate!: number;
|
||||
amount!: number;
|
||||
|
||||
@@ -207,12 +202,16 @@ export class SaleEstimate extends BaseModel {
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
const { ItemEntry } = require('../../TransactionItemEntry/models/ItemEntry');
|
||||
const {
|
||||
ItemEntry,
|
||||
} = require('../../TransactionItemEntry/models/ItemEntry');
|
||||
const { Customer } = require('../../Customers/models/Customer');
|
||||
const { Branch } = require('../../Branches/models/Branch.model');
|
||||
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
|
||||
const { Document } = require('../../ChromiumlyTenancy/models/Document');
|
||||
const { PdfTemplateModel } = require('../../PdfTemplate/models/PdfTemplate');
|
||||
const {
|
||||
PdfTemplateModel,
|
||||
} = require('../../PdfTemplate/models/PdfTemplate');
|
||||
|
||||
return {
|
||||
customer: {
|
||||
|
||||
@@ -3,7 +3,10 @@ import { IItemEntryDTO } from '../TransactionItemEntry/ItemEntry.types';
|
||||
import { AttachmentLinkDTO } from '../Attachments/Attachments.types';
|
||||
import { SaleInvoice } from './models/SaleInvoice';
|
||||
import { IDynamicListFilter } from '../DynamicListing/DynamicFilter/DynamicFilter.types';
|
||||
import { CommonMailOptionsDTO } from '../MailNotification/MailNotification.types';
|
||||
import {
|
||||
CommonMailOptions,
|
||||
CommonMailOptionsDTO,
|
||||
} from '../MailNotification/MailNotification.types';
|
||||
// import SaleInvoice from './models/SaleInvoice';
|
||||
// import { SystemUser } from '../System/models/SystemUser';
|
||||
// import { ISystemUser, IAccount, ITaxTransaction } from '@/interfaces';
|
||||
@@ -185,34 +188,34 @@ export enum SaleInvoiceAction {
|
||||
NotifyBySms = 'NotifyBySms',
|
||||
}
|
||||
|
||||
// export interface SaleInvoiceMailOptions extends CommonMailOptions {
|
||||
// attachInvoice?: boolean;
|
||||
// formatArgs?: Record<string, any>;
|
||||
// }
|
||||
export interface SaleInvoiceMailOptions extends CommonMailOptions {
|
||||
attachInvoice?: boolean;
|
||||
formatArgs?: Record<string, any>;
|
||||
}
|
||||
|
||||
// export interface SaleInvoiceMailState extends SaleInvoiceMailOptions {
|
||||
// invoiceNo: string;
|
||||
export interface SaleInvoiceMailState extends SaleInvoiceMailOptions {
|
||||
invoiceNo: string;
|
||||
|
||||
// invoiceDate: string;
|
||||
// invoiceDateFormatted: string;
|
||||
invoiceDate: string;
|
||||
invoiceDateFormatted: string;
|
||||
|
||||
// dueDate: string;
|
||||
// dueDateFormatted: string;
|
||||
dueDate: string;
|
||||
dueDateFormatted: string;
|
||||
|
||||
// total: number;
|
||||
// totalFormatted: string;
|
||||
total: number;
|
||||
totalFormatted: string;
|
||||
|
||||
// subtotal: number;
|
||||
// subtotalFormatted: number;
|
||||
subtotal: number;
|
||||
subtotalFormatted: number;
|
||||
|
||||
// companyName: string;
|
||||
// companyLogoUri: string;
|
||||
companyName: string;
|
||||
companyLogoUri: string;
|
||||
|
||||
// customerName: string;
|
||||
customerName: string;
|
||||
|
||||
// // # Invoice entries
|
||||
// entries?: Array<{ label: string; total: string; quantity: string | number }>;
|
||||
// }
|
||||
// # Invoice entries
|
||||
entries?: Array<{ label: string; total: string; quantity: string | number }>;
|
||||
}
|
||||
|
||||
export interface SendInvoiceMailDTO extends CommonMailOptionsDTO {
|
||||
attachInvoice?: boolean;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { CreateSaleInvoice } from './commands/CreateSaleInvoice.service';
|
||||
import { DeleteSaleInvoice } from './commands/DeleteSaleInvoice.service';
|
||||
import { GetSaleInvoice } from './queries/GetSaleInvoice.service';
|
||||
import { EditSaleInvoice } from './commands/EditSaleInvoice.service';
|
||||
// import { GetSaleInvoices } from './queries/GetSaleInvoices';
|
||||
import { DeliverSaleInvoice } from './commands/DeliverSaleInvoice.service';
|
||||
import { GetSaleInvoicesPayable } from './queries/GetSaleInvoicesPayable.service';
|
||||
import { WriteoffSaleInvoice } from './commands/WriteoffSaleInvoice.service';
|
||||
@@ -11,16 +10,18 @@ import { SaleInvoicePdf } from './queries/SaleInvoicePdf.service';
|
||||
import { GetInvoicePaymentsService } from './queries/GetInvoicePayments.service';
|
||||
// import { SaleInvoiceNotifyBySms } from './SaleInvoiceNotifyBySms';
|
||||
// import { SendInvoiceMailReminder } from './commands/SendSaleInvoiceMailReminder';
|
||||
// import { SendSaleInvoiceMail } from './commands/SendSaleInvoiceMail';
|
||||
import { GetSaleInvoiceState } from './queries/GetSaleInvoiceState.service';
|
||||
// import { GetSaleInvoiceMailState } from './queries/GetSaleInvoiceMailState.service';
|
||||
import { GetSaleInvoiceMailState } from './queries/GetSaleInvoiceMailState.service';
|
||||
import {
|
||||
ISaleInvoiceCreateDTO,
|
||||
ISaleInvoiceEditDTO,
|
||||
ISaleInvoiceWriteoffDTO,
|
||||
ISalesInvoicesFilter,
|
||||
SaleInvoiceMailState,
|
||||
SendInvoiceMailDTO,
|
||||
} from './SaleInvoice.types';
|
||||
import { GetSaleInvoicesService } from './queries/GetSaleInvoices';
|
||||
import { SendSaleInvoiceMail } from './commands/SendSaleInvoiceMail';
|
||||
|
||||
@Injectable()
|
||||
export class SaleInvoiceApplication {
|
||||
@@ -36,10 +37,9 @@ export class SaleInvoiceApplication {
|
||||
private getInvoicePaymentsService: GetInvoicePaymentsService,
|
||||
private pdfSaleInvoiceService: SaleInvoicePdf,
|
||||
private getSaleInvoiceStateService: GetSaleInvoiceState,
|
||||
private sendSaleInvoiceMailService: SendSaleInvoiceMail,
|
||||
private getSaleInvoiceMailStateService: GetSaleInvoiceMailState,
|
||||
// private invoiceSms: SaleInvoiceNotifyBySms,
|
||||
private sendInvoiceReminderService: SendInvoiceMailReminder,
|
||||
// private sendSaleInvoiceMailService: SendSaleInvoiceMail,
|
||||
// private getSaleInvoiceMailStateService: GetSaleInvoiceMailState,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -175,6 +175,22 @@ export class SaleInvoiceApplication {
|
||||
return this.pdfSaleInvoiceService.getSaleInvoiceHtml(saleInvoiceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the invoice mail of the given sale invoice.
|
||||
* @param {number} saleInvoiceId - Sale invoice id.
|
||||
* @param {SendInvoiceMailDTO} messageDTO - Message data.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public sendSaleInvoiceMail(
|
||||
saleInvoiceId: number,
|
||||
messageDTO: SendInvoiceMailDTO,
|
||||
) {
|
||||
return this.sendSaleInvoiceMailService.triggerMail(
|
||||
saleInvoiceId,
|
||||
messageDTO,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
@@ -223,53 +239,16 @@ export class SaleInvoiceApplication {
|
||||
// );
|
||||
// }
|
||||
|
||||
/**
|
||||
* Sends reminder of the given invoice to the invoice's customer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleInvoiceId
|
||||
* @returns {}
|
||||
*/
|
||||
// public sendSaleInvoiceMailReminder(
|
||||
// tenantId: number,
|
||||
// saleInvoiceId: number,
|
||||
// messageDTO: SendInvoiceMailDTO,
|
||||
// ) {
|
||||
// return this.sendInvoiceReminderService.triggerMail(
|
||||
// tenantId,
|
||||
// saleInvoiceId,
|
||||
// messageDTO,
|
||||
// );
|
||||
// }
|
||||
|
||||
/**
|
||||
* Sends the invoice mail of the given sale invoice.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleInvoiceId
|
||||
* @param {SendInvoiceMailDTO} messageDTO
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
// public sendSaleInvoiceMail(
|
||||
// tenantId: number,
|
||||
// saleInvoiceId: number,
|
||||
// messageDTO: SendInvoiceMailDTO,
|
||||
// ) {
|
||||
// return this.sendSaleInvoiceMailService.triggerMail(
|
||||
// tenantId,
|
||||
// saleInvoiceId,
|
||||
// messageDTO,
|
||||
// );
|
||||
// }
|
||||
|
||||
/**
|
||||
* Retrieves the default mail options of the given sale invoice.
|
||||
* @param {number} saleInvoiceid
|
||||
* @returns {Promise<SaleInvoiceMailState>}
|
||||
*/
|
||||
// public getSaleInvoiceMailState(
|
||||
// saleInvoiceid: number,
|
||||
// ): Promise<SaleInvoiceMailState> {
|
||||
// return this.getSaleInvoiceMailStateService.getInvoiceMailState(
|
||||
// saleInvoiceid,
|
||||
// );
|
||||
// }
|
||||
public getSaleInvoiceMailState(
|
||||
saleInvoiceid: number,
|
||||
): Promise<SaleInvoiceMailState> {
|
||||
return this.getSaleInvoiceMailStateService.getInvoiceMailState(
|
||||
saleInvoiceid,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import SaleInvoiceWriteoffSubscriber from './subscribers/SaleInvoiceWriteoffSubs
|
||||
import { SaleInvoiceWriteoffGLStorage } from './commands/writeoff/SaleInvoiceWriteoffGLStorage';
|
||||
import { InvoiceInventoryTransactions } from './commands/inventory/InvoiceInventoryTransactions';
|
||||
import { SendSaleEstimateMail } from '../SaleEstimates/commands/SendSaleEstimateMail';
|
||||
import { SendInvoiceMailReminder } from './commands/SendSaleInvoiceMailReminder';
|
||||
import { MailModule } from '../Mail/Mail.module';
|
||||
|
||||
@Module({
|
||||
@@ -84,7 +83,6 @@ import { MailModule } from '../Mail/Mail.module';
|
||||
SaleInvoiceWriteoffSubscriber,
|
||||
InvoiceInventoryTransactions,
|
||||
SendSaleEstimateMail,
|
||||
SendInvoiceMailReminder,
|
||||
],
|
||||
})
|
||||
export class SaleInvoicesModule {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import { GetSaleInvoice } from '../queries/GetSaleInvoice.service';
|
||||
import {
|
||||
DEFAULT_INVOICE_MAIL_CONTENT,
|
||||
@@ -8,6 +9,7 @@ import { GenerateShareLink } from './GenerateInvoicePaymentLink.service';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { SaleInvoice } from '../models/SaleInvoice';
|
||||
import { ContactMailNotification } from '@/modules/MailNotification/ContactMailNotification';
|
||||
import { SaleInvoiceMailOptions } from '../SaleInvoice.types';
|
||||
|
||||
@Injectable()
|
||||
export class SendSaleInvoiceMailCommon {
|
||||
@@ -15,7 +17,7 @@ export class SendSaleInvoiceMailCommon {
|
||||
private getSaleInvoiceService: GetSaleInvoice,
|
||||
private contactMailNotification: ContactMailNotification,
|
||||
private getInvoicePaymentMail: GetInvoicePaymentMail,
|
||||
private generatePaymentLinkService: GenerateShareLink,
|
||||
private generatePaymentLinkService: GenerateShareLink,
|
||||
|
||||
@Inject(SaleInvoice.name)
|
||||
private readonly saleInvoiceModel: typeof SaleInvoice,
|
||||
@@ -91,17 +93,17 @@ export class SendSaleInvoiceMailCommon {
|
||||
|
||||
/**
|
||||
* Retrieves the formatted text of the given sale invoice.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} invoiceId - Sale invoice id.
|
||||
* @param {string} text - The given text.
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
// @ts-nocheck
|
||||
public getInvoiceFormatterArgs = async (
|
||||
invoiceId: number,
|
||||
): Promise<Record<string, string | number>> => {
|
||||
const invoice = await this.getSaleInvoiceService.getSaleInvoice(invoiceId);
|
||||
const commonArgs =
|
||||
await this.contactMailNotification.getCommonFormatArgs(tenantId);
|
||||
const commonArgs = await this.contactMailNotification.getCommonFormatArgs();
|
||||
|
||||
return {
|
||||
...commonArgs,
|
||||
'Customer Name': invoice.customer.displayName,
|
||||
@@ -114,4 +116,3 @@ export class SendSaleInvoiceMailCommon {
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon.servic
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { mergeAndValidateMailOptions } from '@/modules/MailNotification/utils';
|
||||
import { SendInvoiceMailDTO } from '../SaleInvoice.types';
|
||||
import { SaleInvoiceMailOptions, SendInvoiceMailDTO } from '../SaleInvoice.types';
|
||||
import { ISaleInvoiceMailSend } from '../SaleInvoice.types';
|
||||
import { Mail } from '@/modules/Mail/Mail';
|
||||
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
DEFAULT_INVOICE_REMINDER_MAIL_CONTENT,
|
||||
DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT,
|
||||
} from '../constants';
|
||||
import { SaleInvoicePdf } from '../queries/SaleInvoicePdf.service';
|
||||
import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon.service';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { ISaleInvoiceMailSend, ISaleInvoiceMailSent, SendInvoiceMailDTO } from '../SaleInvoice.types';
|
||||
import { mergeAndValidateMailOptions } from '@/modules/MailNotification/utils';
|
||||
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
|
||||
import { Mail } from '@/modules/Mail/Mail';
|
||||
|
||||
@Injectable()
|
||||
export class SendInvoiceMailReminder {
|
||||
constructor(
|
||||
private readonly invoicePdf: SaleInvoicePdf,
|
||||
private readonly invoiceCommonMail: SendSaleInvoiceMailCommon,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly mailTransporter: MailTransporter,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Triggers the reminder mail of the given sale invoice.
|
||||
* @param {number} saleInvoiceId
|
||||
*/
|
||||
public async triggerMail(
|
||||
saleInvoiceId: number,
|
||||
messageOptions: SendInvoiceMailDTO,
|
||||
) {
|
||||
const payload = {
|
||||
saleInvoiceId,
|
||||
messageOptions,
|
||||
};
|
||||
// await this.agenda.now('sale-invoice-reminder-mail-send', payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the mail options of the given sale invoice.
|
||||
* @param {number} saleInvoiceId - The sale invocie id.
|
||||
* @returns {Promise<SaleInvoiceMailOptions>}
|
||||
*/
|
||||
public async getMailOption(saleInvoiceId: number) {
|
||||
return this.invoiceCommonMail.getMailOption(
|
||||
saleInvoiceId,
|
||||
DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT,
|
||||
DEFAULT_INVOICE_REMINDER_MAIL_CONTENT,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the mail invoice.
|
||||
* @param {number} saleInvoiceId - Sale invoice id.
|
||||
* @param {SendInvoiceMailDTO} messageOptions - The message options.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async sendMail(
|
||||
saleInvoiceId: number,
|
||||
messageOptions: SendInvoiceMailDTO,
|
||||
) {
|
||||
const localMessageOpts = await this.getMailOption(saleInvoiceId);
|
||||
|
||||
const messageOpts = mergeAndValidateMailOptions(
|
||||
localMessageOpts,
|
||||
messageOptions,
|
||||
);
|
||||
const mail = new Mail()
|
||||
.setSubject(messageOpts.subject)
|
||||
.setTo(messageOpts.to)
|
||||
.setContent(messageOpts.body);
|
||||
|
||||
if (messageOpts.attachInvoice) {
|
||||
// Retrieves document buffer of the invoice pdf document.
|
||||
const [invoicePdfBuffer, filename] = await this.invoicePdf.getSaleInvoicePdf(
|
||||
saleInvoiceId,
|
||||
);
|
||||
mail.setAttachments([
|
||||
{ filename, content: invoicePdfBuffer },
|
||||
]);
|
||||
}
|
||||
// Triggers the event `onSaleInvoiceSend`.
|
||||
await this.eventEmitter.emitAsync(events.saleInvoice.onMailReminderSend, {
|
||||
saleInvoiceId,
|
||||
messageOptions,
|
||||
} as ISaleInvoiceMailSend);
|
||||
|
||||
await this.mailTransporter.send(mail);
|
||||
|
||||
// Triggers the event `onSaleInvoiceSent`.
|
||||
await this.eventEmitter.emitAsync(events.saleInvoice.onMailReminderSent, {
|
||||
saleInvoiceId,
|
||||
messageOptions,
|
||||
} as ISaleInvoiceMailSent);
|
||||
}
|
||||
}
|
||||
@@ -4,20 +4,15 @@ import * as moment from 'moment';
|
||||
import * as R from 'ramda';
|
||||
import { MomentInput, unitOfTime } from 'moment';
|
||||
import { defaultTo } from 'ramda';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import ModelSetting from './ModelSetting';
|
||||
// import SaleInvoiceMeta from './SaleInvoice.Settings';
|
||||
// import CustomViewBaseModel from './CustomViewBaseModel';
|
||||
// import { DEFAULT_VIEWS } from '@/services/Sales/Invoices/constants';
|
||||
// import ModelSearchable from './ModelSearchable';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { TaxRateTransaction } from '@/modules/TaxRates/models/TaxRateTransaction.model';
|
||||
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
|
||||
import { Document } from '@/modules/ChromiumlyTenancy/models/Document';
|
||||
import { DiscountType } from '@/common/types/Discount';
|
||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||
import { ISearchRole } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
export class SaleInvoice extends BaseModel {
|
||||
export class SaleInvoice extends TenantBaseModel{
|
||||
public taxAmountWithheld: number;
|
||||
public balance: number;
|
||||
public paymentAmount: number;
|
||||
@@ -749,11 +744,11 @@ export class SaleInvoice extends BaseModel {
|
||||
/**
|
||||
* Model search attributes.
|
||||
*/
|
||||
static get searchRoles() {
|
||||
static get searchRoles(): ISearchRole[] {
|
||||
return [
|
||||
{ fieldKey: 'invoice_no', comparator: 'contains' },
|
||||
{ condition: 'or', fieldKey: 'reference_no', comparator: 'contains' },
|
||||
{ condition: 'or', fieldKey: 'amount', comparator: 'equals' },
|
||||
// { condition: 'or', fieldKey: 'reference_no', comparator: 'contains' },
|
||||
// { condition: 'or', fieldKey: 'amount', comparator: 'equals' },
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,46 +1,48 @@
|
||||
// import { Inject, Injectable } from '@nestjs/common';
|
||||
// import { GetSaleInvoiceMailStateTransformer } from './GetSaleInvoiceMailState.transformer';
|
||||
// import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
// import { SaleInvoice } from '../models/SaleInvoice';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { GetSaleInvoiceMailStateTransformer } from './GetSaleInvoiceMailState.transformer';
|
||||
import { SendSaleInvoiceMailCommon } from '../commands/SendInvoiceInvoiceMailCommon.service';
|
||||
import { SaleInvoice } from '../models/SaleInvoice';
|
||||
import { SaleInvoiceMailState } from '../SaleInvoice.types';
|
||||
|
||||
// @Injectable()
|
||||
// export class GetSaleInvoiceMailState {
|
||||
// constructor(
|
||||
// private transformer: TransformerInjectable,
|
||||
// // private invoiceMail: SendSaleInvoiceMailCommon,
|
||||
@Injectable()
|
||||
export class GetSaleInvoiceMailState {
|
||||
constructor(
|
||||
private transformer: TransformerInjectable,
|
||||
private invoiceMail: SendSaleInvoiceMailCommon,
|
||||
|
||||
// @Inject(SaleInvoice.name)
|
||||
// private saleInvoiceModel: typeof SaleInvoice,
|
||||
// ) {}
|
||||
@Inject(SaleInvoice.name)
|
||||
private saleInvoiceModel: typeof SaleInvoice,
|
||||
) {}
|
||||
|
||||
// /**
|
||||
// * Retrieves the invoice mail state of the given sale invoice.
|
||||
// * Invoice mail state includes the mail options, branding attributes and the invoice details.
|
||||
// * @param {number} saleInvoiceId - Sale invoice id.
|
||||
// * @returns {Promise<SaleInvoiceMailState>}
|
||||
// */
|
||||
// async getInvoiceMailState(
|
||||
// saleInvoiceId: number,
|
||||
// ): Promise<SaleInvoiceMailState> {
|
||||
// const saleInvoice = await this.saleInvoiceModel
|
||||
// .query()
|
||||
// .findById(saleInvoiceId)
|
||||
// .withGraphFetched('customer')
|
||||
// .withGraphFetched('entries.item')
|
||||
// .withGraphFetched('pdfTemplate')
|
||||
// .throwIfNotFound();
|
||||
/**
|
||||
* Retrieves the invoice mail state of the given sale invoice.
|
||||
* Invoice mail state includes the mail options, branding attributes and the invoice details.
|
||||
* @param {number} saleInvoiceId - Sale invoice id.
|
||||
* @returns {Promise<SaleInvoiceMailState>}
|
||||
*/
|
||||
public async getInvoiceMailState(
|
||||
saleInvoiceId: number,
|
||||
): Promise<SaleInvoiceMailState> {
|
||||
const saleInvoice = await this.saleInvoiceModel
|
||||
.query()
|
||||
.findById(saleInvoiceId)
|
||||
.withGraphFetched('customer')
|
||||
.withGraphFetched('entries.item')
|
||||
.withGraphFetched('pdfTemplate')
|
||||
.throwIfNotFound();
|
||||
|
||||
// const mailOptions =
|
||||
// await this.invoiceMail.getInvoiceMailOptions(saleInvoiceId);
|
||||
const mailOptions =
|
||||
await this.invoiceMail.getInvoiceMailOptions(saleInvoiceId);
|
||||
|
||||
// // Transforms the sale invoice mail state.
|
||||
// const transformed = await this.transformer.transform(
|
||||
// saleInvoice,
|
||||
// new GetSaleInvoiceMailStateTransformer(),
|
||||
// {
|
||||
// mailOptions,
|
||||
// },
|
||||
// );
|
||||
// return transformed;
|
||||
// }
|
||||
// }
|
||||
// Transforms the sale invoice mail state.
|
||||
const transformed = await this.transformer.transform(
|
||||
saleInvoice,
|
||||
new GetSaleInvoiceMailStateTransformer(),
|
||||
{
|
||||
mailOptions,
|
||||
},
|
||||
);
|
||||
return transformed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service
|
||||
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
|
||||
import { SaleInvoice } from '../models/SaleInvoice';
|
||||
import { ISalesInvoicesFilter } from '../SaleInvoice.types';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
@Injectable()
|
||||
export class GetSaleInvoicesService {
|
||||
@@ -36,8 +37,9 @@ export class GetSaleInvoicesService {
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('entries.item');
|
||||
builder.withGraphFetched('customer');
|
||||
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
filterDTO?.filterQuery && filterDTO?.filterQuery(builder);
|
||||
filterDTO?.filterQuery?.(builder as any);
|
||||
})
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { Model, mixin } from 'objection';
|
||||
import { defaultTo } from 'ramda';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import ModelSetting from './ModelSetting';
|
||||
// import SaleReceiptSettings from './SaleReceipt.Settings';
|
||||
// import CustomViewBaseModel from './CustomViewBaseModel';
|
||||
// import { DEFAULT_VIEWS } from '@/services/Sales/Receipts/constants';
|
||||
// import ModelSearchable from './ModelSearchable';
|
||||
import { Model } from 'objection';
|
||||
import { defaultTo } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
|
||||
import { Customer } from '@/modules/Customers/models/Customer';
|
||||
@@ -13,38 +8,48 @@ import { AccountTransaction } from '@/modules/Accounts/models/AccountTransaction
|
||||
import { Branch } from '@/modules/Branches/models/Branch.model';
|
||||
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
|
||||
import { DiscountType } from '@/common/types/Discount';
|
||||
import { MetadataModelMixin } from '@/modules/DynamicListing/models/MetadataModel';
|
||||
import { ResourceableModelMixin } from '@/modules/Resource/models/ResourcableModel';
|
||||
import { CustomViewBaseModelMixin } from '@/modules/CustomViews/CustomViewBaseModel';
|
||||
import { SearchableBaseModelMixin } from '@/modules/DynamicListing/models/SearchableBaseModel';
|
||||
|
||||
export class SaleReceipt extends BaseModel {
|
||||
amount: number;
|
||||
exchangeRate: number;
|
||||
currencyCode: string;
|
||||
depositAccountId: number;
|
||||
customerId: number;
|
||||
receiptDate: Date;
|
||||
receiptNumber: string;
|
||||
referenceNo: string;
|
||||
sendToEmail: string;
|
||||
receiptMessage: string;
|
||||
statement: string;
|
||||
closedAt: Date | string;
|
||||
const ExtendedModel = R.pipe(
|
||||
CustomViewBaseModelMixin,
|
||||
SearchableBaseModelMixin,
|
||||
ResourceableModelMixin,
|
||||
MetadataModelMixin,
|
||||
)(BaseModel);
|
||||
|
||||
discountType: DiscountType;
|
||||
discount: number;
|
||||
adjustment: number;
|
||||
export class SaleReceipt extends ExtendedModel {
|
||||
public amount!: number;
|
||||
public exchangeRate!: number;
|
||||
public currencyCode!: string;
|
||||
public depositAccountId!: number;
|
||||
public customerId!: number;
|
||||
public receiptDate!: Date;
|
||||
public receiptNumber!: string;
|
||||
public referenceNo!: string;
|
||||
public sendToEmail!: string;
|
||||
public receiptMessage!: string;
|
||||
public statement!: string;
|
||||
public closedAt!: Date | string;
|
||||
public discountType!: DiscountType;
|
||||
public discount!: number;
|
||||
public adjustment!: number;
|
||||
|
||||
branchId: number;
|
||||
warehouseId: number;
|
||||
public branchId!: number;
|
||||
public warehouseId!: number;
|
||||
|
||||
userId: number;
|
||||
public userId!: number;
|
||||
|
||||
createdAt: Date;
|
||||
updatedAt: Date | null;
|
||||
public createdAt!: Date;
|
||||
public updatedAt!: Date | null;
|
||||
|
||||
customer!: Customer;
|
||||
entries!: ItemEntry[];
|
||||
transactions!: AccountTransaction[];
|
||||
branch!: Branch;
|
||||
warehouse!: Warehouse;
|
||||
public customer!: Customer;
|
||||
public entries!: ItemEntry[];
|
||||
public transactions!: AccountTransaction[];
|
||||
public branch!: Branch;
|
||||
public warehouse!: Warehouse;
|
||||
|
||||
/**
|
||||
* Table name
|
||||
@@ -142,7 +147,7 @@ export class SaleReceipt extends BaseModel {
|
||||
* Receipt total.
|
||||
* @returns {number}
|
||||
*/
|
||||
get total() {
|
||||
get total(): number {
|
||||
const adjustmentAmount = defaultTo(this.adjustment, 0);
|
||||
|
||||
return this.subtotal - this.discountAmount + adjustmentAmount;
|
||||
@@ -256,6 +261,9 @@ export class SaleReceipt extends BaseModel {
|
||||
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
|
||||
|
||||
return {
|
||||
/**
|
||||
* Sale receipt may has a customer.
|
||||
*/
|
||||
customer: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: Customer,
|
||||
@@ -268,6 +276,9 @@ export class SaleReceipt extends BaseModel {
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Sale receipt may has a deposit account.
|
||||
*/
|
||||
depositAccount: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: Account,
|
||||
@@ -277,6 +288,9 @@ export class SaleReceipt extends BaseModel {
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Sale receipt may has many items entries.
|
||||
*/
|
||||
entries: {
|
||||
relation: Model.HasManyRelation,
|
||||
modelClass: ItemEntry,
|
||||
@@ -290,6 +304,9 @@ export class SaleReceipt extends BaseModel {
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Sale receipt may has many transactions.
|
||||
*/
|
||||
transactions: {
|
||||
relation: Model.HasManyRelation,
|
||||
modelClass: AccountTransaction,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import * as R from 'ramda';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { CustomViewBaseModelMixin } from '@/modules/CustomViews/CustomViewBaseModel';
|
||||
import { MetadataModelMixin } from '@/modules/DynamicListing/models/MetadataModel';
|
||||
import { SearchableBaseModelMixin } from '@/modules/DynamicListing/models/SearchableBaseModel';
|
||||
import { ResourceableModelMixin } from '@/modules/Resource/models/ResourcableModel';
|
||||
|
||||
const ExtendedItem = R.pipe(
|
||||
CustomViewBaseModelMixin,
|
||||
SearchableBaseModelMixin,
|
||||
ResourceableModelMixin,
|
||||
MetadataModelMixin,
|
||||
)(BaseModel);
|
||||
|
||||
export class TenantBaseModel extends ExtendedItem {}
|
||||
@@ -180,7 +180,7 @@ export class Transformer<T = {}, ExtraContext = {}> {
|
||||
* @param {string} date
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formatDateFromNow(date: string) {
|
||||
protected formatDateFromNow(date: moment.MomentInput) {
|
||||
return date ? moment(date).fromNow(true) : '';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
import { Model, raw, mixin } from 'objection';
|
||||
// import TenantModel from 'models/TenantModel';
|
||||
// import BillSettings from './Bill.Settings';
|
||||
// import ModelSetting from './ModelSetting';
|
||||
// import CustomViewBaseModel from './CustomViewBaseModel';
|
||||
// import { DEFAULT_VIEWS } from '@/services/Purchases/VendorCredits/constants';
|
||||
// import ModelSearchable from './ModelSearchable';
|
||||
// import VendorCreditMeta from './VendorCredit.Meta';
|
||||
// import { DiscountType } from '@/interfaces';
|
||||
import { Model, raw } from 'objection';
|
||||
import { Vendor } from '@/modules/Vendors/models/Vendor';
|
||||
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
|
||||
import { Branch } from '@/modules/Branches/models/Branch.model';
|
||||
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { DiscountType } from '@/common/types/Discount';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
export class VendorCredit extends BaseModel {
|
||||
export class VendorCredit extends TenantBaseModel {
|
||||
vendorId: number;
|
||||
amount: number;
|
||||
currencyCode: string;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
|
||||
import { VendorCreditApplyBillsApplicationService } from './VendorCreditApplyBillsApplication.service';
|
||||
import { IVendorCreditApplyToInvoicesDTO } from './types/VendorCreditApplyBills.types';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@Controller('vendor-credits')
|
||||
@ApiTags('vendor-credits-apply-bills')
|
||||
export class VendorCreditApplyBillsController {
|
||||
constructor(
|
||||
private readonly vendorCreditApplyBillsApplication: VendorCreditApplyBillsApplicationService,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Model, mixin } from 'objection';
|
||||
// import { DEFAULT_VIEWS } from '@/services/Contacts/Vendors/constants';
|
||||
// import ModelSearchable from './ModelSearchable';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
// class VendorQueryBuilder extends PaginationQueryBuilder {
|
||||
// constructor(...args) {
|
||||
@@ -20,7 +21,7 @@ import { BaseModel } from '@/models/Model';
|
||||
// }
|
||||
// }
|
||||
|
||||
export class Vendor extends BaseModel {
|
||||
export class Vendor extends TenantBaseModel {
|
||||
contactService: string;
|
||||
contactType: string;
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@ import {
|
||||
import { WarehousesApplication } from './WarehousesApplication.service';
|
||||
import { ICreateWarehouseDTO, IEditWarehouseDTO } from './Warehouse.types';
|
||||
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@Controller('warehouses')
|
||||
@ApiTags('warehouses')
|
||||
@PublicRoute()
|
||||
export class WarehousesController {
|
||||
constructor(private warehousesApplication: WarehousesApplication) {}
|
||||
|
||||
16
packages/server-nest/src/utils/format-message.ts
Normal file
16
packages/server-nest/src/utils/format-message.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { defaultTo } from 'lodash';
|
||||
|
||||
export const formatMessage = (message: string, args: Record<string, any>) => {
|
||||
let formattedMessage = message;
|
||||
|
||||
Object.keys(args).forEach((key) => {
|
||||
const variable = `{${key}}`;
|
||||
const value = defaultTo(args[key], '');
|
||||
|
||||
formattedMessage = formattedMessage.replace(
|
||||
new RegExp(variable, 'g'),
|
||||
value
|
||||
);
|
||||
});
|
||||
return formattedMessage;
|
||||
};
|
||||
Reference in New Issue
Block a user