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