refactor: inventory to nestjs

This commit is contained in:
Ahmed Bouhuolia
2025-01-15 14:14:44 +02:00
parent e7e7a95aa1
commit 936800600b
32 changed files with 455 additions and 227 deletions

View File

@@ -15,12 +15,14 @@ import { GetAccountTypesService } from './GetAccountTypes.service';
import { GetAccountTransactionsService } from './GetAccountTransactions.service';
import { RegisterTenancyModel } from '../Tenancy/TenancyModels/Tenancy.module';
import { BankAccount } from '../BankingTransactions/models/BankAccount';
import { GetAccountsService } from './GetAccounts.service';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
// import { GetAccountsService } from './GetAccounts.service';
const models = [RegisterTenancyModel(BankAccount)];
@Module({
imports: [TenancyDatabaseModule],
imports: [TenancyDatabaseModule, DynamicListModule],
controllers: [AccountsController],
providers: [
AccountsApplication,
@@ -35,6 +37,7 @@ const models = [RegisterTenancyModel(BankAccount)];
ActivateAccount,
GetAccountTypesService,
GetAccountTransactionsService,
GetAccountsService,
...models,
],
exports: [AccountRepository, CreateAccountService, ...models],

View File

@@ -41,6 +41,7 @@ export interface IAccountEventCreatingPayload {
accountDTO: any;
trx: Knex.Transaction;
}
export interface IAccountEventCreatedPayload {
account: Account;
accountId: number;

View File

@@ -1,14 +1,11 @@
import { Inject, Injectable } from '@nestjs/common';
import { kebabCase } from 'lodash';
import { Knex } from 'knex';
import { EventEmitter2 } from '@nestjs/event-emitter';
import {
// IAccount,
// IAccountEventCreatedPayload,
// IAccountCreateDTO,
IAccountEventCreatingPayload,
CreateAccountParams,
IAccountEventCreatedPayload,
} from './Accounts.types';
import { CommandAccountValidators } from './CommandAccountValidators.service';
import { Account } from './models/Account.model';
@@ -16,6 +13,7 @@ import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
import { events } from '@/common/events/events';
import { CreateAccountDTO } from './CreateAccount.dto';
import { PartialModelObject } from 'objection';
@Injectable()
export class CreateAccountService {
@@ -80,7 +78,7 @@ export class CreateAccountService {
private transformDTOToModel = (
createAccountDTO: CreateAccountDTO,
baseCurrency: string,
) => {
): PartialModelObject<Account> => {
return {
...createAccountDTO,
slug: kebabCase(createAccountDTO.name),
@@ -127,11 +125,11 @@ export class CreateAccountService {
...accountInputModel,
});
// Triggers `onAccountCreated` event.
// await this.eventEmitter.emitAsync(events.accounts.onCreated, {
// account,
// accountId: account.id,
// trx,
// } as IAccountEventCreatedPayload);
await this.eventEmitter.emitAsync(events.accounts.onCreated, {
account,
accountId: account.id,
trx,
} as IAccountEventCreatedPayload);
return account;
}, trx);

View File

@@ -33,9 +33,10 @@ export class Account extends TenantBaseModel {
public bankBalance!: number;
public lastFeedsUpdatedAt!: string | Date | null;
public amount!: number;
public plaidItemId!: number;
public plaidItemId!: string;
public plaidAccountId!: string | null;
public isFeedsActive!: boolean;
public isSyncingOwner!: boolean;
public plaidItem!: PlaidItem;
/**

View File

@@ -18,7 +18,7 @@ import { BankRulesModule } from '../BankRules/BankRules.module';
RefreshBankAccountService,
ResumeBankAccountFeedsService,
PauseBankAccountFeeds,
// DeleteUncategorizedTransactionsOnAccountDeleting,
DeleteUncategorizedTransactionsOnAccountDeleting,
DisconnectPlaidItemOnAccountDeleted,
BankAccountsApplication
],

View File

@@ -4,7 +4,7 @@ import { UncategorizedBankTransaction } from './models/UncategorizedBankTransact
import { BankTransactionLine } from './models/BankTransactionLine';
import { BankTransaction } from './models/BankTransaction';
import { BankTransactionAutoIncrement } from './commands/BankTransactionAutoIncrement.service';
import BankingTransactionGLEntriesSubscriber from './subscribers/CashflowTransactionSubscriber';
import { BankingTransactionGLEntriesSubscriber } from './subscribers/CashflowTransactionSubscriber';
import { DecrementUncategorizedTransactionOnCategorizeSubscriber } from './subscribers/DecrementUncategorizedTransactionOnCategorize';
import { DeleteCashflowTransactionOnUncategorizeSubscriber } from './subscribers/DeleteCashflowTransactionOnUncategorize';
import { PreventDeleteTransactionOnDeleteSubscriber } from './subscribers/PreventDeleteTransactionsOnDelete';
@@ -21,15 +21,24 @@ import { BranchTransactionDTOTransformer } from '../Branches/integrations/Branch
import { BranchesModule } from '../Branches/Branches.module';
import { RemovePendingUncategorizedTransaction } from './commands/RemovePendingUncategorizedTransaction.service';
import { BankingTransactionsController } from './BankingTransactions.controller';
import { GetBankAccountsService } from './queries/GetBankAccounts.service';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
import { BankAccount } from './models/BankAccount';
const models = [
RegisterTenancyModel(UncategorizedBankTransaction),
RegisterTenancyModel(BankTransaction),
RegisterTenancyModel(BankTransactionLine),
RegisterTenancyModel(BankAccount),
];
@Module({
imports: [AutoIncrementOrdersModule, LedgerModule, BranchesModule],
imports: [
AutoIncrementOrdersModule,
LedgerModule,
BranchesModule,
DynamicListModule,
],
controllers: [BankingTransactionsController],
providers: [
BankTransactionAutoIncrement,
@@ -43,6 +52,7 @@ const models = [
DeleteCashflowTransaction,
CreateBankTransactionService,
GetBankTransactionService,
GetBankAccountsService,
CommandBankTransactionValidator,
BranchTransactionDTOTransformer,
RemovePendingUncategorizedTransaction,

View File

@@ -45,7 +45,7 @@ export class BankAccount extends TenantBaseModel {
/**
* Retrieve account type label.
*/
get accountTypeLabel() {
get accountTypeLabel(): string {
return AccountTypesUtils.getType(this.accountType, 'label');
}

View File

@@ -1,11 +1,4 @@
/* eslint-disable global-require */
import { Model } from 'objection';
// import {
// getCashflowAccountTransactionsTypes,
// getCashflowTransactionType,
// } from '@/services/Cashflow/utils';
// import { CASHFLOW_DIRECTION } from '@/services/Cashflow/constants';
// import { getCashflowTransactionFormattedType } from '@/utils/transactions-types';
import { BaseModel } from '@/models/Model';
import {
getCashflowAccountTransactionsTypes,

View File

@@ -1,9 +1,8 @@
/* eslint-disable global-require */
import { Model } from 'objection';
// import TenantModel from 'models/TenantModel';
import { BaseModel } from '@/models/Model';
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
export class BankTransactionLine extends BaseModel{
export class BankTransactionLine extends TenantBaseModel{
/**
* Table name.
*/
@@ -18,6 +17,14 @@ export class BankTransactionLine extends BaseModel{
return ['createdAt', 'updatedAt'];
}
/**
* Determine whether the model is resourceable.
* @returns {boolean}
*/
static get resourceable(): boolean {
return false;
}
/**
* Relationship mapping.
*/

View File

@@ -6,7 +6,7 @@ import { events } from '@/common/events/events';
import { ICommandCashflowCreatedPayload, ICommandCashflowDeletedPayload } from '../types/BankingTransactions.types';
@Injectable()
export default class BankingTransactionGLEntriesSubscriber {
export class BankingTransactionGLEntriesSubscriber {
/**
* @param {BankTransactionGLEntriesService} bankTransactionGLEntries - Bank transaction GL entries service.
* @param {BankTransactionAutoIncrement} cashflowTransactionAutoIncrement - Cashflow transaction auto increment service.

View File

@@ -23,9 +23,18 @@ import { LedgerModule } from '../Ledger/Ledger.module';
import { AccountsModule } from '../Accounts/Accounts.module';
import { BillWriteInventoryTransactionsSubscriber } from './subscribers/BillWriteInventoryTransactionsSubscriber';
import { BillInventoryTransactions } from './commands/BillInventoryTransactions';
import { GetBillsService } from './queries/GetBills.service';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
import { InventoryCostModule } from '../InventoryCost/InventoryCost.module';
@Module({
imports: [BillLandedCostsModule, LedgerModule, AccountsModule],
imports: [
BillLandedCostsModule,
LedgerModule,
AccountsModule,
DynamicListModule,
InventoryCostModule,
],
providers: [
TenancyContext,
BillsApplication,
@@ -39,6 +48,7 @@ import { BillInventoryTransactions } from './commands/BillInventoryTransactions'
GetDueBills,
OpenBillService,
GetBill,
GetBillsService,
DeleteBill,
BillDTOTransformer,
BillsValidators,

View File

@@ -13,8 +13,9 @@ import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
import { BillLandedCost } from '@/modules/BillLandedCosts/models/BillLandedCost';
import { DiscountType } from '@/common/types/Discount';
import type { Knex, QueryBuilder } from 'knex';
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
export class Bill extends BaseModel {
export class Bill extends TenantBaseModel {
public amount: number;
public paymentAmount: number;
public landedCostAmount: number;
@@ -95,7 +96,7 @@ export class Bill extends BaseModel {
* Invoice amount in base currency.
* @returns {number}
*/
get amountLocal() {
get amountLocal(): number {
return this.amount * this.exchangeRate;
}
@@ -103,7 +104,7 @@ export class Bill extends BaseModel {
* Subtotal. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number}
*/
get subtotal() {
get subtotal(): number {
return this.amount;
}
@@ -111,7 +112,7 @@ export class Bill extends BaseModel {
* Subtotal in base currency. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number}
*/
get subtotalLocal() {
get subtotalLocal(): number {
return this.amountLocal;
}
@@ -119,7 +120,7 @@ export class Bill extends BaseModel {
* Sale invoice amount excluding tax.
* @returns {number}
*/
get subtotalExcludingTax() {
get subtotalExcludingTax(): number {
return this.isInclusiveTax
? this.subtotal - this.taxAmountWithheld
: this.subtotal;
@@ -129,7 +130,7 @@ export class Bill extends BaseModel {
* Tax amount withheld in base currency.
* @returns {number}
*/
get taxAmountWithheldLocal() {
get taxAmountWithheldLocal(): number {
return this.taxAmountWithheld * this.exchangeRate;
}
@@ -137,7 +138,7 @@ export class Bill extends BaseModel {
* Discount amount.
* @returns {number}
*/
get discountAmount() {
get discountAmount(): number {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
@@ -147,7 +148,7 @@ export class Bill extends BaseModel {
* Discount amount in local currency.
* @returns {number | null}
*/
get discountAmountLocal() {
get discountAmountLocal(): number | null {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
@@ -190,6 +191,132 @@ export class Bill extends BaseModel {
return this.total * this.exchangeRate;
}
/**
* Invoice amount in organization base currency.
* @deprecated
* @returns {number}
*/
get localAmount(): number {
return this.amountLocal;
}
/**
* Retrieves the local allocated cost amount.
* @returns {number}
*/
get localAllocatedCostAmount(): number {
return this.allocatedCostAmount * this.exchangeRate;
}
/**
* Retrieves the local landed cost amount.
* @returns {number}
*/
get localLandedCostAmount(): number {
return this.landedCostAmount * this.exchangeRate;
}
/**
* Retrieves the local unallocated cost amount.
* @returns {number}
*/
get localUnallocatedCostAmount(): number {
return this.unallocatedCostAmount * this.exchangeRate;
}
/**
* Retrieve the balance of bill.
* @return {number}
*/
get balance(): number {
return this.paymentAmount + this.creditedAmount;
}
/**
* Due amount of the given.
* @return {number}
*/
get dueAmount(): number {
return Math.max(this.total - this.balance, 0);
}
/**
* Detarmine whether the bill is open.
* @return {boolean}
*/
get isOpen(): boolean {
return !!this.openedAt;
}
/**
* Deetarmine whether the bill paid partially.
* @return {boolean}
*/
get isPartiallyPaid(): boolean {
return this.dueAmount !== this.total && this.dueAmount > 0;
}
/**
* Deetarmine whether the bill paid fully.
* @return {boolean}
*/
get isFullyPaid(): boolean {
return this.dueAmount === 0;
}
/**
* Detarmines whether the bill paid fully or partially.
* @return {boolean}
*/
get isPaid(): boolean {
return this.isPartiallyPaid || this.isFullyPaid;
}
/**
* Retrieve the remaining days in number
* @return {number|null}
*/
get remainingDays(): number | null {
const currentMoment = moment();
const dueDateMoment = moment(this.dueDate);
return Math.max(dueDateMoment.diff(currentMoment, 'days'), 0);
}
/**
* Retrieve the overdue days in number.
* @return {number|null}
*/
get overdueDays(): number | null {
const currentMoment = moment();
const dueDateMoment = moment(this.dueDate);
return Math.max(currentMoment.diff(dueDateMoment, 'days'), 0);
}
/**
* Detarmines the due date is over.
* @return {boolean}
*/
get isOverdue(): boolean {
return this.overdueDays > 0;
}
/**
* Retrieve the unallocated cost amount.
* @return {number}
*/
get unallocatedCostAmount(): number {
return Math.max(this.landedCostAmount - this.allocatedCostAmount, 0);
}
/**
* Retrieves the calculated amount which have not been invoiced.
*/
get billableAmount(): number {
return Math.max(this.total - this.invoicedAmount, 0);
}
/**
* Table name
*/
@@ -335,132 +462,6 @@ export class Bill extends BaseModel {
};
}
/**
* Invoice amount in organization base currency.
* @deprecated
* @returns {number}
*/
get localAmount() {
return this.amountLocal;
}
/**
* Retrieves the local allocated cost amount.
* @returns {number}
*/
get localAllocatedCostAmount() {
return this.allocatedCostAmount * this.exchangeRate;
}
/**
* Retrieves the local landed cost amount.
* @returns {number}
*/
get localLandedCostAmount() {
return this.landedCostAmount * this.exchangeRate;
}
/**
* Retrieves the local unallocated cost amount.
* @returns {number}
*/
get localUnallocatedCostAmount() {
return this.unallocatedCostAmount * this.exchangeRate;
}
/**
* Retrieve the balance of bill.
* @return {number}
*/
get balance() {
return this.paymentAmount + this.creditedAmount;
}
/**
* Due amount of the given.
* @return {number}
*/
get dueAmount() {
return Math.max(this.total - this.balance, 0);
}
/**
* Detarmine whether the bill is open.
* @return {boolean}
*/
get isOpen() {
return !!this.openedAt;
}
/**
* Deetarmine whether the bill paid partially.
* @return {boolean}
*/
get isPartiallyPaid() {
return this.dueAmount !== this.total && this.dueAmount > 0;
}
/**
* Deetarmine whether the bill paid fully.
* @return {boolean}
*/
get isFullyPaid() {
return this.dueAmount === 0;
}
/**
* Detarmines whether the bill paid fully or partially.
* @return {boolean}
*/
get isPaid() {
return this.isPartiallyPaid || this.isFullyPaid;
}
/**
* Retrieve the remaining days in number
* @return {number|null}
*/
get remainingDays() {
const currentMoment = moment();
const dueDateMoment = moment(this.dueDate);
return Math.max(dueDateMoment.diff(currentMoment, 'days'), 0);
}
/**
* Retrieve the overdue days in number.
* @return {number|null}
*/
get overdueDays() {
const currentMoment = moment();
const dueDateMoment = moment(this.dueDate);
return Math.max(currentMoment.diff(dueDateMoment, 'days'), 0);
}
/**
* Detarmines the due date is over.
* @return {boolean}
*/
get isOverdue() {
return this.overdueDays > 0;
}
/**
* Retrieve the unallocated cost amount.
* @return {number}
*/
get unallocatedCostAmount() {
return Math.max(this.landedCostAmount - this.allocatedCostAmount, 0);
}
/**
* Retrieves the calculated amount which have not been invoiced.
*/
get billableAmount() {
return Math.max(this.total - this.invoicedAmount, 0);
}
/**
* Bill model settings.
*/
@@ -482,7 +483,9 @@ export class Bill extends BaseModel {
const { Branch } = require('../../Branches/models/Branch.model');
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
const { TaxRateModel } = require('../../TaxRates/models/TaxRate.model');
const { TaxRateTransaction } = require('../../TaxRates/models/TaxRateTransaction.model');
const {
TaxRateTransaction,
} = require('../../TaxRates/models/TaxRateTransaction.model');
const { Document } = require('../../ChromiumlyTenancy/models/Document');
// const { MatchedBankTransaction } = require('models/MatchedBankTransaction');

View File

@@ -16,9 +16,10 @@ import { ExpenseGLEntriesService } from './subscribers/ExpenseGLEntries.service'
import { LedgerModule } from '../Ledger/Ledger.module';
import { BranchesModule } from '../Branches/Branches.module';
import { GetExpensesService } from './queries/GetExpenses.service';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
@Module({
imports: [LedgerModule, BranchesModule],
imports: [LedgerModule, BranchesModule, DynamicListModule],
controllers: [ExpensesController],
exports: [
CreateExpense,

View File

@@ -5,7 +5,7 @@ import { InventoryAdjustmentEntry } from './models/InventoryAdjustmentEntry';
import { CreateQuickInventoryAdjustmentService } from './commands/CreateQuickInventoryAdjustment.service';
import { PublishInventoryAdjustmentService } from './commands/PublishInventoryAdjustment.service';
import { GetInventoryAdjustmentService } from './queries/GetInventoryAdjustment.service';
// import { GetInventoryAdjustmentsService } from './queries/GetInventoryAdjustments.service';
import { GetInventoryAdjustmentsService } from './queries/GetInventoryAdjustments.service';
import { DeleteInventoryAdjustmentService } from './commands/DeleteInventoryAdjustment.service';
import { InventoryAdjustmentsApplicationService } from './InventoryAdjustmentsApplication.service';
import { InventoryAdjustmentsController } from './InventoryAdjustments.controller';
@@ -13,23 +13,24 @@ import { BranchesModule } from '../Branches/Branches.module';
import { WarehousesModule } from '../Warehouses/Warehouses.module';
import { InventoryAdjustmentsGLSubscriber } from './subscribers/InventoryAdjustmentGL.subscriber';
import { InventoryAdjustmentsGLEntries } from './commands/ledger/InventoryAdjustmentsGLEntries';
import { LedgerModule } from '../Ledger/Ledger.module';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
import { InventoryAdjustmentInventoryTransactionsSubscriber } from './inventory/InventoryAdjustmentInventoryTransactionsSubscriber';
import { InventoryAdjustmentInventoryTransactions } from './inventory/InventoryAdjustmentInventoryTransactions';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
import { LedgerModule } from '../Ledger/Ledger.module';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
const models = [
RegisterTenancyModel(InventoryAdjustment),
RegisterTenancyModel(InventoryAdjustmentEntry),
];
@Module({
imports: [BranchesModule, WarehousesModule, LedgerModule],
imports: [BranchesModule, WarehousesModule, LedgerModule, DynamicListModule],
controllers: [InventoryAdjustmentsController],
providers: [
...models,
CreateQuickInventoryAdjustmentService,
PublishInventoryAdjustmentService,
// GetInventoryAdjustmentsService,
GetInventoryAdjustmentsService,
GetInventoryAdjustmentService,
DeleteInventoryAdjustmentService,
InventoryAdjustmentsApplicationService,

View File

@@ -1,5 +1,5 @@
import { Knex } from 'knex';
import { Inject } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import * as R from 'ramda';
import * as moment from 'moment';
import { omit } from 'lodash';
@@ -20,6 +20,7 @@ import { WarehouseTransactionDTOTransform } from '@/modules/Warehouses/Integrati
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
import { ERRORS } from '../constants/InventoryAdjustments.constants';
@Injectable()
export class CreateQuickInventoryAdjustmentService {
constructor(
@Inject(InventoryAdjustment.name)

View File

@@ -1,4 +1,4 @@
import { Inject } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
@@ -10,6 +10,7 @@ import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { InventoryAdjustmentEntry } from '../models/InventoryAdjustmentEntry';
import { InventoryAdjustment } from '../models/InventoryAdjustment';
@Injectable()
export class DeleteInventoryAdjustmentService {
constructor(
private readonly eventEmitter: EventEmitter2,

View File

@@ -1,5 +1,5 @@
import { Knex } from 'knex';
import { Inject } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import * as moment from 'moment';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
@@ -12,6 +12,7 @@ import { events } from '@/common/events/events';
import { ServiceError } from '@/modules/Items/ServiceError';
import { ERRORS } from '../constants/InventoryAdjustments.constants';
@Injectable()
export class PublishInventoryAdjustmentService {
constructor(
private readonly eventEmitter: EventEmitter2,

View File

@@ -1,10 +1,11 @@
import { Knex } from 'knex';
import { Inject } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import { LedgerStorageService } from '../../../Ledger/LedgerStorage.service';
import { InventoryAdjustment } from '../../models/InventoryAdjustment';
import { TenancyContext } from '../../../Tenancy/TenancyContext.service';
import { InventoryAdjustmentsGL } from './InventoryAdjustmentGL';
@Injectable()
export class InventoryAdjustmentsGLEntries {
constructor(
private readonly ledgerStorage: LedgerStorageService,

View File

@@ -1,13 +1,13 @@
import { Injectable } from "@nestjs/common";
import { Knex } from "knex";
import { InventoryAdjustment } from "../models/InventoryAdjustment";
import { InventoryTransaction } from "@/modules/InventoryCost/models/InventoryTransaction";
import { InventoryService } from "@/modules/InventoryCost/Inventory";
import { Injectable } from "@nestjs/common";
import { InventoryTransactionsService } from "@/modules/InventoryCost/InventoryTransactions.service";
@Injectable()
export class InventoryAdjustmentInventoryTransactions {
constructor(
private readonly inventoryService: InventoryService
private readonly inventoryService: InventoryTransactionsService
) {}
/**

View File

@@ -3,21 +3,21 @@ import { InventoryAdjustmentEntry } from './InventoryAdjustmentEntry';
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
export class InventoryAdjustment extends TenantBaseModel {
date!: string;
type!: string;
adjustmentAccountId!: number;
reason?: string;
referenceNo!: string;
description?: string;
userId!: number;
publishedAt?: string;
public readonly date!: string;
public readonly type!: string;
public readonly adjustmentAccountId!: number;
public readonly reason?: string;
public readonly referenceNo!: string;
public readonly description?: string;
public readonly userId!: number;
public readonly publishedAt?: string;
branchId!: number;
warehouseId!: number;
public readonly branchId!: number;
public readonly warehouseId!: number;
createdAt!: Date | string;
public readonly createdAt!: Date | string;
entries: InventoryAdjustmentEntry[];
public readonly entries: InventoryAdjustmentEntry[];
/**
* Table name

View File

@@ -1,8 +1,9 @@
import { Inject, Injectable } from '@nestjs/common';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { InventoryAdjustment } from '../models/InventoryAdjustment';
import { InventoryAdjustmentTransformer } from '../InventoryAdjustmentTransformer';
import { Inject } from '@nestjs/common';
@Injectable()
export class GetInventoryAdjustmentService {
constructor(
private readonly transformer: TransformerInjectable,

View File

@@ -1,4 +1,4 @@
import { Inject } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import * as R from 'ramda';
import { IPaginationMeta } from '@/interfaces/Model';
import { InventoryAdjustmentTransformer } from '../InventoryAdjustmentTransformer';
@@ -7,9 +7,10 @@ import { IInventoryAdjustmentsFilter } from '../types/InventoryAdjustments.types
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
@Injectable()
export class GetInventoryAdjustmentsService {
constructor(
public readonly transformer: TransformerInjectable,
private readonly transformer: TransformerInjectable,
private readonly dynamicListService: DynamicListService,
@Inject(InventoryAdjustment.name)

View File

@@ -6,6 +6,8 @@ import { InventoryTransaction } from './models/InventoryTransaction';
import { InventoryCostGLBeforeWriteSubscriber } from './subscribers/InventoryCostGLBeforeWriteSubscriber';
import { InventoryItemsQuantitySyncService } from './InventoryItemsQuantitySync.service';
import { InventoryCostMethod } from './InventoryCostMethod';
import { InventoryTransactionsService } from './InventoryTransactions.service';
import { LedgerModule } from '../Ledger/Ledger.module';
const models = [
RegisterTenancyModel(InventoryCostLotTracker),
@@ -13,12 +15,14 @@ const models = [
];
@Module({
imports: [LedgerModule],
providers: [
...models,
InventoryCostGLBeforeWriteSubscriber,
InventoryCostGLStorage,
InventoryItemsQuantitySyncService,
InventoryCostMethod,
InventoryTransactionsService
],
exports: [...models],
})

View File

@@ -1,31 +1,33 @@
import { Knex } from 'knex';
import { Inject } from '@nestjs/common';
import { omit } from 'lodash';
import { InventoryCostLotTracker } from './models/InventoryCostLotTracker';
import { Inject } from '@nestjs/common';
import { Knex } from 'knex';
export class InventoryCostMethod {
constructor(
@Inject(InventoryCostLotTracker.name)
private readonly inventoryCostLotTracker: typeof InventoryCostLotTracker
private readonly inventoryCostLotTracker: typeof InventoryCostLotTracker,
) {}
/**
* Stores the inventory lots costs transactions in bulk.
* @param {IInventoryLotCost[]} costLotsTransactions
* @return {Promise[]}
* @param {InventoryCostLotTracker[]} costLotsTransactions - Inventory lots costs transactions.
* @param {Knex.Transaction} trx - Knex transaction.
* @return {Promise<object>}
*/
public storeInventoryLotsCost(
costLotsTransactions: InventoryCostLotTracker[],
trx: Knex.Transaction
trx: Knex.Transaction,
): Promise<object> {
const opers: any = [];
costLotsTransactions.forEach((transaction: any) => {
if (transaction.lotTransId && transaction.decrement) {
const decrementOper = this.inventoryCostLotTracker.query(trx)
const decrementOper = this.inventoryCostLotTracker
.query(trx)
.where('id', transaction.lotTransId)
.decrement('remaining', transaction.decrement);
opers.push(decrementOper);
} else if (!transaction.lotTransId) {
const operation = this.inventoryCostLotTracker.query(trx).insert({

View File

@@ -0,0 +1,167 @@
import { IInventoryTransactionsDeletedPayload, TInventoryTransactionDirection } from './types/InventoryCost.types';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { InventoryCostLotTracker } from './models/InventoryCostLotTracker';
import { InventoryTransaction } from './models/InventoryTransaction';
import { Knex } from 'knex';
import { events } from '@/common/events/events';
import { IInventoryTransactionsCreatedPayload } from './types/InventoryCost.types';
import { transformItemEntriesToInventory } from "./utils";
import { IItemEntryTransactionType } from '../TransactionItemEntry/ItemEntry.types';
import { ItemEntry } from '../TransactionItemEntry/models/ItemEntry';
import { Inject } from '@nestjs/common';
export class InventoryTransactionsService {
constructor(
private readonly eventEmitter: EventEmitter2,
@Inject(InventoryTransaction.name)
private readonly inventoryTransactionModel: typeof InventoryTransaction,
@Inject(InventoryCostLotTracker.name)
private readonly inventoryCostLotTracker: typeof InventoryCostLotTracker,
) {}
/**
* Records the inventory transactions.
* @param {InventoryTransaction[]} transactions - Inventory transactions.
* @param {boolean} override - Override the existing transactions.
* @param {Knex.Transaction} trx - Knex transaction.
* @return {Promise<void>}
*/
async recordInventoryTransactions(
transactions: InventoryTransaction[],
override: boolean = false,
trx?: Knex.Transaction,
): Promise<void> {
const bulkInsertOpers = [];
transactions.forEach((transaction: InventoryTransaction) => {
const oper = this.recordInventoryTransaction(transaction, override, trx);
bulkInsertOpers.push(oper);
});
const inventoryTransactions = await Promise.all(bulkInsertOpers);
// Triggers `onInventoryTransactionsCreated` event.
await this.eventEmitter.emitAsync(
events.inventory.onInventoryTransactionsCreated,
{
inventoryTransactions,
trx,
} as IInventoryTransactionsCreatedPayload,
);
}
/**
* Writes the inventory transactiosn on the storage from the given
* inventory transactions entries.
* @param {InventoryTransaction} inventoryEntry - Inventory transaction.
* @param {boolean} deleteOld - Delete the existing inventory transactions.
* @param {Knex.Transaction} trx - Knex transaction.
* @return {Promise<InventoryTransaction>}
*/
async recordInventoryTransaction(
inventoryEntry: InventoryTransaction,
deleteOld: boolean = false,
trx: Knex.Transaction,
): Promise<InventoryTransaction> {
if (deleteOld) {
await this.deleteInventoryTransactions(
inventoryEntry.transactionId,
inventoryEntry.transactionType,
trx,
);
}
return this.inventoryTransactionModel.query(trx).insertGraph({
...inventoryEntry,
});
}
/**
* Records the inventory transactions from items entries that have (inventory) type.
*
* @param {number} tenantId
* @param {number} transactionId
* @param {string} transactionType
* @param {Date|string} transactionDate
* @param {boolean} override
*/
async recordInventoryTransactionsFromItemsEntries(
transaction: {
transactionId: number;
transactionType: IItemEntryTransactionType;
exchangeRate: number;
date: Date | string;
direction: TInventoryTransactionDirection;
entries: ItemEntry[];
createdAt: Date | string;
warehouseId?: number;
},
override: boolean = false,
trx?: Knex.Transaction,
): Promise<void> {
// Can't continue if there is no entries has inventory items in the invoice.
if (transaction.entries.length <= 0) {
return;
}
// Inventory transactions.
const inventoryTranscations = transformItemEntriesToInventory(transaction);
// Records the inventory transactions of the given sale invoice.
await this.recordInventoryTransactions(
inventoryTranscations,
override,
trx,
);
}
/**
* Deletes the given inventory transactions.
* @param {number} transactionId - Transaction id.
* @param {string} transactionType - Transaction type.
* @param {Knex.Transaction} trx - Knex transaction.
* @return {Promise<{ oldInventoryTransactions: IInventoryTransaction[] }>}
*/
public async deleteInventoryTransactions(
transactionId: number,
transactionType: string,
trx?: Knex.Transaction,
): Promise<{ oldInventoryTransactions: InventoryTransaction[] }> {
// Retrieve the inventory transactions of the given sale invoice.
const oldInventoryTransactions = await this.inventoryTransactionModel
.query(trx)
.where({ transactionId, transactionType });
// Deletes the inventory transactions by the given transaction type and id.
await this.inventoryTransactionModel
.query(trx)
.where({ transactionType, transactionId })
.delete();
// Triggers `onInventoryTransactionsDeleted` event.
await this.eventEmitter.emitAsync(
events.inventory.onInventoryTransactionsDeleted,
{
oldInventoryTransactions,
transactionId,
transactionType,
trx,
} as IInventoryTransactionsDeletedPayload,
);
return { oldInventoryTransactions };
}
/**
* Records the inventory cost lot transaction.
* @param {InventoryCostLotTracker} inventoryLotEntry
* @return {Promise<InventoryCostLotTracker>}
*/
async recordInventoryCostLotTransaction(
inventoryLotEntry: Partial<InventoryCostLotTracker>,
): Promise<InventoryCostLotTracker> {
return this.inventoryCostLotTracker.query().insert({
...inventoryLotEntry,
});
}
}

View File

@@ -4,8 +4,9 @@ import moment, { unitOfTime } from 'moment';
import { BaseModel } from '@/models/Model';
import { getTransactionTypeLabel } from '@/modules/BankingTransactions/utils';
import { TInventoryTransactionDirection } from '../types/InventoryCost.types';
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
export class InventoryTransaction extends BaseModel {
export class InventoryTransaction extends TenantBaseModel {
date: Date | string;
direction: TInventoryTransactionDirection;
itemId: number;
@@ -39,7 +40,7 @@ export class InventoryTransaction extends BaseModel {
* Retrieve formatted reference type.
* @return {string}
*/
get transcationTypeFormatted() {
get transcationTypeFormatted(): string {
return getTransactionTypeLabel(this.transactionType);
}

View File

@@ -9,14 +9,17 @@ import { ItemCategoryController } from './ItemCategory.controller';
import { CommandItemCategoryValidatorService } from './commands/CommandItemCategoryValidator.service';
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
import { GetItemCategoriesService } from './queries/GetItemCategories.service';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
@Module({
imports: [TenancyDatabaseModule],
imports: [TenancyDatabaseModule, DynamicListModule],
controllers: [ItemCategoryController],
providers: [
CreateItemCategoryService,
EditItemCategoryService,
GetItemCategoryService,
GetItemCategoriesService,
DeleteItemCategoryService,
ItemCategoryApplication,
CommandItemCategoryValidatorService,

View File

@@ -5,29 +5,26 @@ import { MAIL_TRANSPORTER_PROVIDER } from './Mail.constants';
import { MailTransporter } from './MailTransporter.service';
@Module({
imports: [
providers: [
{
module: MailModule,
providers: [
{
provide: MAIL_TRANSPORTER_PROVIDER,
useFactory: (configService: ConfigService) => {
// Create reusable transporter object using the default SMTP transport
const transporter = createTransport({
host: configService.get('mail.host'),
port: configService.get('mail.port'),
secure: configService.get('mail.secure'), // true for 465, false for other ports
auth: {
user: configService.get('mail.username'),
pass: configService.get('mail.password'),
},
});
return transporter;
provide: MAIL_TRANSPORTER_PROVIDER,
inject: [ConfigService],
useFactory: (configService: ConfigService) => {
// Create reusable transporter object using the default SMTP transport
const transporter = createTransport({
host: configService.get('mail.host'),
port: configService.get('mail.port'),
secure: configService.get('mail.secure'), // true for 465, false for other ports
auth: {
user: configService.get('mail.username'),
pass: configService.get('mail.password'),
},
},
],
});
return transporter;
},
},
MailTransporter
MailTransporter,
],
exports: [MAIL_TRANSPORTER_PROVIDER, MailTransporter],
})
export class MailModule {}

View File

@@ -1,8 +1,11 @@
import { Module } from '@nestjs/common';
import { ContactMailNotification } from './ContactMailNotification';
import { MailTenancyModule } from '../MailTenancy/MailTenancy.module';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
@Module({
imports: [ContactMailNotification],
imports: [MailTenancyModule],
providers: [ContactMailNotification, TenancyContext],
exports: [ContactMailNotification],
})
export class MailNotificationModule {}

View File

@@ -3,7 +3,7 @@ import { MailTenancy } from './MailTenancy.service';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
@Module({
imports: [],
providers: [MailTenancy, TenancyContext],
exports: [MailTenancy],
})
export class MailTenancyModule {}

View File

@@ -24,11 +24,24 @@ import { DeleteSaleEstimate } from './commands/DeleteSaleEstimate.service';
import { GetSaleEstimate } from './queries/GetSaleEstimate.service';
import { GetSaleEstimateState } from './queries/GetSaleEstimateState.service';
import { SendSaleEstimateMail } from './commands/SendSaleEstimateMail';
import { GetSaleEstimatesService } from './queries/GetSaleEstimates.service';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
import { GetSaleEstimatePdf } from './queries/GetSaleEstimatePdf';
import { MailNotificationModule } from '../MailNotification/MailNotification.module';
import { MailModule } from '../Mail/Mail.module';
import { ChromiumlyTenancyModule } from '../ChromiumlyTenancy/ChromiumlyTenancy.module';
import { TemplateInjectableModule } from '../TemplateInjectable/TemplateInjectable.module';
// import { SaleEstimateNotifyBySms } from './commands/SaleEstimateSmsNotify';
// import { SendSaleEstimateMail } from './commands/SendSaleEstimateMail';
//
@Module({
imports: [TenancyDatabaseModule],
imports: [
TenancyDatabaseModule,
DynamicListModule,
MailNotificationModule,
MailModule,
ChromiumlyTenancyModule,
TemplateInjectableModule
],
controllers: [SaleEstimatesController],
providers: [
AutoIncrementOrdersService,
@@ -39,6 +52,7 @@ import { SendSaleEstimateMail } from './commands/SendSaleEstimateMail';
EditSaleEstimate,
DeleteSaleEstimate,
GetSaleEstimate,
GetSaleEstimatesService,
GetSaleEstimateState,
ApproveSaleEstimateService,
DeliverSaleEstimateService,
@@ -54,6 +68,7 @@ import { SendSaleEstimateMail } from './commands/SendSaleEstimateMail';
TransformerInjectable,
SaleEstimatesApplication,
SendSaleEstimateMail,
GetSaleEstimatePdf,
// SaleEstimateNotifyBySms,
],
})

View File

@@ -11,6 +11,7 @@ import { CommandTaxRatesValidators } from './commands/CommandTaxRatesValidator.s
import { TenancyContext } from '../Tenancy/TenancyContext.service';
import { TaxRatesApplication } from './TaxRate.application';
import { ItemEntriesTaxTransactions } from './ItemEntriesTaxTransactions.service';
import { GetTaxRatesService } from './queries/GetTaxRates.service';
@Module({
imports: [],
@@ -20,6 +21,7 @@ import { ItemEntriesTaxTransactions } from './ItemEntriesTaxTransactions.service
EditTaxRateService,
DeleteTaxRateService,
GetTaxRateService,
GetTaxRatesService,
ActivateTaxRateService,
InactivateTaxRateService,
CommandTaxRatesValidators,