mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-14 03:40:31 +00:00
refactor: financial statements to nestjs
This commit is contained in:
@@ -1,35 +1,138 @@
|
||||
|
||||
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Bill } from '@/modules/Bills/models/Bill';
|
||||
import { Vendor } from '@/modules/Vendors/models/Vendor';
|
||||
import { IAPAgingSummaryQuery } from './APAgingSummary.types';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { groupBy } from 'ramda';
|
||||
|
||||
export class APAgingSummaryRepository {
|
||||
@Inject(Vendor.name)
|
||||
private readonly vendorModel: typeof Vendor;
|
||||
|
||||
@Inject(Bill.name)
|
||||
private readonly billModel: typeof Bill;
|
||||
|
||||
asyncInit() {
|
||||
// Settings tenant service.
|
||||
const tenant = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
@Inject(TenancyContext)
|
||||
private readonly tenancyContext: TenancyContext;
|
||||
|
||||
/**
|
||||
* Filter.
|
||||
* @param {IAPAgingSummaryQuery} filter
|
||||
*/
|
||||
filter: IAPAgingSummaryQuery;
|
||||
|
||||
/**
|
||||
* Due bills.
|
||||
* @param {Bill[]} dueBills
|
||||
*/
|
||||
dueBills: Bill[];
|
||||
|
||||
/**
|
||||
* Due bills by vendor id.
|
||||
* @param {Record<string, Bill[]>} dueBillsByVendorId
|
||||
*/
|
||||
dueBillsByVendorId: Record<number, Bill[]>;
|
||||
|
||||
/**
|
||||
* Overdue bills.
|
||||
* @param {Bill[]} overdueBills
|
||||
*/
|
||||
overdueBills: Bill[];
|
||||
|
||||
/**
|
||||
* Overdue bills by vendor id.
|
||||
* @param {Record<string, Bill[]>} overdueBillsByVendorId
|
||||
*/
|
||||
overdueBillsByVendorId: Record<number, Bill[]>;
|
||||
|
||||
/**
|
||||
* Vendors.
|
||||
* @param {Vendor[]} vendors
|
||||
*/
|
||||
vendors: Vendor[];
|
||||
|
||||
/**
|
||||
* Base currency.
|
||||
* @param {string} baseCurrency
|
||||
*/
|
||||
baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Set the filter.
|
||||
* @param {IAPAgingSummaryQuery} filter
|
||||
*/
|
||||
setFilter(filter: IAPAgingSummaryQuery) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the data.
|
||||
*/
|
||||
async load() {
|
||||
await this.asyncBaseCurrency();
|
||||
await this.asyncVendors();
|
||||
await this.asyncDueBills();
|
||||
await this.asyncOverdueBills();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the base currency.
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async asyncBaseCurrency() {
|
||||
const metadata = await this.tenancyContext.getTenantMetadata();
|
||||
|
||||
this.baseCurrency = metadata.baseCurrency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all vendors from the storage.
|
||||
*/
|
||||
async asyncVendors() {
|
||||
// Retrieve all vendors from the storage.
|
||||
const vendors =
|
||||
filter.vendorsIds.length > 0
|
||||
? await vendorRepository.findWhereIn('id', filter.vendorsIds)
|
||||
: await vendorRepository.all();
|
||||
this.filter.vendorsIds.length > 0
|
||||
? await this.vendorModel.query().whereIn('id', this.filter.vendorsIds)
|
||||
: await this.vendorModel.query();
|
||||
|
||||
// Common query.
|
||||
this.vendors = vendors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all overdue bills from the storage.
|
||||
*/
|
||||
async asyncOverdueBills() {
|
||||
const commonQuery = (query) => {
|
||||
if (!isEmpty(filter.branchesIds)) {
|
||||
query.modify('filterByBranches', filter.branchesIds);
|
||||
if (!isEmpty(this.filter.branchesIds)) {
|
||||
query.modify('filterByBranches', this.filter.branchesIds);
|
||||
}
|
||||
};
|
||||
// Retrieve all overdue vendors bills.
|
||||
const overdueBills = await Bill.query()
|
||||
.modify('overdueBillsFromDate', filter.asDate)
|
||||
const overdueBills = await this.billModel
|
||||
.query()
|
||||
.modify('overdueBillsFromDate', this.filter.asDate)
|
||||
.onBuild(commonQuery);
|
||||
|
||||
// Retrieve all due vendors bills.
|
||||
const dueBills = await Bill.query()
|
||||
.modify('dueBillsFromDate', filter.asDate)
|
||||
.onBuild(commonQuery);
|
||||
this.overdueBills = overdueBills;
|
||||
this.overdueBillsByVendorId = groupBy(overdueBills, 'vendorId');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all due bills from the storage.
|
||||
*/
|
||||
async asyncDueBills() {
|
||||
const commonQuery = (query) => {
|
||||
if (!isEmpty(this.filter.branchesIds)) {
|
||||
query.modify('filterByBranches', this.filter.branchesIds);
|
||||
}
|
||||
};
|
||||
// Retrieve all due vendors bills.
|
||||
const dueBills = await this.billModel
|
||||
.query()
|
||||
.modify('dueBillsFromDate', this.filter.asDate)
|
||||
.onBuild(commonQuery);
|
||||
|
||||
this.dueBills = dueBills;
|
||||
this.dueBillsByVendorId = groupBy(dueBills, 'vendorId');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { events } from '@/common/events/events';
|
||||
import {
|
||||
IAPAgingSummaryQuery,
|
||||
IAPAgingSummarySheet,
|
||||
@@ -7,19 +8,19 @@ import {
|
||||
import { APAgingSummarySheet } from './APAgingSummarySheet';
|
||||
import { APAgingSummaryMeta } from './APAgingSummaryMeta';
|
||||
import { getAPAgingSummaryDefaultQuery } from './utils';
|
||||
import { events } from '@/common/events/events';
|
||||
import { APAgingSummaryRepository } from './APAgingSummaryRepository';
|
||||
|
||||
@Injectable()
|
||||
export class APAgingSummaryService {
|
||||
constructor(
|
||||
private readonly APAgingSummaryMeta: APAgingSummaryMeta,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
private readonly APAgingSummaryRepository: APAgingSummaryRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve A/P aging summary report.
|
||||
* @param {number} tenantId -
|
||||
* @param {IAPAgingSummaryQuery} query -
|
||||
* @param {IAPAgingSummaryQuery} query - A/P aging summary query.
|
||||
* @returns {Promise<IAPAgingSummarySheet>}
|
||||
*/
|
||||
public async APAgingSummary(
|
||||
@@ -30,13 +31,14 @@ export class APAgingSummaryService {
|
||||
...getAPAgingSummaryDefaultQuery(),
|
||||
...query,
|
||||
};
|
||||
// Load the data.
|
||||
this.APAgingSummaryRepository.setFilter(filter);
|
||||
await this.APAgingSummaryRepository.load();
|
||||
|
||||
// A/P aging summary report instance.
|
||||
const APAgingSummaryReport = new APAgingSummarySheet(
|
||||
filter,
|
||||
vendors,
|
||||
overdueBills,
|
||||
dueBills,
|
||||
tenant.metadata.baseCurrency,
|
||||
this.APAgingSummaryRepository,
|
||||
);
|
||||
// A/P aging summary report data and columns.
|
||||
const data = APAgingSummaryReport.reportData();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { groupBy, sum, isEmpty } from 'lodash';
|
||||
import { sum, isEmpty } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IAPAgingSummaryQuery,
|
||||
@@ -10,20 +10,13 @@ import {
|
||||
import { AgingSummaryReport } from '../AgingSummary/AgingSummary';
|
||||
import { IAgingPeriod } from '../AgingSummary/AgingSummary.types';
|
||||
import { ModelObject } from 'objection';
|
||||
import { Bill } from '@/modules/Bills/models/Bill';
|
||||
import { Vendor } from '@/modules/Vendors/models/Vendor';
|
||||
import { allPassedConditionsPass } from '@/utils/all-conditions-passed';
|
||||
import { APAgingSummaryRepository } from './APAgingSummaryRepository';
|
||||
|
||||
export class APAgingSummarySheet extends AgingSummaryReport {
|
||||
readonly tenantId: number;
|
||||
readonly repository: APAgingSummaryRepository;
|
||||
readonly query: IAPAgingSummaryQuery;
|
||||
readonly contacts: ModelObject<Vendor>[];
|
||||
readonly unpaidBills: ModelObject<Bill>[];
|
||||
readonly baseCurrency: string;
|
||||
|
||||
readonly overdueInvoicesByContactId: Record<number, Array<ModelObject<Bill>>>;
|
||||
readonly currentInvoicesByContactId: Record<number, Array<ModelObject<Bill>>>;
|
||||
|
||||
readonly agingPeriods: IAgingPeriod[];
|
||||
|
||||
/**
|
||||
@@ -34,23 +27,14 @@ export class APAgingSummarySheet extends AgingSummaryReport {
|
||||
* @param {string} baseCurrency - Base currency of the organization.
|
||||
*/
|
||||
constructor(
|
||||
tenantId: number,
|
||||
query: IAPAgingSummaryQuery,
|
||||
vendors: ModelObject<Vendor>[],
|
||||
overdueBills: ModelObject<Bill>[],
|
||||
unpaidBills: ModelObject<Bill>[],
|
||||
baseCurrency: string,
|
||||
repository: APAgingSummaryRepository,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.tenantId = tenantId;
|
||||
this.query = query;
|
||||
this.repository = repository;
|
||||
this.numberFormat = this.query.numberFormat;
|
||||
this.contacts = vendors;
|
||||
this.baseCurrency = baseCurrency;
|
||||
|
||||
this.overdueInvoicesByContactId = groupBy(overdueBills, 'vendorId');
|
||||
this.currentInvoicesByContactId = groupBy(unpaidBills, 'vendorId');
|
||||
|
||||
// Initializes the aging periods.
|
||||
this.agingPeriods = this.agingRangePeriods(
|
||||
@@ -170,7 +154,7 @@ export class APAgingSummarySheet extends AgingSummaryReport {
|
||||
* @return {IAPAgingSummaryData}
|
||||
*/
|
||||
public reportData = (): IAPAgingSummaryData => {
|
||||
const vendorsAgingPeriods = this.vendorsSection(this.contacts);
|
||||
const vendorsAgingPeriods = this.vendorsSection(this.repository.vendors);
|
||||
const vendorsTotal = this.getVendorsTotal(vendorsAgingPeriods);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,38 +1,146 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import { isEmpty, groupBy } from 'lodash';
|
||||
import { Customer } from '@/modules/Customers/models/Customer';
|
||||
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||
import { ModelObject } from 'objection';
|
||||
import { IARAgingSummaryQuery } from './ARAgingSummary.types';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { Inject } from '@nestjs/common';
|
||||
|
||||
export class ARAgingSummaryRepository {
|
||||
@Inject(TenancyContext)
|
||||
private tenancyContext: TenancyContext;
|
||||
|
||||
@Inject(Customer.name)
|
||||
private customerModel: typeof Customer;
|
||||
|
||||
init(){
|
||||
const tenant = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
@Inject(SaleInvoice.name)
|
||||
private saleInvoiceModel: typeof SaleInvoice;
|
||||
|
||||
// Retrieve all customers from the storage.
|
||||
const customers =
|
||||
filter.customersIds.length > 0
|
||||
? await customerRepository.findWhereIn('id', filter.customersIds)
|
||||
: await customerRepository.all();
|
||||
/**
|
||||
* Filter.
|
||||
* @param {IARAgingSummaryQuery} filter
|
||||
*/
|
||||
filter: IARAgingSummaryQuery;
|
||||
|
||||
// Common query.
|
||||
const commonQuery = (query) => {
|
||||
if (!isEmpty(filter.branchesIds)) {
|
||||
query.modify('filterByBranches', filter.branchesIds);
|
||||
}
|
||||
};
|
||||
// Retrieve all overdue sale invoices.
|
||||
const overdueSaleInvoices = await SaleInvoice.query()
|
||||
.modify('overdueInvoicesFromDate', filter.asDate)
|
||||
.onBuild(commonQuery);
|
||||
/**
|
||||
* Base currency.
|
||||
* @param {string} baseCurrency
|
||||
*/
|
||||
baseCurrency: string;
|
||||
|
||||
// Retrieve all due sale invoices.
|
||||
const currentInvoices = await SaleInvoice.query()
|
||||
.modify('dueInvoicesFromDate', filter.asDate)
|
||||
.onBuild(commonQuery);
|
||||
/**
|
||||
* Customers.
|
||||
* @param {ModelObject<Customer>[]} customers
|
||||
*/
|
||||
customers: ModelObject<Customer>[];
|
||||
|
||||
/**
|
||||
* Overdue sale invoices.
|
||||
* @param {ModelObject<SaleInvoice>[]} overdueSaleInvoices
|
||||
*/
|
||||
overdueSaleInvoices: ModelObject<SaleInvoice>[];
|
||||
|
||||
/**
|
||||
* Current sale invoices.
|
||||
* @param {ModelObject<SaleInvoice>[]} currentInvoices
|
||||
*/
|
||||
currentInvoices: ModelObject<SaleInvoice>[];
|
||||
|
||||
/**
|
||||
* Current sale invoices by contact id.
|
||||
* @param {Record<string, ModelObject<SaleInvoice>[]>} currentInvoicesByContactId
|
||||
*/
|
||||
currentInvoicesByContactId: Record<string, ModelObject<SaleInvoice>[]>;
|
||||
|
||||
/**
|
||||
* Overdue sale invoices by contact id.
|
||||
* @param {Record<string, ModelObject<SaleInvoice>[]>} overdueInvoicesByContactId
|
||||
*/
|
||||
overdueInvoicesByContactId: Record<string, ModelObject<SaleInvoice>[]>;
|
||||
|
||||
/**
|
||||
* Set the filter.
|
||||
* @param {IARAgingSummaryQuery} filter
|
||||
*/
|
||||
setFilter(filter: IARAgingSummaryQuery) {
|
||||
this.filter = filter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the repository.
|
||||
*/
|
||||
async load() {
|
||||
await this.initBaseCurrency();
|
||||
await this.initCustomers();
|
||||
await this.initOverdueSaleInvoices();
|
||||
await this.initCurrentInvoices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the base currency.
|
||||
*/
|
||||
async initBaseCurrency() {
|
||||
const tenantMetadata = await this.tenancyContext.getTenantMetadata();
|
||||
|
||||
this.baseCurrency = tenantMetadata.baseCurrency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the customers.
|
||||
*/
|
||||
async initCustomers() {
|
||||
// Retrieve all customers from the storage.
|
||||
const customers =
|
||||
this.filter.customersIds.length > 0
|
||||
? await this.customerModel
|
||||
.query()
|
||||
.whereIn('id', this.filter.customersIds)
|
||||
: await this.customerModel.query();
|
||||
|
||||
this.customers = customers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the overdue sale invoices.
|
||||
*/
|
||||
async initOverdueSaleInvoices() {
|
||||
const commonQuery = (query) => {
|
||||
if (!isEmpty(this.filter.branchesIds)) {
|
||||
query.modify('filterByBranches', this.filter.branchesIds);
|
||||
}
|
||||
};
|
||||
// Retrieve all overdue sale invoices.
|
||||
const overdueSaleInvoices = await this.saleInvoiceModel
|
||||
.query()
|
||||
.modify('overdueInvoicesFromDate', this.filter.asDate)
|
||||
.onBuild(commonQuery);
|
||||
|
||||
this.overdueSaleInvoices = overdueSaleInvoices;
|
||||
this.overdueInvoicesByContactId = groupBy(
|
||||
overdueSaleInvoices,
|
||||
'customerId',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the current sale invoices.
|
||||
*/
|
||||
async initCurrentInvoices() {
|
||||
const commonQuery = (query) => {
|
||||
if (!isEmpty(this.filter.branchesIds)) {
|
||||
query.modify('filterByBranches', this.filter.branchesIds);
|
||||
}
|
||||
};
|
||||
// Retrieve all due sale invoices.
|
||||
const currentInvoices = await this.saleInvoiceModel
|
||||
.query()
|
||||
.modify('dueInvoicesFromDate', this.filter.asDate)
|
||||
.onBuild(commonQuery);
|
||||
|
||||
this.currentInvoices = currentInvoices;
|
||||
this.currentInvoicesByContactId = groupBy(
|
||||
currentInvoices,
|
||||
'customerId',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import { Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { IARAgingSummaryQuery } from './ARAgingSummary.types';
|
||||
import { events } from '@/common/events/events';
|
||||
import { ARAgingSummaryRepository } from './ARAgingSummaryRepository';
|
||||
|
||||
@Injectable()
|
||||
export class ARAgingSummaryService {
|
||||
constructor(
|
||||
private readonly ARAgingSummaryMeta: ARAgingSummaryMeta,
|
||||
private readonly ARAgingSummaryRepository: ARAgingSummaryRepository,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
) {}
|
||||
|
||||
@@ -22,13 +24,14 @@ export class ARAgingSummaryService {
|
||||
...getARAgingSummaryDefaultQuery(),
|
||||
...query,
|
||||
};
|
||||
// Load the A/R aging summary repository.
|
||||
this.ARAgingSummaryRepository.setFilter(filter);
|
||||
await this.ARAgingSummaryRepository.load();
|
||||
|
||||
// AR aging summary report instance.
|
||||
const ARAgingSummaryReport = new ARAgingSummarySheet(
|
||||
filter,
|
||||
customers,
|
||||
overdueSaleInvoices,
|
||||
currentInvoices,
|
||||
tenant.metadata.baseCurrency,
|
||||
this.ARAgingSummaryRepository,
|
||||
);
|
||||
// AR aging summary report data and columns.
|
||||
const data = ARAgingSummaryReport.reportData();
|
||||
@@ -40,9 +43,7 @@ export class ARAgingSummaryService {
|
||||
// Triggers `onReceivableAgingViewed` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.reports.onReceivableAgingViewed,
|
||||
{
|
||||
query,
|
||||
},
|
||||
{ query },
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as R from 'ramda';
|
||||
import { Dictionary, groupBy, isEmpty, sum } from 'lodash';
|
||||
import { isEmpty, sum } from 'lodash';
|
||||
import { IAgingPeriod } from '../AgingSummary/AgingSummary.types';
|
||||
import {
|
||||
IARAgingSummaryQuery,
|
||||
@@ -12,17 +12,12 @@ import { AgingSummaryReport } from '../AgingSummary/AgingSummary';
|
||||
import { allPassedConditionsPass } from '@/utils/all-conditions-passed';
|
||||
import { ModelObject } from 'objection';
|
||||
import { Customer } from '@/modules/Customers/models/Customer';
|
||||
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||
import { ARAgingSummaryRepository } from './ARAgingSummaryRepository';
|
||||
|
||||
export class ARAgingSummarySheet extends AgingSummaryReport {
|
||||
readonly tenantId: number;
|
||||
readonly query: IARAgingSummaryQuery;
|
||||
readonly contacts: ModelObject<Customer>[];
|
||||
readonly agingPeriods: IAgingPeriod[];
|
||||
readonly baseCurrency: string;
|
||||
|
||||
readonly overdueInvoicesByContactId: Dictionary<ModelObject<SaleInvoice>[]>;
|
||||
readonly currentInvoicesByContactId: Dictionary<ModelObject<SaleInvoice>[]>;
|
||||
readonly repository: ARAgingSummaryRepository;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
@@ -32,29 +27,15 @@ export class ARAgingSummarySheet extends AgingSummaryReport {
|
||||
* @param {IJournalPoster} journal
|
||||
*/
|
||||
constructor(
|
||||
tenantId: number,
|
||||
query: IARAgingSummaryQuery,
|
||||
customers: ModelObject<Customer>[],
|
||||
overdueSaleInvoices: ModelObject<SaleInvoice>[],
|
||||
currentSaleInvoices: ModelObject<SaleInvoice>[],
|
||||
baseCurrency: string,
|
||||
repository: ARAgingSummaryRepository,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.tenantId = tenantId;
|
||||
this.contacts = customers;
|
||||
this.query = query;
|
||||
this.baseCurrency = baseCurrency;
|
||||
this.repository = repository;
|
||||
this.numberFormat = this.query.numberFormat;
|
||||
|
||||
this.overdueInvoicesByContactId = groupBy(
|
||||
overdueSaleInvoices,
|
||||
'customerId',
|
||||
);
|
||||
this.currentInvoicesByContactId = groupBy(
|
||||
currentSaleInvoices,
|
||||
'customerId',
|
||||
);
|
||||
// Initializes the aging periods.
|
||||
this.agingPeriods = this.agingRangePeriods(
|
||||
this.query.asDate,
|
||||
@@ -179,7 +160,9 @@ export class ARAgingSummarySheet extends AgingSummaryReport {
|
||||
* @return {IARAgingSummaryData}
|
||||
*/
|
||||
public reportData = (): IARAgingSummaryData => {
|
||||
const customersAgingPeriods = this.customersWalker(this.contacts);
|
||||
const customersAgingPeriods = this.customersWalker(
|
||||
this.repository.customers,
|
||||
);
|
||||
const customersTotal = this.getCustomersTotal(customersAgingPeriods);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Controller, Headers, Query, Res } from '@nestjs/common';
|
||||
import { InventortyDetailsApplication } from './InventoryItemDetailsApplication';
|
||||
import { InventoryItemDetailsApplication } from './InventoryItemDetailsApplication';
|
||||
import { IInventoryDetailsQuery } from './InventoryItemDetails.types';
|
||||
import { AcceptType } from '@/constants/accept-type';
|
||||
import { Response } from 'express';
|
||||
@@ -7,7 +7,7 @@ import { Response } from 'express';
|
||||
@Controller('reports/inventory-item-details')
|
||||
export class InventoryItemDetailsController {
|
||||
constructor(
|
||||
private readonly inventoryItemDetailsApp: InventortyDetailsApplication,
|
||||
private readonly inventoryItemDetailsApp: InventoryItemDetailsApplication,
|
||||
) {}
|
||||
|
||||
async inventoryItemDetails(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InventoryItemDetailsController } from './InventoryItemDetails.controller';
|
||||
import { InventoryDetailsTablePdf } from './InventoryItemDetailsTablePdf';
|
||||
import { InventoryDetailsService } from './InventoryItemDetailsService';
|
||||
import { InventoryDetailsService } from './InventoryItemDetails.service';
|
||||
import { InventoryDetailsTableInjectable } from './InventoryItemDetailsTableInjectable';
|
||||
import { InventoryItemDetailsExportInjectable } from './InventoryItemDetailsExportInjectable';
|
||||
import { InventoryItemDetailsApplication } from './InventoryItemDetailsApplication';
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
IInventoryDetailsQuery,
|
||||
IInvetoryItemDetailDOO,
|
||||
} from './InventoryItemDetails.types';
|
||||
import { InventoryDetails } from './InventoryItemDetails';
|
||||
import { InventoryItemDetailsRepository } from './InventoryItemDetailsRepository';
|
||||
import { InventoryDetailsMetaInjectable } from './InventoryItemDetailsMeta';
|
||||
import { getInventoryItemDetailsDefaultQuery } from './constant';
|
||||
|
||||
@Injectable()
|
||||
export class InventoryDetailsService {
|
||||
constructor(
|
||||
private readonly inventoryItemDetailsRepository: InventoryItemDetailsRepository,
|
||||
private readonly inventoryDetailsMeta: InventoryDetailsMetaInjectable,
|
||||
private readonly i18n: I18nService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve the inventory details report data.
|
||||
* @param {IInventoryDetailsQuery} query - Inventory details query.
|
||||
* @return {Promise<IInvetoryItemDetailDOO>}
|
||||
*/
|
||||
public async inventoryDetails(
|
||||
query: IInventoryDetailsQuery,
|
||||
): Promise<IInvetoryItemDetailDOO> {
|
||||
const filter = {
|
||||
...getInventoryItemDetailsDefaultQuery(),
|
||||
...query,
|
||||
};
|
||||
// Inventory details report mapper.
|
||||
const inventoryDetailsInstance = new InventoryDetails(
|
||||
filter,
|
||||
this.inventoryItemDetailsRepository,
|
||||
this.i18n,
|
||||
);
|
||||
const meta = await this.inventoryDetailsMeta.meta(query);
|
||||
|
||||
return {
|
||||
data: inventoryDetailsInstance.reportData(),
|
||||
query: filter,
|
||||
meta,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as R from 'ramda';
|
||||
import * as moment from 'moment';
|
||||
import { defaultTo, sumBy, get } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import {
|
||||
IInventoryDetailsQuery,
|
||||
IInventoryDetailsNumber,
|
||||
@@ -11,56 +12,42 @@ import {
|
||||
IInventoryDetailsOpening,
|
||||
IInventoryDetailsItemTransaction,
|
||||
} from './InventoryItemDetails.types';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import { transformToMapBy, transformToMapKeyValue } from 'utils';
|
||||
import { filterDeep } from 'utils/deepdash';
|
||||
|
||||
const MAP_CONFIG = { childrenPath: 'children', pathFormat: 'array' };
|
||||
|
||||
enum INodeTypes {
|
||||
ITEM = 'item',
|
||||
TRANSACTION = 'transaction',
|
||||
OPENING_ENTRY = 'OPENING_ENTRY',
|
||||
CLOSING_ENTRY = 'CLOSING_ENTRY',
|
||||
}
|
||||
import { ModelObject } from 'objection';
|
||||
import { Item } from '@/modules/Items/models/Item';
|
||||
import {
|
||||
IFormatNumberSettings,
|
||||
INumberFormatQuery,
|
||||
} from '../../types/Report.types';
|
||||
import { InventoryTransaction } from '@/modules/InventoryCost/models/InventoryTransaction';
|
||||
import { InventoryItemDetailsRepository } from './InventoryItemDetailsRepository';
|
||||
import { TInventoryTransactionDirection } from '@/modules/InventoryCost/types/InventoryCost.types';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
import { filterDeep } from '@/utils/deepdash';
|
||||
import { INodeTypes, MAP_CONFIG } from './constant';
|
||||
|
||||
export class InventoryDetails extends FinancialSheet {
|
||||
readonly inventoryTransactionsByItemId: Map<number, IInventoryTransaction[]>;
|
||||
readonly openingBalanceTransactions: Map<number, IInventoryTransaction>;
|
||||
readonly repository: InventoryItemDetailsRepository;
|
||||
readonly query: IInventoryDetailsQuery;
|
||||
readonly numberFormat: INumberFormatQuery;
|
||||
readonly baseCurrency: string;
|
||||
readonly items: IItem[];
|
||||
readonly items: ModelObject<Item>[];
|
||||
readonly i18n: I18nService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IItem[]} items - Items.
|
||||
* @param {IInventoryTransaction[]} inventoryTransactions - Inventory transactions.
|
||||
* @param {IInventoryDetailsQuery} query - Report query.
|
||||
* @param {string} baseCurrency - The base currency.
|
||||
* @param {InventoryItemDetailsRepository} repository - The repository.
|
||||
* @param {I18nService} i18n - The i18n service.
|
||||
*/
|
||||
constructor(
|
||||
items: IItem[],
|
||||
openingBalanceTransactions: IInventoryTransaction[],
|
||||
inventoryTransactions: IInventoryTransaction[],
|
||||
query: IInventoryDetailsQuery,
|
||||
baseCurrency: string,
|
||||
i18n: any
|
||||
filter: IInventoryDetailsQuery,
|
||||
repository: InventoryItemDetailsRepository,
|
||||
i18n: I18nService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.inventoryTransactionsByItemId = transformToMapBy(
|
||||
inventoryTransactions,
|
||||
'itemId'
|
||||
);
|
||||
this.openingBalanceTransactions = transformToMapKeyValue(
|
||||
openingBalanceTransactions,
|
||||
'itemId'
|
||||
);
|
||||
this.query = query;
|
||||
this.repository = repository;
|
||||
|
||||
this.query = filter;
|
||||
this.numberFormat = this.query.numberFormat;
|
||||
this.items = items;
|
||||
this.baseCurrency = baseCurrency;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@@ -69,9 +56,9 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {number} number
|
||||
* @returns
|
||||
*/
|
||||
private getNumberMeta(
|
||||
public getNumberMeta(
|
||||
number: number,
|
||||
settings?: IFormatNumberSettings
|
||||
settings?: IFormatNumberSettings,
|
||||
): IInventoryDetailsNumber {
|
||||
return {
|
||||
formattedNumber: this.formatNumber(number, {
|
||||
@@ -89,9 +76,9 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IFormatNumberSettings} settings -
|
||||
* @retrun {IInventoryDetailsNumber}
|
||||
*/
|
||||
private getTotalNumberMeta(
|
||||
public getTotalNumberMeta(
|
||||
number: number,
|
||||
settings?: IFormatNumberSettings
|
||||
settings?: IFormatNumberSettings,
|
||||
): IInventoryDetailsNumber {
|
||||
return this.getNumberMeta(number, { excerptZero: false, ...settings });
|
||||
}
|
||||
@@ -101,7 +88,7 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {Date|string} date
|
||||
* @returns {IInventoryDetailsDate}
|
||||
*/
|
||||
private getDateMeta(date: Date | string): IInventoryDetailsDate {
|
||||
public getDateMeta(date: Date | string): IInventoryDetailsDate {
|
||||
return {
|
||||
formattedDate: moment(date).format('YYYY-MM-DD'),
|
||||
date: moment(date).toDate(),
|
||||
@@ -110,14 +97,14 @@ export class InventoryDetails extends FinancialSheet {
|
||||
|
||||
/**
|
||||
* Adjusts the movement amount.
|
||||
* @param {number} amount
|
||||
* @param {TInventoryTransactionDirection} direction
|
||||
* @param {number} amount - The amount.
|
||||
* @param {TInventoryTransactionDirection} direction - The transaction direction.
|
||||
* @returns {number}
|
||||
*/
|
||||
private adjustAmountMovement = R.curry(
|
||||
public adjustAmountMovement = R.curry(
|
||||
(direction: TInventoryTransactionDirection, amount: number): number => {
|
||||
return direction === 'OUT' ? amount * -1 : amount;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -125,8 +112,8 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IInventoryDetailsItemTransaction[]} transactions
|
||||
* @returns {IInventoryDetailsItemTransaction[]}
|
||||
*/
|
||||
private mapAccumTransactionsRunningQuantity(
|
||||
transactions: IInventoryDetailsItemTransaction[]
|
||||
public mapAccumTransactionsRunningQuantity(
|
||||
transactions: IInventoryDetailsItemTransaction[],
|
||||
): IInventoryDetailsItemTransaction[] {
|
||||
const initial = this.getNumberMeta(0);
|
||||
|
||||
@@ -140,7 +127,7 @@ export class InventoryDetails extends FinancialSheet {
|
||||
return R.mapAccum(
|
||||
mapAccumAppender,
|
||||
{ runningQuantity: initial },
|
||||
transactions
|
||||
transactions,
|
||||
)[1];
|
||||
}
|
||||
|
||||
@@ -149,8 +136,8 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IInventoryDetailsItemTransaction[]} transactions
|
||||
* @returns {IInventoryDetailsItemTransaction}
|
||||
*/
|
||||
private mapAccumTransactionsRunningValuation(
|
||||
transactions: IInventoryDetailsItemTransaction[]
|
||||
public mapAccumTransactionsRunningValuation(
|
||||
transactions: IInventoryDetailsItemTransaction[],
|
||||
): IInventoryDetailsItemTransaction[] {
|
||||
const initial = this.getNumberMeta(0);
|
||||
|
||||
@@ -165,16 +152,18 @@ export class InventoryDetails extends FinancialSheet {
|
||||
return R.mapAccum(
|
||||
mapAccumAppender,
|
||||
{ runningValuation: initial },
|
||||
transactions
|
||||
transactions,
|
||||
)[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the inventory transaction total.
|
||||
* @param {IInventoryTransaction} transaction
|
||||
* @param {ModelObject<InventoryTransaction>} transaction
|
||||
* @returns {number}
|
||||
*/
|
||||
private getTransactionTotal = (transaction: IInventoryTransaction) => {
|
||||
public getTransactionTotal = (
|
||||
transaction: ModelObject<InventoryTransaction>,
|
||||
) => {
|
||||
return transaction.quantity
|
||||
? transaction.quantity * transaction.rate
|
||||
: transaction.rate;
|
||||
@@ -186,9 +175,9 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IInvetoryTransaction} transaction
|
||||
* @returns {IInventoryDetailsItemTransaction}
|
||||
*/
|
||||
private itemTransactionMapper(
|
||||
item: IItem,
|
||||
transaction: IInventoryTransaction
|
||||
public itemTransactionMapper(
|
||||
item: ModelObject<Item>,
|
||||
transaction: ModelObject<InventoryTransaction>,
|
||||
): IInventoryDetailsItemTransaction {
|
||||
const total = this.getTransactionTotal(transaction);
|
||||
const amountMovement = this.adjustAmountMovement(transaction.direction);
|
||||
@@ -209,7 +198,7 @@ export class InventoryDetails extends FinancialSheet {
|
||||
return {
|
||||
nodeType: INodeTypes.TRANSACTION,
|
||||
date: this.getDateMeta(transaction.date),
|
||||
transactionType: this.i18n.__(transaction.transcationTypeFormatted),
|
||||
transactionType: this.i18n.t(transaction.transcationTypeFormatted),
|
||||
transactionNumber: transaction?.meta?.transactionNumber,
|
||||
direction: transaction.direction,
|
||||
|
||||
@@ -232,13 +221,16 @@ export class InventoryDetails extends FinancialSheet {
|
||||
|
||||
/**
|
||||
* Retrieve the inventory transcations by item id.
|
||||
* @param {number} itemId
|
||||
* @returns {IInventoryTransaction[]}
|
||||
* @param {number} itemId - The item id.
|
||||
* @returns {ModelObject<InventoryTransaction>[]}
|
||||
*/
|
||||
private getInventoryTransactionsByItemId(
|
||||
itemId: number
|
||||
): IInventoryTransaction[] {
|
||||
return defaultTo(this.inventoryTransactionsByItemId.get(itemId + ''), []);
|
||||
public getInventoryTransactionsByItemId(
|
||||
itemId: number,
|
||||
): ModelObject<InventoryTransaction>[] {
|
||||
return defaultTo(
|
||||
this.repository.inventoryTransactionsByItemId.get(itemId),
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,13 +238,15 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IItem} item
|
||||
* @returns {IInventoryDetailsItemTransaction[]}
|
||||
*/
|
||||
private getItemTransactions(item: IItem): IInventoryDetailsItemTransaction[] {
|
||||
public getItemTransactions(
|
||||
item: ModelObject<Item>,
|
||||
): ModelObject<InventoryTransaction>[] {
|
||||
const transactions = this.getInventoryTransactionsByItemId(item.id);
|
||||
|
||||
return R.compose(
|
||||
this.mapAccumTransactionsRunningQuantity.bind(this),
|
||||
this.mapAccumTransactionsRunningValuation.bind(this),
|
||||
R.map(R.curry(this.itemTransactionMapper.bind(this))(item))
|
||||
R.map(R.curry(this.itemTransactionMapper.bind(this))(item)),
|
||||
)(transactions);
|
||||
}
|
||||
|
||||
@@ -265,8 +259,8 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* | IInventoryDetailsClosing
|
||||
* )[]}
|
||||
*/
|
||||
private itemTransactionsMapper(
|
||||
item: IItem
|
||||
public itemTransactionsMapper(
|
||||
item: ModelObject<Item>,
|
||||
): (
|
||||
| IInventoryDetailsItemTransaction
|
||||
| IInventoryDetailsOpening
|
||||
@@ -277,7 +271,7 @@ export class InventoryDetails extends FinancialSheet {
|
||||
const closingValuation = this.getItemClosingValuation(
|
||||
item,
|
||||
transactions,
|
||||
openingValuation
|
||||
openingValuation,
|
||||
);
|
||||
const hasTransactions = transactions.length > 0;
|
||||
const isItemHasOpeningBalance = this.isItemHasOpeningBalance(item.id);
|
||||
@@ -285,7 +279,7 @@ export class InventoryDetails extends FinancialSheet {
|
||||
return R.pipe(
|
||||
R.concat(transactions),
|
||||
R.when(R.always(isItemHasOpeningBalance), R.prepend(openingValuation)),
|
||||
R.when(R.always(hasTransactions), R.append(closingValuation))
|
||||
R.when(R.always(hasTransactions), R.append(closingValuation)),
|
||||
)([]);
|
||||
}
|
||||
|
||||
@@ -294,8 +288,8 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {number} itemId - Item id.
|
||||
* @return {boolean}
|
||||
*/
|
||||
private isItemHasOpeningBalance(itemId: number): boolean {
|
||||
return !!this.openingBalanceTransactions.get(itemId);
|
||||
public isItemHasOpeningBalance(itemId: number): boolean {
|
||||
return !!this.repository.openingBalanceTransactionsByItemId.get(itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,8 +297,12 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IItem} item -
|
||||
* @returns {IInventoryDetailsOpening}
|
||||
*/
|
||||
private getItemOpeingValuation(item: IItem): IInventoryDetailsOpening {
|
||||
const openingBalance = this.openingBalanceTransactions.get(item.id);
|
||||
public getItemOpeingValuation(
|
||||
item: ModelObject<Item>,
|
||||
): IInventoryDetailsOpening {
|
||||
const openingBalance = this.repository.openingBalanceTransactionsByItemId.get(
|
||||
item.id,
|
||||
);
|
||||
const quantity = defaultTo(get(openingBalance, 'quantity'), 0);
|
||||
const value = defaultTo(get(openingBalance, 'value'), 0);
|
||||
|
||||
@@ -321,10 +319,10 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IItem} item -
|
||||
* @returns {IInventoryDetailsOpening}
|
||||
*/
|
||||
private getItemClosingValuation(
|
||||
item: IItem,
|
||||
transactions: IInventoryDetailsItemTransaction[],
|
||||
openingValuation: IInventoryDetailsOpening
|
||||
public getItemClosingValuation(
|
||||
item: ModelObject<Item>,
|
||||
transactions: ModelObject<InventoryTransaction>[],
|
||||
openingValuation: IInventoryDetailsOpening,
|
||||
): IInventoryDetailsOpening {
|
||||
const value = sumBy(transactions, 'valueMovement.number');
|
||||
const quantity = sumBy(transactions, 'quantityMovement.number');
|
||||
@@ -347,7 +345,7 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IItem} item
|
||||
* @returns {IInventoryDetailsItem}
|
||||
*/
|
||||
private itemsNodeMapper(item: IItem): IInventoryDetailsItem {
|
||||
public itemsNodeMapper(item: ModelObject<Item>): IInventoryDetailsItem {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
@@ -363,9 +361,9 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IItem} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private isNodeTypeEquals(
|
||||
public isNodeTypeEquals(
|
||||
nodeType: string,
|
||||
node: IInventoryDetailsItem
|
||||
node: IInventoryDetailsItem,
|
||||
): boolean {
|
||||
return nodeType === node.nodeType;
|
||||
}
|
||||
@@ -375,8 +373,8 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IInventoryDetailsItem} item
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private isItemNodeHasTransactions(item: IInventoryDetailsItem) {
|
||||
return !!this.inventoryTransactionsByItemId.get(item.id);
|
||||
public isItemNodeHasTransactions(item: IInventoryDetailsItem) {
|
||||
return !!this.repository.inventoryTransactionsByItemId.get(item.id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,11 +382,11 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IInventoryDetailsItem} item
|
||||
* @return {boolean}
|
||||
*/
|
||||
private isFilterNode(item: IInventoryDetailsItem): boolean {
|
||||
public isFilterNode(item: IInventoryDetailsItem): boolean {
|
||||
return R.ifElse(
|
||||
R.curry(this.isNodeTypeEquals)(INodeTypes.ITEM),
|
||||
this.isItemNodeHasTransactions.bind(this),
|
||||
R.always(true)
|
||||
R.always(true),
|
||||
)(item);
|
||||
}
|
||||
|
||||
@@ -397,24 +395,24 @@ export class InventoryDetails extends FinancialSheet {
|
||||
* @param {IInventoryDetailsItem[]} items -
|
||||
* @returns {IInventoryDetailsItem[]}
|
||||
*/
|
||||
private filterItemsNodes(items: IInventoryDetailsItem[]) {
|
||||
public filterItemsNodes(items: IInventoryDetailsItem[]) {
|
||||
const filtered = filterDeep(
|
||||
items,
|
||||
this.isFilterNode.bind(this),
|
||||
MAP_CONFIG
|
||||
MAP_CONFIG,
|
||||
);
|
||||
return defaultTo(filtered, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the items nodes of the report.
|
||||
* @param {IItem} items
|
||||
* @param {ModelObject<Item>[]} items
|
||||
* @returns {IInventoryDetailsItem[]}
|
||||
*/
|
||||
private itemsNodes(items: IItem[]): IInventoryDetailsItem[] {
|
||||
public itemsNodes(items: ModelObject<Item>[]): IInventoryDetailsItem[] {
|
||||
return R.compose(
|
||||
this.filterItemsNodes.bind(this),
|
||||
R.map(this.itemsNodeMapper.bind(this))
|
||||
R.map(this.itemsNodeMapper.bind(this)),
|
||||
)(items);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {
|
||||
IInventoryDetailsQuery,
|
||||
IInvetoryItemDetailsTable,
|
||||
} from '@/interfaces';
|
||||
} from './InventoryItemDetails.types';
|
||||
import { InventoryItemDetailsExportInjectable } from './InventoryItemDetailsExportInjectable';
|
||||
import { InventoryDetailsTableInjectable } from './InventoryItemDetailsTableInjectable';
|
||||
import { InventoryDetailsService } from './InventoryItemDetailsService';
|
||||
import { InventoryDetailsService } from './InventoryItemDetails.service';
|
||||
import { InventoryDetailsTablePdf } from './InventoryItemDetailsTablePdf';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
|
||||
@@ -4,19 +4,129 @@ import moment from 'moment';
|
||||
import { IInventoryDetailsQuery } from './InventoryItemDetails.types';
|
||||
import { Item } from '@/modules/Items/models/Item';
|
||||
import { InventoryTransaction } from '@/modules/InventoryCost/models/InventoryTransaction';
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { transformToMapKeyValue } from '@/utils/transform-to-map-key-value';
|
||||
import { transformToMapBy } from '@/utils/transform-to-map-by';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class InventoryItemDetailsRepository {
|
||||
@Inject(Item.name)
|
||||
readonly itemModel: typeof Item;
|
||||
|
||||
@Inject(InventoryTransaction.name)
|
||||
readonly inventoryTransactionModel: typeof InventoryTransaction;
|
||||
|
||||
@Inject(TenancyContext)
|
||||
readonly tenancyContext: TenancyContext;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {typeof Item} itemModel - Item model.
|
||||
* @param {typeof InventoryTransaction} inventoryTransactionModel - Inventory transaction model.
|
||||
* The items.
|
||||
* @param {ModelObject<Item>[]} items - The items.
|
||||
*/
|
||||
constructor(
|
||||
private readonly itemModel: typeof Item,
|
||||
private readonly inventoryTransactionModel: typeof InventoryTransaction,
|
||||
) {}
|
||||
items: ModelObject<Item>[];
|
||||
|
||||
/**
|
||||
* The opening balance transactions.
|
||||
* @param {ModelObject<InventoryTransaction>[]} openingBalanceTransactions - The opening balance transactions.
|
||||
*/
|
||||
openingBalanceTransactions: ModelObject<InventoryTransaction>[];
|
||||
|
||||
/**
|
||||
* The opening balance transactions by item id.
|
||||
* @param {Map<number, ModelObject<InventoryTransaction>>} openingBalanceTransactionsByItemId - The opening balance transactions by item id.
|
||||
*/
|
||||
openingBalanceTransactionsByItemId: Map<number, ModelObject<InventoryTransaction>>;
|
||||
|
||||
/**
|
||||
* The inventory transactions.
|
||||
* @param {ModelObject<InventoryTransaction>[]} inventoryTransactions - The inventory transactions.
|
||||
*/
|
||||
inventoryTransactions: ModelObject<InventoryTransaction>[];
|
||||
|
||||
/**
|
||||
* The inventory transactions by item id.
|
||||
* @param {Map<number, ModelObject<InventoryTransaction>>} inventoryTransactionsByItemId - The inventory transactions by item id.
|
||||
*/
|
||||
inventoryTransactionsByItemId: Map<number, ModelObject<InventoryTransaction>[]>;
|
||||
|
||||
/**
|
||||
* The filter.
|
||||
* @param {IInventoryDetailsQuery} filter - The filter.
|
||||
*/
|
||||
filter: IInventoryDetailsQuery;
|
||||
|
||||
/**
|
||||
* The base currency.
|
||||
* @param {string} baseCurrency - The base currency.
|
||||
*/
|
||||
baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Set the filter.
|
||||
* @param {IInventoryDetailsQuery} filter - The filter.
|
||||
*/
|
||||
setFilter(filter: IInventoryDetailsQuery) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the repository.
|
||||
*/
|
||||
async asyncInit() {
|
||||
await this.initItems();
|
||||
await this.initOpeningBalanceTransactions();
|
||||
await this.initInventoryTransactions();
|
||||
await this.initBaseCurrency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the items.
|
||||
*/
|
||||
async initItems() {
|
||||
// Retrieves the items.
|
||||
const items = await this.getInventoryItems(this.filter.itemsIds);
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the opening balance transactions.
|
||||
*/
|
||||
async initOpeningBalanceTransactions() {
|
||||
// Retrieves the opening balance transactions.
|
||||
const openingBalanceTransactions = await this.getOpeningBalanceTransactions(
|
||||
this.filter,
|
||||
);
|
||||
this.openingBalanceTransactions = openingBalanceTransactions;
|
||||
this.openingBalanceTransactionsByItemId = transformToMapKeyValue(
|
||||
openingBalanceTransactions,
|
||||
'itemId'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the inventory transactions.
|
||||
*/
|
||||
async initInventoryTransactions() {
|
||||
// Retrieves the inventory transactions.
|
||||
const inventoryTransactions = await this.getItemInventoryTransactions(
|
||||
this.filter,
|
||||
);
|
||||
this.inventoryTransactions = inventoryTransactions;
|
||||
this.inventoryTransactionsByItemId = transformToMapBy(
|
||||
inventoryTransactions,
|
||||
'itemId'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the base currency.
|
||||
*/
|
||||
async initBaseCurrency() {
|
||||
const tenantMetadata = await this.tenancyContext.getTenantMetadata();
|
||||
|
||||
this.baseCurrency = tenantMetadata.baseCurrency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve inventory items.
|
||||
@@ -39,7 +149,7 @@ export class InventoryItemDetailsRepository {
|
||||
* @param {IInventoryDetailsQuery}
|
||||
* @return {Promise<ModelObject<InventoryTransaction>>}
|
||||
*/
|
||||
public async openingBalanceTransactions(
|
||||
public async getOpeningBalanceTransactions(
|
||||
filter: IInventoryDetailsQuery,
|
||||
): Promise<ModelObject<InventoryTransaction>[]> {
|
||||
const openingBalanceDate = moment(filter.fromDate)
|
||||
@@ -95,7 +205,7 @@ export class InventoryItemDetailsRepository {
|
||||
* @param {IInventoryDetailsQuery}
|
||||
* @return {Promise<IInventoryTransaction>}
|
||||
*/
|
||||
public async itemInventoryTransactions(
|
||||
public async getItemInventoryTransactions(
|
||||
filter: IInventoryDetailsQuery,
|
||||
): Promise<ModelObject<InventoryTransaction>[]> {
|
||||
const inventoryTransactions = this.inventoryTransactionModel
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import moment from 'moment';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IInventoryDetailsQuery, IInvetoryItemDetailDOO } from '@/interfaces';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { InventoryDetails } from './InventoryItemDetails';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import InventoryDetailsRepository from './InventoryItemDetailsRepository';
|
||||
import { Tenant } from '@/system/models';
|
||||
import { InventoryDetailsMetaInjectable } from './InventoryItemDetailsMeta';
|
||||
|
||||
@Service()
|
||||
export class InventoryDetailsService extends FinancialSheet {
|
||||
@Inject()
|
||||
private tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
private reportRepo: InventoryDetailsRepository;
|
||||
|
||||
@Inject()
|
||||
private inventoryDetailsMeta: InventoryDetailsMetaInjectable;
|
||||
|
||||
/**
|
||||
* Defaults balance sheet filter query.
|
||||
* @return {IBalanceSheetQuery}
|
||||
*/
|
||||
private get defaultQuery(): IInventoryDetailsQuery {
|
||||
return {
|
||||
fromDate: moment().startOf('month').format('YYYY-MM-DD'),
|
||||
toDate: moment().format('YYYY-MM-DD'),
|
||||
itemsIds: [],
|
||||
numberFormat: {
|
||||
precision: 2,
|
||||
divideOn1000: false,
|
||||
showZero: false,
|
||||
formatMoney: 'total',
|
||||
negativeFormat: 'mines',
|
||||
},
|
||||
noneTransactions: false,
|
||||
branchesIds: [],
|
||||
warehousesIds: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the inventory details report data.
|
||||
* @param {number} tenantId -
|
||||
* @param {IInventoryDetailsQuery} query -
|
||||
* @return {Promise<IInvetoryItemDetailDOO>}
|
||||
*/
|
||||
public async inventoryDetails(
|
||||
tenantId: number,
|
||||
query: IInventoryDetailsQuery
|
||||
): Promise<IInvetoryItemDetailDOO> {
|
||||
const i18n = this.tenancy.i18n(tenantId);
|
||||
|
||||
const tenant = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
const filter = {
|
||||
...this.defaultQuery,
|
||||
...query,
|
||||
};
|
||||
// Retrieves the items.
|
||||
const items = await this.reportRepo.getInventoryItems(
|
||||
tenantId,
|
||||
filter.itemsIds
|
||||
);
|
||||
// Opening balance transactions.
|
||||
const openingBalanceTransactions =
|
||||
await this.reportRepo.openingBalanceTransactions(tenantId, filter);
|
||||
|
||||
// Retrieves the inventory transaction.
|
||||
const inventoryTransactions =
|
||||
await this.reportRepo.itemInventoryTransactions(tenantId, filter);
|
||||
|
||||
// Inventory details report mapper.
|
||||
const inventoryDetailsInstance = new InventoryDetails(
|
||||
items,
|
||||
openingBalanceTransactions,
|
||||
inventoryTransactions,
|
||||
filter,
|
||||
tenant.metadata.baseCurrency,
|
||||
i18n
|
||||
);
|
||||
const meta = await this.inventoryDetailsMeta.meta(tenantId, query);
|
||||
|
||||
return {
|
||||
data: inventoryDetailsInstance.reportData(),
|
||||
query: filter,
|
||||
meta,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { InventoryDetailsTable } from './InventoryItemDetailsTable';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import {
|
||||
IInventoryDetailsQuery,
|
||||
IInvetoryItemDetailsTable,
|
||||
} from './InventoryItemDetails.types';
|
||||
import { InventoryDetailsService } from './InventoryItemDetailsService';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import { InventoryDetailsService } from './InventoryItemDetails.service';
|
||||
import { InventoryItemDetailsTable } from './InventoryItemDetailsTable';
|
||||
|
||||
@Injectable()
|
||||
export class InventoryDetailsTableInjectable {
|
||||
@@ -24,7 +24,8 @@ export class InventoryDetailsTableInjectable {
|
||||
): Promise<IInvetoryItemDetailsTable> {
|
||||
const inventoryDetails =
|
||||
await this.inventoryDetails.inventoryDetails(query);
|
||||
const table = new InventoryDetailsTable(inventoryDetails, this.i18n);
|
||||
|
||||
const table = new InventoryItemDetailsTable(inventoryDetails.data, this.i18n);
|
||||
|
||||
return {
|
||||
table: {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { IInventoryDetailsQuery } from "./InventoryItemDetails.types";
|
||||
|
||||
export const HtmlTableCustomCss = `
|
||||
table tr.row-type--item td,
|
||||
table tr.row-type--opening-entry td,
|
||||
@@ -5,3 +7,32 @@ table tr.row-type--closing-entry td{
|
||||
font-weight: 500;
|
||||
}
|
||||
`;
|
||||
|
||||
export const getInventoryItemDetailsDefaultQuery =
|
||||
(): IInventoryDetailsQuery => {
|
||||
return {
|
||||
fromDate: moment().startOf('month').format('YYYY-MM-DD'),
|
||||
toDate: moment().format('YYYY-MM-DD'),
|
||||
itemsIds: [],
|
||||
numberFormat: {
|
||||
precision: 2,
|
||||
divideOn1000: false,
|
||||
showZero: false,
|
||||
formatMoney: 'total',
|
||||
negativeFormat: 'mines',
|
||||
},
|
||||
noneTransactions: false,
|
||||
branchesIds: [],
|
||||
warehousesIds: [],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
export const MAP_CONFIG = { childrenPath: 'children', pathFormat: 'array' };
|
||||
|
||||
export enum INodeTypes {
|
||||
ITEM = 'item',
|
||||
TRANSACTION = 'transaction',
|
||||
OPENING_ENTRY = 'OPENING_ENTRY',
|
||||
CLOSING_ENTRY = 'CLOSING_ENTRY',
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ModelObject } from 'objection';
|
||||
import { sumBy, get, isEmpty } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
@@ -6,41 +7,29 @@ import {
|
||||
IInventoryValuationStatement,
|
||||
IInventoryValuationTotal,
|
||||
} from './InventoryValuationSheet.types';
|
||||
import { ModelObject } from 'objection';
|
||||
import { Item } from '@/modules/Items/models/Item';
|
||||
import { InventoryCostLotTracker } from '@/modules/InventoryCost/models/InventoryCostLotTracker';
|
||||
import { FinancialSheet } from '../../common/FinancialSheet';
|
||||
import { transformToMap } from '@/utils/transform-to-key';
|
||||
import { InventoryValuationSheetRepository } from './InventoryValuationSheetRepository';
|
||||
import { allPassedConditionsPass } from '@/utils/all-conditions-passed';
|
||||
|
||||
export class InventoryValuationSheet extends FinancialSheet {
|
||||
readonly query: IInventoryValuationReportQuery;
|
||||
readonly items: ModelObject<Item>[];
|
||||
readonly INInventoryCostLots: Map<number, InventoryCostLotTracker>;
|
||||
readonly OUTInventoryCostLots: Map<number, InventoryCostLotTracker>;
|
||||
readonly baseCurrency: string;
|
||||
readonly repository: InventoryValuationSheetRepository;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IInventoryValuationReportQuery} query
|
||||
* @param {ModelObject<Item>[]} items
|
||||
* @param {Map<number, InventoryCostLotTracker[]>} INInventoryCostLots
|
||||
* @param {Map<number, InventoryCostLotTracker[]>} OUTInventoryCostLots
|
||||
* @param {string} baseCurrency
|
||||
* @param {IInventoryValuationReportQuery} query - Inventory valuation query.
|
||||
* @param {InventoryValuationSheetRepository} repository - Inventory valuation sheet repository.
|
||||
*/
|
||||
constructor(
|
||||
query: IInventoryValuationReportQuery,
|
||||
items: ModelObject<Item>[],
|
||||
INInventoryCostLots: Map<number, InventoryCostLotTracker[]>,
|
||||
OUTInventoryCostLots: Map<number, InventoryCostLotTracker[]>,
|
||||
baseCurrency: string,
|
||||
repository: InventoryValuationSheetRepository,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.query = query;
|
||||
this.items = items;
|
||||
this.INInventoryCostLots = transformToMap(INInventoryCostLots, 'itemId');
|
||||
this.OUTInventoryCostLots = transformToMap(OUTInventoryCostLots, 'itemId');
|
||||
this.baseCurrency = baseCurrency;
|
||||
this.repository = repository;
|
||||
this.numberFormat = this.query.numberFormat;
|
||||
}
|
||||
|
||||
@@ -70,7 +59,7 @@ export class InventoryValuationSheet extends FinancialSheet {
|
||||
cost: number;
|
||||
quantity: number;
|
||||
} {
|
||||
return this.getItemTransaction(this.INInventoryCostLots, itemId);
|
||||
return this.getItemTransaction(this.repository.INInventoryCostLots, itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +70,7 @@ export class InventoryValuationSheet extends FinancialSheet {
|
||||
cost: number;
|
||||
quantity: number;
|
||||
} {
|
||||
return this.getItemTransaction(this.OUTInventoryCostLots, itemId);
|
||||
return this.getItemTransaction(this.repository.OUTInventoryCostLots, itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,10 +137,13 @@ export class InventoryValuationSheet extends FinancialSheet {
|
||||
private filterNoneTransactions = (
|
||||
valuationItem: IInventoryValuationItem,
|
||||
): boolean => {
|
||||
const transactionIN = this.INInventoryCostLots.get(valuationItem.id);
|
||||
const transactionOUT = this.OUTInventoryCostLots.get(valuationItem.id);
|
||||
|
||||
return transactionOUT || transactionIN;
|
||||
const transactionIN = this.repository.INInventoryCostLots.get(
|
||||
valuationItem.id,
|
||||
);
|
||||
const transactionOUT = this.repository.OUTInventoryCostLots.get(
|
||||
valuationItem.id,
|
||||
);
|
||||
return !isEmpty(transactionOUT) || !isEmpty(transactionIN);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -200,8 +192,8 @@ export class InventoryValuationSheet extends FinancialSheet {
|
||||
* @param {IItem[]} items
|
||||
* @returns {IInventoryValuationItem[]}
|
||||
*/
|
||||
private itemsMapper = (items: IItem[]): IInventoryValuationItem[] => {
|
||||
return this.items.map(this.itemMapper.bind(this));
|
||||
private itemsMapper = (items: ModelObject<Item>[]): IInventoryValuationItem[] => {
|
||||
return this.repository.inventoryItems.map(this.itemMapper.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -230,7 +222,7 @@ export class InventoryValuationSheet extends FinancialSheet {
|
||||
return R.compose(
|
||||
R.when(this.isItemsPostFilter, this.itemsFilter),
|
||||
this.itemsMapper,
|
||||
)(this.items);
|
||||
)(this.repository.inventoryItems);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,17 +1,109 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
import { IInventoryValuationReportQuery } from './InventoryValuationSheet.types';
|
||||
import { InventoryCostLotTracker } from '@/modules/InventoryCost/models/InventoryCostLotTracker';
|
||||
import { ModelObject } from 'objection';
|
||||
import { Item } from '@/modules/Items/models/Item';
|
||||
import { transformToMap } from '@/utils/transform-to-key';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
|
||||
|
||||
@Injectable()
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class InventoryValuationSheetRepository {
|
||||
asyncInit() {
|
||||
const inventoryItems = await Item.query().onBuild((q) => {
|
||||
@Inject(TenancyContext)
|
||||
private readonly tenancyContext: TenancyContext;
|
||||
|
||||
@Inject(InventoryCostLotTracker.name)
|
||||
private readonly inventoryCostLotTracker: typeof InventoryCostLotTracker;
|
||||
|
||||
@Inject(Item.name)
|
||||
private readonly itemModel: typeof Item;
|
||||
|
||||
/**
|
||||
* The filter.
|
||||
* @param {IInventoryValuationReportQuery} value
|
||||
*/
|
||||
filter: IInventoryValuationReportQuery;
|
||||
|
||||
/**
|
||||
* The inventory items.
|
||||
* @param {ModelObject<Item>[]} value
|
||||
*/
|
||||
inventoryItems: ModelObject<Item>[];
|
||||
|
||||
/**
|
||||
* The inventory cost `IN` transactions.
|
||||
* @param {ModelObject<InventoryCostLotTracker>[]} value
|
||||
*/
|
||||
INTransactions: ModelObject<InventoryCostLotTracker>[];
|
||||
|
||||
/**
|
||||
* The inventory cost `IN` transactions map.
|
||||
* @param {Map<number, InventoryCostLotTracker[]>} value
|
||||
*/
|
||||
INInventoryCostLots: Map<number, InventoryCostLotTracker[]>;
|
||||
|
||||
/**
|
||||
* The inventory cost `OUT` transactions.
|
||||
* @param {ModelObject<InventoryCostLotTracker>[]} value
|
||||
*/
|
||||
OUTTransactions: ModelObject<InventoryCostLotTracker>[];
|
||||
|
||||
/**
|
||||
* The inventory cost `OUT` transactions map.
|
||||
* @param {Map<number, InventoryCostLotTracker[]>} value
|
||||
*/
|
||||
OUTInventoryCostLots: Map<number, InventoryCostLotTracker[]>;
|
||||
|
||||
/**
|
||||
* The base currency.
|
||||
* @param {string} value
|
||||
*/
|
||||
baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Set the filter.
|
||||
* @param {IInventoryValuationReportQuery} filter
|
||||
*/
|
||||
setFilter(filter: IInventoryValuationReportQuery) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the repository.
|
||||
*/
|
||||
async asyncInit() {
|
||||
await this.asyncItems();
|
||||
await this.asyncCostLotsTransactions();
|
||||
await this.asyncBaseCurrency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the base currency.
|
||||
*/
|
||||
async asyncBaseCurrency() {
|
||||
const tenantMetadata = await this.tenancyContext.getTenantMetadata();
|
||||
this.baseCurrency = tenantMetadata.baseCurrency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the inventory items.
|
||||
*/
|
||||
async asyncItems() {
|
||||
const inventoryItems = await this.itemModel.query().onBuild((q) => {
|
||||
q.where('type', 'inventory');
|
||||
|
||||
if (filter.itemsIds.length > 0) {
|
||||
q.whereIn('id', filter.itemsIds);
|
||||
if (this.filter.itemsIds.length > 0) {
|
||||
q.whereIn('id', this.filter.itemsIds);
|
||||
}
|
||||
});
|
||||
const inventoryItemsIds = inventoryItems.map((item) => item.id);
|
||||
this.inventoryItems = inventoryItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the inventory cost `IN` and `OUT` transactions.
|
||||
*/
|
||||
async asyncCostLotsTransactions() {
|
||||
const inventoryItemsIds = this.inventoryItems.map((item) => item.id);
|
||||
|
||||
const commonQuery = (builder) => {
|
||||
builder.whereIn('item_id', inventoryItemsIds);
|
||||
@@ -21,23 +113,29 @@ export class InventoryValuationSheetRepository {
|
||||
builder.select('itemId');
|
||||
builder.groupBy('itemId');
|
||||
|
||||
if (!isEmpty(query.branchesIds)) {
|
||||
builder.modify('filterByBranches', query.branchesIds);
|
||||
if (!isEmpty(this.filter.branchesIds)) {
|
||||
builder.modify('filterByBranches', this.filter.branchesIds);
|
||||
}
|
||||
if (!isEmpty(query.warehousesIds)) {
|
||||
builder.modify('filterByWarehouses', query.warehousesIds);
|
||||
if (!isEmpty(this.filter.warehousesIds)) {
|
||||
builder.modify('filterByWarehouses', this.filter.warehousesIds);
|
||||
}
|
||||
};
|
||||
// Retrieve the inventory cost `IN` transactions.
|
||||
const INTransactions = await InventoryCostLotTracker.query()
|
||||
const INTransactions = await this.inventoryCostLotTracker
|
||||
.query()
|
||||
.onBuild(commonQuery)
|
||||
.where('direction', 'IN');
|
||||
|
||||
// Retrieve the inventory cost `OUT` transactions.
|
||||
const OUTTransactions = await InventoryCostLotTracker.query()
|
||||
const OUTTransactions = await this.inventoryCostLotTracker
|
||||
.query()
|
||||
.onBuild(commonQuery)
|
||||
.where('direction', 'OUT');
|
||||
|
||||
|
||||
this.INTransactions = INTransactions;
|
||||
this.OUTTransactions = OUTTransactions;
|
||||
|
||||
this.INInventoryCostLots = transformToMap(INTransactions, 'itemId');
|
||||
this.OUTInventoryCostLots = transformToMap(OUTTransactions, 'itemId');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,19 +9,15 @@ import { InventoryValuationMetaInjectable } from './InventoryValuationSheetMeta'
|
||||
import { getInventoryValuationDefaultQuery } from './_constants';
|
||||
import { InventoryCostLotTracker } from '@/modules/InventoryCost/models/InventoryCostLotTracker';
|
||||
import { Item } from '@/modules/Items/models/Item';
|
||||
import { InventoryValuationSheetRepository } from './InventoryValuationSheetRepository';
|
||||
import { InventoryValuationSheet } from './InventoryValuationSheet';
|
||||
|
||||
@Injectable()
|
||||
export class InventoryValuationSheetService {
|
||||
constructor(
|
||||
private readonly inventoryService: InventoryService,
|
||||
private readonly inventoryValuationMeta: InventoryValuationMetaInjectable,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
|
||||
@Inject(Item.name)
|
||||
private readonly itemModel: typeof Item,
|
||||
|
||||
@Inject(InventoryCostLotTracker.name)
|
||||
private readonly inventoryCostLotTracker: typeof InventoryCostLotTracker,
|
||||
private readonly inventoryValuationSheetRepository: InventoryValuationSheetRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -35,12 +31,12 @@ export class InventoryValuationSheetService {
|
||||
...getInventoryValuationDefaultQuery(),
|
||||
...query,
|
||||
};
|
||||
this.inventoryValuationSheetRepository.setFilter(filter);
|
||||
await this.inventoryValuationSheetRepository.asyncInit();
|
||||
|
||||
const inventoryValuationInstance = new InventoryValuationSheet(
|
||||
filter,
|
||||
inventoryItems,
|
||||
INTransactions,
|
||||
OUTTransactions,
|
||||
tenant.metadata.baseCurrency,
|
||||
this.inventoryValuationSheetRepository,
|
||||
);
|
||||
// Retrieve the inventory valuation report data.
|
||||
const inventoryValuationData = inventoryValuationInstance.reportData();
|
||||
@@ -51,9 +47,7 @@ export class InventoryValuationSheetService {
|
||||
// Triggers `onInventoryValuationViewed` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.reports.onInventoryValuationViewed,
|
||||
{
|
||||
query,
|
||||
},
|
||||
{ query },
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as moment from 'moment';
|
||||
import { getTransactionTypeLabel } from '@/modules/BankingTransactions/utils';
|
||||
import { TInventoryTransactionDirection } from '../types/InventoryCost.types';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
import { InventoryTransactionMeta } from './InventoryTransactionMeta';
|
||||
|
||||
export class InventoryTransaction extends TenantBaseModel {
|
||||
date: Date | string;
|
||||
@@ -21,6 +22,8 @@ export class InventoryTransaction extends TenantBaseModel {
|
||||
|
||||
warehouseId?: number;
|
||||
|
||||
meta?: InventoryTransactionMeta;
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,10 @@ import { BaseModel } from '@/models/Model';
|
||||
import { Model, raw } from 'objection';
|
||||
|
||||
export class InventoryTransactionMeta extends BaseModel {
|
||||
transactionNumber!: string;
|
||||
description!: string;
|
||||
inventoryTransactionId!: number;
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export const transformToMapKeyValue = <T, K extends string | number>(
|
||||
collection: T[],
|
||||
key: keyof T,
|
||||
): Map<K, T> => {
|
||||
return new Map(collection.map((item) => [item[key], item]));
|
||||
};
|
||||
Reference in New Issue
Block a user