mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
feat: export purchases by items to csv/xlsx (#327)
This commit is contained in:
@@ -1,17 +1,18 @@
|
|||||||
import { Router, Request, Response, NextFunction } from 'express';
|
import { Router, Request, Response, NextFunction } from 'express';
|
||||||
import { query, ValidationChain } from 'express-validator';
|
import { query, ValidationChain } from 'express-validator';
|
||||||
import moment from 'moment';
|
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||||
import PurchasesByItemsService from '@/services/FinancialStatements/PurchasesByItems/PurchasesByItemsService';
|
import { PurchasesByItemsService } from '@/services/FinancialStatements/PurchasesByItems/PurchasesByItemsService';
|
||||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||||
|
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||||
|
import { PurcahsesByItemsApplication } from '@/services/FinancialStatements/PurchasesByItems/PurchasesByItemsApplication';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class PurchasesByItemReportController extends BaseFinancialReportController {
|
export default class PurchasesByItemReportController extends BaseFinancialReportController {
|
||||||
@Inject()
|
@Inject()
|
||||||
purchasesByItemsService: PurchasesByItemsService;
|
private purchasesByItemsApp: PurcahsesByItemsApplication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router constructor.
|
* Router constructor.
|
||||||
@@ -63,20 +64,47 @@ export default class PurchasesByItemReportController extends BaseFinancialReport
|
|||||||
* @param {Request} req -
|
* @param {Request} req -
|
||||||
* @param {Response} res -
|
* @param {Response} res -
|
||||||
*/
|
*/
|
||||||
async purchasesByItems(req: Request, res: Response, next: NextFunction) {
|
public async purchasesByItems(req: Request, res: Response) {
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const filter = this.matchedQueryData(req);
|
const filter = this.matchedQueryData(req);
|
||||||
|
|
||||||
try {
|
const accept = this.accepts(req);
|
||||||
const { data, query, meta } =
|
|
||||||
await this.purchasesByItemsService.purchasesByItems(tenantId, filter);
|
const acceptType = accept.types([
|
||||||
return res.status(200).send({
|
ACCEPT_TYPE.APPLICATION_JSON,
|
||||||
meta: this.transfromToResponse(meta),
|
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||||
data: this.transfromToResponse(data),
|
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||||
query: this.transfromToResponse(query),
|
ACCEPT_TYPE.APPLICATION_CSV,
|
||||||
});
|
]);
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
// JSON table response format.
|
||||||
|
if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) {
|
||||||
|
const table = await this.purchasesByItemsApp.table(tenantId, filter);
|
||||||
|
|
||||||
|
return res.status(200).send(table);
|
||||||
|
// CSV response format.
|
||||||
|
} else if (ACCEPT_TYPE.APPLICATION_CSV === acceptType) {
|
||||||
|
const buffer = await this.purchasesByItemsApp.csv(tenantId, filter);
|
||||||
|
|
||||||
|
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
||||||
|
res.setHeader('Content-Type', 'text/csv');
|
||||||
|
|
||||||
|
return res.send(buffer);
|
||||||
|
// Xlsx response format.
|
||||||
|
} else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) {
|
||||||
|
const buffer = await this.purchasesByItemsApp.xlsx(tenantId, filter);
|
||||||
|
|
||||||
|
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
|
||||||
|
res.setHeader(
|
||||||
|
'Content-Type',
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
);
|
||||||
|
return res.send(buffer);
|
||||||
|
// Json response format.
|
||||||
|
} else {
|
||||||
|
const sheet = await this.purchasesByItemsApp.sheet(tenantId, filter);
|
||||||
|
|
||||||
|
return res.status(200).send(sheet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
IAgingPeriod,
|
IAgingPeriod,
|
||||||
IAgingPeriodTotal,
|
|
||||||
IAgingAmount,
|
|
||||||
IAgingSummaryQuery,
|
IAgingSummaryQuery,
|
||||||
IAgingSummaryTotal,
|
IAgingSummaryTotal,
|
||||||
IAgingSummaryContact,
|
IAgingSummaryContact,
|
||||||
IAgingSummaryData,
|
IAgingSummaryData,
|
||||||
} from './AgingReport';
|
} from './AgingReport';
|
||||||
import { INumberFormatQuery } from './FinancialStatements';
|
|
||||||
import { IFinancialTable } from './Table';
|
import { IFinancialTable } from './Table';
|
||||||
|
|
||||||
export interface IAPAgingSummaryQuery extends IAgingSummaryQuery {
|
export interface IAPAgingSummaryQuery extends IAgingSummaryQuery {
|
||||||
|
|||||||
54
packages/server/src/interfaces/PurchasesByItemsSheet.ts
Normal file
54
packages/server/src/interfaces/PurchasesByItemsSheet.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { INumberFormatQuery } from './FinancialStatements';
|
||||||
|
import { IFinancialTable } from './Table';
|
||||||
|
|
||||||
|
export interface IPurchasesByItemsReportQuery {
|
||||||
|
fromDate: Date | string;
|
||||||
|
toDate: Date | string;
|
||||||
|
itemsIds: number[];
|
||||||
|
numberFormat: INumberFormatQuery;
|
||||||
|
noneTransactions: boolean;
|
||||||
|
onlyActive: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPurchasesByItemsSheetMeta {
|
||||||
|
organizationName: string;
|
||||||
|
baseCurrency: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPurchasesByItemsItem {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
code: string;
|
||||||
|
quantitySold: number;
|
||||||
|
soldCost: number;
|
||||||
|
averageSellPrice: number;
|
||||||
|
|
||||||
|
quantitySoldFormatted: string;
|
||||||
|
soldCostFormatted: string;
|
||||||
|
averageSellPriceFormatted: string;
|
||||||
|
currencyCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPurchasesByItemsTotal {
|
||||||
|
quantitySold: number;
|
||||||
|
soldCost: number;
|
||||||
|
quantitySoldFormatted: string;
|
||||||
|
soldCostFormatted: string;
|
||||||
|
currencyCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IPurchasesByItemsSheetData = {
|
||||||
|
items: IPurchasesByItemsItem[];
|
||||||
|
total: IPurchasesByItemsTotal;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IPurchasesByItemsSheet {
|
||||||
|
data: IPurchasesByItemsSheetData;
|
||||||
|
query: IPurchasesByItemsReportQuery;
|
||||||
|
meta: IPurchasesByItemsSheetMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPurchasesByItemsTable extends IFinancialTable {
|
||||||
|
query: IPurchasesByItemsReportQuery;
|
||||||
|
meta: IPurchasesByItemsSheetMeta;
|
||||||
|
}
|
||||||
@@ -3,8 +3,10 @@ import {
|
|||||||
IAccount,
|
IAccount,
|
||||||
IAccountCreateDTO,
|
IAccountCreateDTO,
|
||||||
IAccountEditDTO,
|
IAccountEditDTO,
|
||||||
|
IAccountResponse,
|
||||||
IAccountsFilter,
|
IAccountsFilter,
|
||||||
IAccountsTransactionsFilter,
|
IAccountsTransactionsFilter,
|
||||||
|
IFilterMeta,
|
||||||
IGetAccountTransactionPOJO,
|
IGetAccountTransactionPOJO,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import { CreateAccount } from './CreateAccount';
|
import { CreateAccount } from './CreateAccount';
|
||||||
@@ -14,6 +16,7 @@ import { ActivateAccount } from './ActivateAccount';
|
|||||||
import { GetAccounts } from './GetAccounts';
|
import { GetAccounts } from './GetAccounts';
|
||||||
import { GetAccount } from './GetAccount';
|
import { GetAccount } from './GetAccount';
|
||||||
import { GetAccountTransactions } from './GetAccountTransactions';
|
import { GetAccountTransactions } from './GetAccountTransactions';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class AccountsApplication {
|
export class AccountsApplication {
|
||||||
@Inject()
|
@Inject()
|
||||||
@@ -115,9 +118,12 @@ export class AccountsApplication {
|
|||||||
* Retrieves the accounts list.
|
* Retrieves the accounts list.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {IAccountsFilter} filterDTO
|
* @param {IAccountsFilter} filterDTO
|
||||||
* @returns
|
* @returns {Promise<{ accounts: IAccountResponse[]; filterMeta: IFilterMeta }>}
|
||||||
*/
|
*/
|
||||||
public getAccounts = (tenantId: number, filterDTO: IAccountsFilter) => {
|
public getAccounts = (
|
||||||
|
tenantId: number,
|
||||||
|
filterDTO: IAccountsFilter
|
||||||
|
): Promise<{ accounts: IAccountResponse[]; filterMeta: IFilterMeta }> => {
|
||||||
return this.getAccountsService.getAccountsList(tenantId, filterDTO);
|
return this.getAccountsService.getAccountsList(tenantId, filterDTO);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,36 +2,34 @@ import { get, isEmpty, sumBy } from 'lodash';
|
|||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import FinancialSheet from '../FinancialSheet';
|
import FinancialSheet from '../FinancialSheet';
|
||||||
import { allPassedConditionsPass, transformToMap } from 'utils';
|
import { allPassedConditionsPass, transformToMap } from 'utils';
|
||||||
|
import { IAccountTransaction, IItem } from '@/interfaces';
|
||||||
import {
|
import {
|
||||||
IAccountTransaction,
|
IPurchasesByItemsItem,
|
||||||
IInventoryValuationTotal,
|
IPurchasesByItemsReportQuery,
|
||||||
IInventoryValuationItem,
|
IPurchasesByItemsSheetData,
|
||||||
IInventoryValuationReportQuery,
|
IPurchasesByItemsTotal,
|
||||||
IInventoryValuationStatement,
|
} from '@/interfaces/PurchasesByItemsSheet';
|
||||||
IItem,
|
|
||||||
} from '@/interfaces';
|
|
||||||
|
|
||||||
export default class InventoryValuationReport extends FinancialSheet {
|
export class PurchasesByItems extends FinancialSheet {
|
||||||
readonly baseCurrency: string;
|
readonly baseCurrency: string;
|
||||||
readonly items: IItem[];
|
readonly items: IItem[];
|
||||||
readonly itemsTransactions: Map<number, IAccountTransaction>;
|
readonly itemsTransactions: Map<number, IAccountTransaction>;
|
||||||
readonly query: IInventoryValuationReportQuery;
|
readonly query: IPurchasesByItemsReportQuery;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor method.
|
* Constructor method.
|
||||||
* @param {IInventoryValuationReportQuery} query
|
* @param {IPurchasesByItemsReportQuery} query
|
||||||
* @param {IItem[]} items
|
* @param {IItem[]} items
|
||||||
* @param {IAccountTransaction[]} itemsTransactions
|
* @param {IAccountTransaction[]} itemsTransactions
|
||||||
* @param {string} baseCurrency
|
* @param {string} baseCurrency
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
query: IInventoryValuationReportQuery,
|
query: IPurchasesByItemsReportQuery,
|
||||||
items: IItem[],
|
items: IItem[],
|
||||||
itemsTransactions: IAccountTransaction[],
|
itemsTransactions: IAccountTransaction[],
|
||||||
baseCurrency: string
|
baseCurrency: string
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.baseCurrency = baseCurrency;
|
this.baseCurrency = baseCurrency;
|
||||||
this.items = items;
|
this.items = items;
|
||||||
this.itemsTransactions = transformToMap(itemsTransactions, 'itemId');
|
this.itemsTransactions = transformToMap(itemsTransactions, 'itemId');
|
||||||
@@ -98,7 +96,7 @@ export default class InventoryValuationReport extends FinancialSheet {
|
|||||||
* @param {IInventoryValuationItem} item
|
* @param {IInventoryValuationItem} item
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
private itemSectionMapper = (item: IItem): IInventoryValuationItem => {
|
private itemSectionMapper = (item: IItem): IPurchasesByItemsItem => {
|
||||||
const meta = this.getItemTransaction(item.id);
|
const meta = this.getItemTransaction(item.id);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -145,9 +143,9 @@ export default class InventoryValuationReport extends FinancialSheet {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the items sections.
|
* Retrieve the items sections.
|
||||||
* @returns {IInventoryValuationItem[]}
|
* @returns {IPurchasesByItemsItem[]}
|
||||||
*/
|
*/
|
||||||
private itemsSection = (): IInventoryValuationItem[] => {
|
private itemsSection = (): IPurchasesByItemsItem[] => {
|
||||||
return R.compose(
|
return R.compose(
|
||||||
R.when(this.isItemsPostFilter, this.itemsFilter),
|
R.when(this.isItemsPostFilter, this.itemsFilter),
|
||||||
this.itemsMapper
|
this.itemsMapper
|
||||||
@@ -156,10 +154,10 @@ export default class InventoryValuationReport extends FinancialSheet {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the total section of the sheet.
|
* Retrieve the total section of the sheet.
|
||||||
* @param {IInventoryValuationItem[]} items
|
* @param {IPurchasesByItemsItem[]} items
|
||||||
* @returns {IInventoryValuationTotal}
|
* @returns {IPurchasesByItemsTotal}
|
||||||
*/
|
*/
|
||||||
totalSection(items: IInventoryValuationItem[]): IInventoryValuationTotal {
|
private totalSection(items: IPurchasesByItemsItem[]): IPurchasesByItemsTotal {
|
||||||
const quantityPurchased = sumBy(items, (item) => item.quantityPurchased);
|
const quantityPurchased = sumBy(items, (item) => item.quantityPurchased);
|
||||||
const purchaseCost = sumBy(items, (item) => item.purchaseCost);
|
const purchaseCost = sumBy(items, (item) => item.purchaseCost);
|
||||||
|
|
||||||
@@ -176,12 +174,12 @@ export default class InventoryValuationReport extends FinancialSheet {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the sheet data.
|
* Retrieve the sheet data.
|
||||||
* @returns
|
* @returns {IInventoryValuationStatement}
|
||||||
*/
|
*/
|
||||||
reportData(): IInventoryValuationStatement {
|
public reportData(): IPurchasesByItemsSheetData {
|
||||||
const items = this.itemsSection();
|
const items = this.itemsSection();
|
||||||
const total = this.totalSection(items);
|
const total = this.totalSection(items);
|
||||||
|
|
||||||
return items.length > 0 ? { items, total } : {};
|
return { items, total };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import { PurchasesByItemsExport } from './PurchasesByItemsExport';
|
||||||
|
import {
|
||||||
|
IPurchasesByItemsReportQuery,
|
||||||
|
IPurchasesByItemsSheet,
|
||||||
|
IPurchasesByItemsTable,
|
||||||
|
} from '@/interfaces/PurchasesByItemsSheet';
|
||||||
|
import { PurchasesByItemsTableInjectable } from './PurchasesByItemsTableInjectable';
|
||||||
|
import { PurchasesByItemsService } from './PurchasesByItemsService';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class PurcahsesByItemsApplication {
|
||||||
|
@Inject()
|
||||||
|
private purchasesByItemsSheet: PurchasesByItemsService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private purchasesByItemsTable: PurchasesByItemsTableInjectable;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private purchasesByItemsExport: PurchasesByItemsExport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the purchases by items in json format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IPurchasesByItemsReportQuery} query
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public sheet(
|
||||||
|
tenantId: number,
|
||||||
|
query: IPurchasesByItemsReportQuery
|
||||||
|
): Promise<IPurchasesByItemsSheet> {
|
||||||
|
return this.purchasesByItemsSheet.purchasesByItems(tenantId, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the purchases by items in table format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IPurchasesByItemsReportQuery} query
|
||||||
|
* @returns {Promise<IPurchasesByItemsTable>}
|
||||||
|
*/
|
||||||
|
public table(
|
||||||
|
tenantId: number,
|
||||||
|
query: IPurchasesByItemsReportQuery
|
||||||
|
): Promise<IPurchasesByItemsTable> {
|
||||||
|
return this.purchasesByItemsTable.table(tenantId, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the purchases by items in csv format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IPurchasesByItemsReportQuery} query
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
public csv(
|
||||||
|
tenantId: number,
|
||||||
|
query: IPurchasesByItemsReportQuery
|
||||||
|
): Promise<string> {
|
||||||
|
return this.purchasesByItemsExport.csv(tenantId, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the purchases by items in xlsx format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IPurchasesByItemsReportQuery} query
|
||||||
|
* @returns {Promise<Buffer>}
|
||||||
|
*/
|
||||||
|
public xlsx(
|
||||||
|
tenantId: number,
|
||||||
|
query: IPurchasesByItemsReportQuery
|
||||||
|
): Promise<Buffer> {
|
||||||
|
return this.purchasesByItemsExport.xlsx(tenantId, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { TableSheet } from '@/lib/Xlsx/TableSheet';
|
||||||
|
import { PurchasesByItemsTableInjectable } from './PurchasesByItemsTableInjectable';
|
||||||
|
import { IPurchasesByItemsReportQuery } from '@/interfaces/PurchasesByItemsSheet';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class PurchasesByItemsExport {
|
||||||
|
@Inject()
|
||||||
|
private purchasesByItemsTable: PurchasesByItemsTableInjectable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the purchases by items sheet in XLSX format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IPurchasesByItemsReportQuery} query
|
||||||
|
* @returns {Promise<Buffer>}
|
||||||
|
*/
|
||||||
|
public async xlsx(
|
||||||
|
tenantId: number,
|
||||||
|
query: IPurchasesByItemsReportQuery
|
||||||
|
): Promise<Buffer> {
|
||||||
|
const table = await this.purchasesByItemsTable.table(tenantId, query);
|
||||||
|
|
||||||
|
const tableSheet = new TableSheet(table.table);
|
||||||
|
const tableCsv = tableSheet.convertToXLSX();
|
||||||
|
|
||||||
|
return tableSheet.convertToBuffer(tableCsv, 'xlsx');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the purchases by items sheet in CSV format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IPurchasesByItemsReportQuery} query
|
||||||
|
* @returns {Promise<Buffer>}
|
||||||
|
*/
|
||||||
|
public async csv(
|
||||||
|
tenantId: number,
|
||||||
|
query: IPurchasesByItemsReportQuery
|
||||||
|
): Promise<string> {
|
||||||
|
const table = await this.purchasesByItemsTable.table(tenantId, query);
|
||||||
|
|
||||||
|
const tableSheet = new TableSheet(table.table);
|
||||||
|
const tableCsv = tableSheet.convertToCSV();
|
||||||
|
|
||||||
|
return tableCsv;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {
|
import { Service, Inject } from 'typedi';
|
||||||
IInventoryValuationReportQuery,
|
|
||||||
IInventoryValuationStatement,
|
|
||||||
IInventoryValuationSheetMeta,
|
|
||||||
} from '@/interfaces';
|
|
||||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import PurchasesByItems from './PurchasesByItems';
|
import { PurchasesByItems } from './PurchasesByItems';
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
|
import {
|
||||||
|
IPurchasesByItemsReportQuery,
|
||||||
|
IPurchasesByItemsSheet,
|
||||||
|
IPurchasesByItemsSheetMeta,
|
||||||
|
} from '@/interfaces/PurchasesByItemsSheet';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class InventoryValuationReportService {
|
export class PurchasesByItemsService {
|
||||||
@Inject()
|
@Inject()
|
||||||
private tenancy: TenancyService;
|
private tenancy: TenancyService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults balance sheet filter query.
|
* Defaults purchases by items filter query.
|
||||||
* @return {IBalanceSheetQuery}
|
* @return {IPurchasesByItemsReportQuery}
|
||||||
*/
|
*/
|
||||||
get defaultQuery(): IInventoryValuationReportQuery {
|
get defaultQuery(): IPurchasesByItemsReportQuery {
|
||||||
return {
|
return {
|
||||||
fromDate: moment().startOf('month').format('YYYY-MM-DD'),
|
fromDate: moment().startOf('month').format('YYYY-MM-DD'),
|
||||||
toDate: moment().format('YYYY-MM-DD'),
|
toDate: moment().format('YYYY-MM-DD'),
|
||||||
@@ -40,7 +40,7 @@ export default class InventoryValuationReportService {
|
|||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
* @returns {IBalanceSheetMeta}
|
* @returns {IBalanceSheetMeta}
|
||||||
*/
|
*/
|
||||||
reportMetadata(tenantId: number): IInventoryValuationSheetMeta {
|
reportMetadata(tenantId: number): IPurchasesByItemsSheetMeta {
|
||||||
const settings = this.tenancy.settings(tenantId);
|
const settings = this.tenancy.settings(tenantId);
|
||||||
|
|
||||||
const organizationName = settings.get({
|
const organizationName = settings.get({
|
||||||
@@ -62,18 +62,13 @@ export default class InventoryValuationReportService {
|
|||||||
* Retrieve balance sheet statement.
|
* Retrieve balance sheet statement.
|
||||||
* -------------
|
* -------------
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {IBalanceSheetQuery} query
|
* @param {IPurchasesByItemsReportQuery} query
|
||||||
*
|
* @return {Promise<IPurchasesByItemsSheet>}
|
||||||
* @return {IBalanceSheetStatement}
|
|
||||||
*/
|
*/
|
||||||
public async purchasesByItems(
|
public async purchasesByItems(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
query: IInventoryValuationReportQuery
|
query: IPurchasesByItemsReportQuery
|
||||||
): Promise<{
|
): Promise<IPurchasesByItemsSheet> {
|
||||||
data: IInventoryValuationStatement;
|
|
||||||
query: IInventoryValuationReportQuery;
|
|
||||||
meta: IInventoryValuationSheetMeta;
|
|
||||||
}> {
|
|
||||||
const { Item, InventoryTransaction } = this.tenancy.models(tenantId);
|
const { Item, InventoryTransaction } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const tenant = await Tenant.query()
|
const tenant = await Tenant.query()
|
||||||
@@ -106,7 +101,6 @@ export default class InventoryValuationReportService {
|
|||||||
builder.modify('filterDateRange', filter.fromDate, filter.toDate);
|
builder.modify('filterDateRange', filter.fromDate, filter.toDate);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const purchasesByItemsInstance = new PurchasesByItems(
|
const purchasesByItemsInstance = new PurchasesByItems(
|
||||||
filter,
|
filter,
|
||||||
inventoryItems,
|
inventoryItems,
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import * as R from 'ramda';
|
||||||
|
import { ITableColumn, ITableColumnAccessor, ITableRow } from '@/interfaces';
|
||||||
|
import { ROW_TYPE } from './_types';
|
||||||
|
import { tableRowMapper } from '@/utils';
|
||||||
|
import { FinancialTable } from '../FinancialTable';
|
||||||
|
import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||||
|
import FinancialSheet from '../FinancialSheet';
|
||||||
|
import {
|
||||||
|
IPurchasesByItemsItem,
|
||||||
|
IPurchasesByItemsSheetData,
|
||||||
|
IPurchasesByItemsTotal,
|
||||||
|
} from '@/interfaces/PurchasesByItemsSheet';
|
||||||
|
|
||||||
|
export class PurchasesByItemsTable extends R.compose(
|
||||||
|
FinancialTable,
|
||||||
|
FinancialSheetStructure
|
||||||
|
)(FinancialSheet) {
|
||||||
|
private data: IPurchasesByItemsSheetData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
constructor(data) {
|
||||||
|
super();
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves thge common table accessors.
|
||||||
|
* @returns {ITableColumnAccessor[]}
|
||||||
|
*/
|
||||||
|
private commonTableAccessors(): ITableColumnAccessor[] {
|
||||||
|
return [
|
||||||
|
{ key: 'item_name', accessor: 'name' },
|
||||||
|
{ key: 'quantity_purchases', accessor: 'quantityPurchasedFormatted' },
|
||||||
|
{ key: 'purchase_amount', accessor: 'purchaseCostFormatted' },
|
||||||
|
{ key: 'average_cost', accessor: 'averageCostPriceFormatted' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the common table columns.
|
||||||
|
* @returns {ITableColumn[]}
|
||||||
|
*/
|
||||||
|
private commonTableColumns(): ITableColumn[] {
|
||||||
|
return [
|
||||||
|
{ label: 'Item name', key: 'item_name' },
|
||||||
|
{ label: 'Quantity Purchased', key: 'quantity_purchases' },
|
||||||
|
{ label: 'Purchase Amount', key: 'purchase_amount' },
|
||||||
|
{ label: 'Average Price', key: 'average_cost' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the given item node to table row.
|
||||||
|
* @param {IPurchasesByItemsItem} item
|
||||||
|
* @returns {ITableRow}
|
||||||
|
*/
|
||||||
|
private itemMap = (item: IPurchasesByItemsItem): ITableRow => {
|
||||||
|
const columns = this.commonTableAccessors();
|
||||||
|
const meta = {
|
||||||
|
rowTypes: [ROW_TYPE.ITEM],
|
||||||
|
};
|
||||||
|
return tableRowMapper(item, columns, meta);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the given items nodes to table rows.
|
||||||
|
* @param {IPurchasesByItemsItem[]} items - Items nodes.
|
||||||
|
* @returns {ITableRow[]}
|
||||||
|
*/
|
||||||
|
private itemsMap = (items: IPurchasesByItemsItem[]): ITableRow[] => {
|
||||||
|
return R.map(this.itemMap)(items);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the given total node to table rows.
|
||||||
|
* @param {IPurchasesByItemsTotal} total
|
||||||
|
* @returns {ITableRow}
|
||||||
|
*/
|
||||||
|
private totalNodeMap = (total: IPurchasesByItemsTotal): ITableRow => {
|
||||||
|
const columns = this.commonTableAccessors();
|
||||||
|
const meta = {
|
||||||
|
rowTypes: [ROW_TYPE.TOTAL],
|
||||||
|
};
|
||||||
|
return tableRowMapper(total, columns, meta);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the table columns.
|
||||||
|
* @returns {ITableColumn[]}
|
||||||
|
*/
|
||||||
|
public tableColumns(): ITableColumn[] {
|
||||||
|
const columns = this.commonTableColumns();
|
||||||
|
return R.compose(this.tableColumnsCellIndexing)(columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the table rows.
|
||||||
|
* @returns {ITableRow[]}
|
||||||
|
*/
|
||||||
|
public tableData(): ITableRow[] {
|
||||||
|
const itemsRows = this.itemsMap(this.data.items);
|
||||||
|
const totalRow = this.totalNodeMap(this.data.total);
|
||||||
|
|
||||||
|
return R.compose(
|
||||||
|
R.when(R.always(R.not(R.isEmpty(itemsRows))), R.append(totalRow))
|
||||||
|
)(itemsRows) as ITableRow[];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
IPurchasesByItemsReportQuery,
|
||||||
|
IPurchasesByItemsTable,
|
||||||
|
} from '@/interfaces/PurchasesByItemsSheet';
|
||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { PurchasesByItemsService } from './PurchasesByItemsService';
|
||||||
|
import { PurchasesByItemsTable } from './PurchasesByItemsTable';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class PurchasesByItemsTableInjectable {
|
||||||
|
@Inject()
|
||||||
|
private purchasesByItemsSheet: PurchasesByItemsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the purchases by items table format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IPurchasesByItemsReportQuery} filter
|
||||||
|
* @returns {Promise<IPurchasesByItemsTable>}
|
||||||
|
*/
|
||||||
|
public async table(
|
||||||
|
tenantId: number,
|
||||||
|
filter: IPurchasesByItemsReportQuery
|
||||||
|
): Promise<IPurchasesByItemsTable> {
|
||||||
|
const { data, query, meta } =
|
||||||
|
await this.purchasesByItemsSheet.purchasesByItems(tenantId, filter);
|
||||||
|
|
||||||
|
const table = new PurchasesByItemsTable(data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
table: {
|
||||||
|
columns: table.tableColumns(),
|
||||||
|
rows: table.tableData(),
|
||||||
|
},
|
||||||
|
meta,
|
||||||
|
query,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
export enum ROW_TYPE {
|
||||||
|
TOTAL = 'TOTAL',
|
||||||
|
ITEM = 'ITEM'
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ import withPurchasesByItems from './withPurchasesByItems';
|
|||||||
import withPurchasesByItemsActions from './withPurchasesByItemsActions';
|
import withPurchasesByItemsActions from './withPurchasesByItemsActions';
|
||||||
import { compose, saveInvoke } from '@/utils';
|
import { compose, saveInvoke } from '@/utils';
|
||||||
import { usePurchaseByItemsContext } from './PurchasesByItemsProvider';
|
import { usePurchaseByItemsContext } from './PurchasesByItemsProvider';
|
||||||
|
import { PurchasesByItemsExportMenu } from './components';
|
||||||
|
|
||||||
function PurchasesByItemsActionsBar({
|
function PurchasesByItemsActionsBar({
|
||||||
// #withPurchasesByItems
|
// #withPurchasesByItems
|
||||||
@@ -106,11 +107,18 @@ function PurchasesByItemsActionsBar({
|
|||||||
icon={<Icon icon="print-16" iconSize={16} />}
|
icon={<Icon icon="print-16" iconSize={16} />}
|
||||||
text={<T id={'print'} />}
|
text={<T id={'print'} />}
|
||||||
/>
|
/>
|
||||||
|
<Popover
|
||||||
|
content={<PurchasesByItemsExportMenu />}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
placement="bottom-start"
|
||||||
|
minimal
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||||
text={<T id={'export'} />}
|
text={<T id={'export'} />}
|
||||||
/>
|
/>
|
||||||
|
</Popover>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</DashboardActionsBar>
|
</DashboardActionsBar>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
import FinancialReportPage from '../FinancialReportPage';
|
import FinancialReportPage from '../FinancialReportPage';
|
||||||
import { usePurchasesByItems } from '@/hooks/query';
|
import { usePurchasesByItemsTable } from '@/hooks/query';
|
||||||
import { transformFilterFormToQuery } from '../common';
|
import { transformFilterFormToQuery } from '../common';
|
||||||
|
|
||||||
const PurchasesByItemsContext = createContext();
|
const PurchasesByItemsContext = createContext();
|
||||||
@@ -13,7 +13,7 @@ function PurchasesByItemsProvider({ query, ...props }) {
|
|||||||
isFetching,
|
isFetching,
|
||||||
isLoading,
|
isLoading,
|
||||||
refetch,
|
refetch,
|
||||||
} = usePurchasesByItems(
|
} = usePurchasesByItemsTable(
|
||||||
{
|
{
|
||||||
...transformFilterFormToQuery(query),
|
...transformFilterFormToQuery(query),
|
||||||
},
|
},
|
||||||
@@ -26,7 +26,6 @@ function PurchasesByItemsProvider({ query, ...props }) {
|
|||||||
purchaseByItems,
|
purchaseByItems,
|
||||||
isFetching,
|
isFetching,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
|
||||||
refetchSheet: refetch,
|
refetchSheet: refetch,
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import styled from 'styled-components';
|
|||||||
import { ReportDataTable, FinancialSheet } from '@/components';
|
import { ReportDataTable, FinancialSheet } from '@/components';
|
||||||
|
|
||||||
import { usePurchaseByItemsContext } from './PurchasesByItemsProvider';
|
import { usePurchaseByItemsContext } from './PurchasesByItemsProvider';
|
||||||
import { usePurchasesByItemsTableColumns } from './components';
|
|
||||||
|
|
||||||
import { tableRowTypesToClassnames } from '@/utils';
|
import { tableRowTypesToClassnames } from '@/utils';
|
||||||
import { TableStyle } from '@/constants';
|
import { TableStyle } from '@/constants';
|
||||||
|
import { usePurchasesByItemsTableColumns } from './dynamicColumns';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Purchases by items data table.
|
* Purchases by items data table.
|
||||||
@@ -17,7 +17,7 @@ import { TableStyle } from '@/constants';
|
|||||||
export default function PurchasesByItemsTable({ companyName }) {
|
export default function PurchasesByItemsTable({ companyName }) {
|
||||||
// Purchases by items context.
|
// Purchases by items context.
|
||||||
const {
|
const {
|
||||||
purchaseByItems: { tableRows, query },
|
purchaseByItems: { table, query },
|
||||||
} = usePurchaseByItemsContext();
|
} = usePurchaseByItemsContext();
|
||||||
|
|
||||||
// Purchases by items table columns.
|
// Purchases by items table columns.
|
||||||
@@ -32,7 +32,7 @@ export default function PurchasesByItemsTable({ companyName }) {
|
|||||||
>
|
>
|
||||||
<PurchasesByItemsDataTable
|
<PurchasesByItemsDataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={tableRows}
|
data={table.rows}
|
||||||
expandable={true}
|
expandable={true}
|
||||||
expandToggleColumn={1}
|
expandToggleColumn={1}
|
||||||
expandColumnSpace={1}
|
expandColumnSpace={1}
|
||||||
@@ -58,7 +58,7 @@ const PurchasesByItemsDataTable = styled(ReportDataTable)`
|
|||||||
padding-top: 0.36rem;
|
padding-top: 0.36rem;
|
||||||
padding-bottom: 0.36rem;
|
padding-bottom: 0.36rem;
|
||||||
}
|
}
|
||||||
.tr.row_type--total .td {
|
.tr.row_type--TOTAL .td {
|
||||||
border-top: 1px solid #bbb;
|
border-top: 1px solid #bbb;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
border-bottom: 3px double #000;
|
border-bottom: 3px double #000;
|
||||||
|
|||||||
@@ -1,69 +1,22 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import { useRef } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import classNames from 'classnames';
|
||||||
|
import {
|
||||||
|
Classes,
|
||||||
|
Intent,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
ProgressBar,
|
||||||
|
Text,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
|
||||||
import { If } from '@/components';
|
import { AppToaster, If, Stack } from '@/components';
|
||||||
import { Align } from '@/constants';
|
|
||||||
import { CellTextSpan } from '@/components/Datatable/Cells';
|
|
||||||
import { usePurchaseByItemsContext } from './PurchasesByItemsProvider';
|
import { usePurchaseByItemsContext } from './PurchasesByItemsProvider';
|
||||||
import { getColumnWidth } from '@/utils';
|
|
||||||
import FinancialLoadingBar from '../FinancialLoadingBar';
|
import FinancialLoadingBar from '../FinancialLoadingBar';
|
||||||
|
import {
|
||||||
/**
|
usePurchasesByItemsCsvExport,
|
||||||
* Retrieve purchases by items table columns.
|
usePurchasesByItemsXlsxExport,
|
||||||
*/
|
} from '@/hooks/query';
|
||||||
export const usePurchasesByItemsTableColumns = () => {
|
|
||||||
// purchases by items context.
|
|
||||||
const {
|
|
||||||
purchaseByItems: { tableRows },
|
|
||||||
} = usePurchaseByItemsContext();
|
|
||||||
|
|
||||||
return React.useMemo(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
Header: intl.get('item_name'),
|
|
||||||
accessor: (row) => (row.code ? `${row.name} - ${row.code}` : row.name),
|
|
||||||
className: 'name',
|
|
||||||
width: 180,
|
|
||||||
textOverview: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: intl.get('quantity_purchased'),
|
|
||||||
accessor: 'quantity_purchased_formatted',
|
|
||||||
Cell: CellTextSpan,
|
|
||||||
className: 'quantity_purchased_formatted',
|
|
||||||
width: getColumnWidth(tableRows, `quantity_purchased_formatted`, {
|
|
||||||
minWidth: 150,
|
|
||||||
}),
|
|
||||||
textOverview: true,
|
|
||||||
align: Align.Right,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: intl.get('purchase_amount'),
|
|
||||||
accessor: 'purchase_cost_formatted',
|
|
||||||
Cell: CellTextSpan,
|
|
||||||
className: 'purchase_cost_formatted',
|
|
||||||
width: getColumnWidth(tableRows, `purchase_cost_formatted`, {
|
|
||||||
minWidth: 150,
|
|
||||||
}),
|
|
||||||
textOverview: true,
|
|
||||||
align: Align.Right,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: intl.get('average_price'),
|
|
||||||
accessor: 'average_cost_price_formatted',
|
|
||||||
Cell: CellTextSpan,
|
|
||||||
className: 'average_cost_price_formatted',
|
|
||||||
width: getColumnWidth(tableRows, `average_cost_price_formatted`, {
|
|
||||||
minWidth: 180,
|
|
||||||
}),
|
|
||||||
textOverview: true,
|
|
||||||
align: Align.Right,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[tableRows],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Purchases by items progress loading bar.
|
* Purchases by items progress loading bar.
|
||||||
@@ -77,3 +30,88 @@ export function PurchasesByItemsLoadingBar() {
|
|||||||
</If>
|
</If>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the purchases by items export menu.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export const PurchasesByItemsExportMenu = () => {
|
||||||
|
const toastKey = useRef(null);
|
||||||
|
const commonToastConfig = {
|
||||||
|
isCloseButtonShown: true,
|
||||||
|
timeout: 2000,
|
||||||
|
};
|
||||||
|
const { query } = usePurchaseByItemsContext();
|
||||||
|
|
||||||
|
const openProgressToast = (amount: number) => {
|
||||||
|
return (
|
||||||
|
<Stack spacing={8}>
|
||||||
|
<Text>The report has been exported successfully.</Text>
|
||||||
|
<ProgressBar
|
||||||
|
className={classNames('toast-progress', {
|
||||||
|
[Classes.PROGRESS_NO_STRIPES]: amount >= 100,
|
||||||
|
})}
|
||||||
|
intent={amount < 100 ? Intent.PRIMARY : Intent.SUCCESS}
|
||||||
|
value={amount / 100}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Export the report to xlsx.
|
||||||
|
const { mutateAsync: xlsxExport } = usePurchasesByItemsXlsxExport(query, {
|
||||||
|
onDownloadProgress: (xlsxExportProgress: number) => {
|
||||||
|
if (!toastKey.current) {
|
||||||
|
toastKey.current = AppToaster.show({
|
||||||
|
message: openProgressToast(xlsxExportProgress),
|
||||||
|
...commonToastConfig,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
AppToaster.show(
|
||||||
|
{
|
||||||
|
message: openProgressToast(xlsxExportProgress),
|
||||||
|
...commonToastConfig,
|
||||||
|
},
|
||||||
|
toastKey.current,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Export the report to csv.
|
||||||
|
const { mutateAsync: csvExport } = usePurchasesByItemsCsvExport(query, {
|
||||||
|
onDownloadProgress: (xlsxExportProgress: number) => {
|
||||||
|
if (!toastKey.current) {
|
||||||
|
toastKey.current = AppToaster.show({
|
||||||
|
message: openProgressToast(xlsxExportProgress),
|
||||||
|
...commonToastConfig,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
AppToaster.show(
|
||||||
|
{
|
||||||
|
message: openProgressToast(xlsxExportProgress),
|
||||||
|
...commonToastConfig,
|
||||||
|
},
|
||||||
|
toastKey.current,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Handle csv export button click.
|
||||||
|
const handleCsvExportBtnClick = () => {
|
||||||
|
csvExport();
|
||||||
|
};
|
||||||
|
// Handle xlsx export button click.
|
||||||
|
const handleXlsxExportBtnClick = () => {
|
||||||
|
xlsxExport();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu>
|
||||||
|
<MenuItem
|
||||||
|
text={'XLSX (Microsoft Excel)'}
|
||||||
|
onClick={handleXlsxExportBtnClick}
|
||||||
|
/>
|
||||||
|
<MenuItem text={'CSV'} onClick={handleCsvExportBtnClick} />
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { getColumnWidth } from '@/utils';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import { Align } from '@/constants';
|
||||||
|
import { usePurchaseByItemsContext } from './PurchasesByItemsProvider';
|
||||||
|
|
||||||
|
const getTableCellValueAccessor = (index) => `cells[${index}].value`;
|
||||||
|
|
||||||
|
const getReportColWidth = (data, accessor, headerText) => {
|
||||||
|
return getColumnWidth(
|
||||||
|
data,
|
||||||
|
accessor,
|
||||||
|
{ magicSpacing: 10, minWidth: 100 },
|
||||||
|
headerText,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account name column mapper.
|
||||||
|
*/
|
||||||
|
const commonColumnMapper = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: column.key,
|
||||||
|
Header: column.label,
|
||||||
|
accessor,
|
||||||
|
className: column.key,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Numeric columns accessor.
|
||||||
|
*/
|
||||||
|
const numericColumnAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...column,
|
||||||
|
align: Align.Right,
|
||||||
|
width,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item name column accessor.
|
||||||
|
*/
|
||||||
|
const itemNameColumnAccessor = R.curry((data, column) => {
|
||||||
|
return {
|
||||||
|
...column,
|
||||||
|
width: 180,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const dynamiColumnMapper = R.curry((data, column) => {
|
||||||
|
const _numericColumnAccessor = numericColumnAccessor(data);
|
||||||
|
const _itemNameColumnAccessor = itemNameColumnAccessor(data);
|
||||||
|
|
||||||
|
return R.compose(
|
||||||
|
R.when(R.pathEq(['key'], 'item_name'), _itemNameColumnAccessor),
|
||||||
|
R.when(R.pathEq(['key'], 'quantity_purchases'), _numericColumnAccessor),
|
||||||
|
R.when(R.pathEq(['key'], 'purchase_amount'), _numericColumnAccessor),
|
||||||
|
R.when(R.pathEq(['key'], 'average_cost'), _numericColumnAccessor),
|
||||||
|
commonColumnMapper(data),
|
||||||
|
)(column);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composes the dynamic columns that fetched from request to columns to table component.
|
||||||
|
*/
|
||||||
|
export const dynamicColumns = R.curry((data, columns) => {
|
||||||
|
return R.map(dynamiColumnMapper(data), columns);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the purchases by items sheet table columns for table component.
|
||||||
|
*/
|
||||||
|
export const usePurchasesByItemsTableColumns = () => {
|
||||||
|
const { purchaseByItems } = usePurchaseByItemsContext();
|
||||||
|
|
||||||
|
if (!purchaseByItems) {
|
||||||
|
throw new Error('Purchases by items context not found');
|
||||||
|
}
|
||||||
|
const { table } = purchaseByItems;
|
||||||
|
|
||||||
|
return dynamicColumns(table.rows, table.columns);
|
||||||
|
};
|
||||||
@@ -426,20 +426,58 @@ export function usePurchasesByItems(query, props) {
|
|||||||
params: query,
|
params: query,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
select: (res) => ({
|
select: (res) => res.data,
|
||||||
tableRows: purchasesByItemsReducer(res.data.data),
|
|
||||||
...res.data,
|
|
||||||
}),
|
|
||||||
defaultData: {
|
|
||||||
tableRows: [],
|
|
||||||
data: [],
|
|
||||||
query: {},
|
|
||||||
},
|
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function usePurchasesByItemsTable(query, props) {
|
||||||
|
return useRequestQuery(
|
||||||
|
[t.FINANCIAL_REPORT, t.PURCHASES_BY_ITEMS, query],
|
||||||
|
{
|
||||||
|
method: 'get',
|
||||||
|
url: '/financial_statements/purchases-by-items',
|
||||||
|
params: query,
|
||||||
|
headers: {
|
||||||
|
accept: 'application/json+table',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: (res) => res.data,
|
||||||
|
...props,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const usePurchasesByItemsCsvExport = (query, args) => {
|
||||||
|
return useDownloadFile({
|
||||||
|
url: '/financial_statements/purchases-by-items',
|
||||||
|
config: {
|
||||||
|
headers: {
|
||||||
|
accept: 'application/csv',
|
||||||
|
},
|
||||||
|
params: query,
|
||||||
|
},
|
||||||
|
filename: 'purchases_by_items.csv',
|
||||||
|
...args,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const usePurchasesByItemsXlsxExport = (query, args) => {
|
||||||
|
return useDownloadFile({
|
||||||
|
url: '/financial_statements/purchases-by-items',
|
||||||
|
config: {
|
||||||
|
headers: {
|
||||||
|
accept: 'application/xlsx',
|
||||||
|
},
|
||||||
|
params: query,
|
||||||
|
},
|
||||||
|
filename: 'purchases_by_items.xlsx',
|
||||||
|
...args,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sales by items.
|
* Retrieve sales by items.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user