Skip to content

Commit

Permalink
feat: export purchases by items to csv/xlsx (#327)
Browse files Browse the repository at this point in the history
  • Loading branch information
abouolia authored Jan 23, 2024
1 parent 7eb8447 commit 429159a
Show file tree
Hide file tree
Showing 17 changed files with 672 additions and 150 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { Router, Request, Response, NextFunction } from 'express';
import { query, ValidationChain } from 'express-validator';
import moment from 'moment';
import { Inject, Service } from 'typedi';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import BaseFinancialReportController from './BaseFinancialReportController';
import PurchasesByItemsService from '@/services/FinancialStatements/PurchasesByItems/PurchasesByItemsService';
import { PurchasesByItemsService } from '@/services/FinancialStatements/PurchasesByItems/PurchasesByItemsService';
import { AbilitySubject, ReportsAction } from '@/interfaces';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { ACCEPT_TYPE } from '@/interfaces/Http';
import { PurcahsesByItemsApplication } from '@/services/FinancialStatements/PurchasesByItems/PurchasesByItemsApplication';

@Service()
export default class PurchasesByItemReportController extends BaseFinancialReportController {
@Inject()
purchasesByItemsService: PurchasesByItemsService;
private purchasesByItemsApp: PurcahsesByItemsApplication;

/**
* Router constructor.
Expand Down Expand Up @@ -63,20 +64,47 @@ export default class PurchasesByItemReportController extends BaseFinancialReport
* @param {Request} req -
* @param {Response} res -
*/
async purchasesByItems(req: Request, res: Response, next: NextFunction) {
public async purchasesByItems(req: Request, res: Response) {
const { tenantId } = req;
const filter = this.matchedQueryData(req);

try {
const { data, query, meta } =
await this.purchasesByItemsService.purchasesByItems(tenantId, filter);
return res.status(200).send({
meta: this.transfromToResponse(meta),
data: this.transfromToResponse(data),
query: this.transfromToResponse(query),
});
} catch (error) {
next(error);
const accept = this.accepts(req);

const acceptType = accept.types([
ACCEPT_TYPE.APPLICATION_JSON,
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
ACCEPT_TYPE.APPLICATION_XLSX,
ACCEPT_TYPE.APPLICATION_CSV,
]);

// 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);
}
}
}
3 changes: 0 additions & 3 deletions packages/server/src/interfaces/APAgingSummaryReport.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import {
IAgingPeriod,
IAgingPeriodTotal,
IAgingAmount,
IAgingSummaryQuery,
IAgingSummaryTotal,
IAgingSummaryContact,
IAgingSummaryData,
} from './AgingReport';
import { INumberFormatQuery } from './FinancialStatements';
import { IFinancialTable } from './Table';

export interface IAPAgingSummaryQuery extends IAgingSummaryQuery {
Expand Down
54 changes: 54 additions & 0 deletions packages/server/src/interfaces/PurchasesByItemsSheet.ts
Original file line number Diff line number Diff line change
@@ -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;
}
20 changes: 13 additions & 7 deletions packages/server/src/services/Accounts/AccountsApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import {
IAccount,
IAccountCreateDTO,
IAccountEditDTO,
IAccountResponse,
IAccountsFilter,
IAccountsTransactionsFilter,
IFilterMeta,
IGetAccountTransactionPOJO,
} from '@/interfaces';
import { CreateAccount } from './CreateAccount';
Expand All @@ -14,6 +16,7 @@ import { ActivateAccount } from './ActivateAccount';
import { GetAccounts } from './GetAccounts';
import { GetAccount } from './GetAccount';
import { GetAccountTransactions } from './GetAccountTransactions';

@Service()
export class AccountsApplication {
@Inject()
Expand Down Expand Up @@ -113,19 +116,22 @@ export class AccountsApplication {

/**
* Retrieves the accounts list.
* @param {number} tenantId
* @param {IAccountsFilter} filterDTO
* @returns
* @param {number} tenantId
* @param {IAccountsFilter} filterDTO
* @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);
};

/**
* Retrieves the given account transactions.
* @param {number} tenantId
* @param {IAccountsTransactionsFilter} filter
* @returns {Promise<IGetAccountTransactionPOJO[]>}
* @param {number} tenantId
* @param {IAccountsTransactionsFilter} filter
* @returns {Promise<IGetAccountTransactionPOJO[]>}
*/
public getAccountsTransactions = (
tenantId: number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,34 @@ import { get, isEmpty, sumBy } from 'lodash';
import * as R from 'ramda';
import FinancialSheet from '../FinancialSheet';
import { allPassedConditionsPass, transformToMap } from 'utils';
import { IAccountTransaction, IItem } from '@/interfaces';
import {
IAccountTransaction,
IInventoryValuationTotal,
IInventoryValuationItem,
IInventoryValuationReportQuery,
IInventoryValuationStatement,
IItem,
} from '@/interfaces';

export default class InventoryValuationReport extends FinancialSheet {
IPurchasesByItemsItem,
IPurchasesByItemsReportQuery,
IPurchasesByItemsSheetData,
IPurchasesByItemsTotal,
} from '@/interfaces/PurchasesByItemsSheet';

export class PurchasesByItems extends FinancialSheet {
readonly baseCurrency: string;
readonly items: IItem[];
readonly itemsTransactions: Map<number, IAccountTransaction>;
readonly query: IInventoryValuationReportQuery;
readonly query: IPurchasesByItemsReportQuery;

/**
* Constructor method.
* @param {IInventoryValuationReportQuery} query
* @param {IPurchasesByItemsReportQuery} query
* @param {IItem[]} items
* @param {IAccountTransaction[]} itemsTransactions
* @param {string} baseCurrency
*/
constructor(
query: IInventoryValuationReportQuery,
query: IPurchasesByItemsReportQuery,
items: IItem[],
itemsTransactions: IAccountTransaction[],
baseCurrency: string
) {
super();

this.baseCurrency = baseCurrency;
this.items = items;
this.itemsTransactions = transformToMap(itemsTransactions, 'itemId');
Expand Down Expand Up @@ -98,7 +96,7 @@ export default class InventoryValuationReport extends FinancialSheet {
* @param {IInventoryValuationItem} item
* @returns
*/
private itemSectionMapper = (item: IItem): IInventoryValuationItem => {
private itemSectionMapper = (item: IItem): IPurchasesByItemsItem => {
const meta = this.getItemTransaction(item.id);

return {
Expand Down Expand Up @@ -145,9 +143,9 @@ export default class InventoryValuationReport extends FinancialSheet {

/**
* Retrieve the items sections.
* @returns {IInventoryValuationItem[]}
* @returns {IPurchasesByItemsItem[]}
*/
private itemsSection = (): IInventoryValuationItem[] => {
private itemsSection = (): IPurchasesByItemsItem[] => {
return R.compose(
R.when(this.isItemsPostFilter, this.itemsFilter),
this.itemsMapper
Expand All @@ -156,10 +154,10 @@ export default class InventoryValuationReport extends FinancialSheet {

/**
* Retrieve the total section of the sheet.
* @param {IInventoryValuationItem[]} items
* @returns {IInventoryValuationTotal}
* @param {IPurchasesByItemsItem[]} items
* @returns {IPurchasesByItemsTotal}
*/
totalSection(items: IInventoryValuationItem[]): IInventoryValuationTotal {
private totalSection(items: IPurchasesByItemsItem[]): IPurchasesByItemsTotal {
const quantityPurchased = sumBy(items, (item) => item.quantityPurchased);
const purchaseCost = sumBy(items, (item) => item.purchaseCost);

Expand All @@ -176,12 +174,12 @@ export default class InventoryValuationReport extends FinancialSheet {

/**
* Retrieve the sheet data.
* @returns
* @returns {IInventoryValuationStatement}
*/
reportData(): IInventoryValuationStatement {
public reportData(): IPurchasesByItemsSheetData {
const items = this.itemsSection();
const total = this.totalSection(items);

return items.length > 0 ? { items, total } : {};
return { items, total };
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit 429159a

Please sign in to comment.