mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 22:00:31 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abffdd1029 | ||
|
|
d052c23560 | ||
|
|
f02afd3c9f | ||
|
|
3df17390e2 | ||
|
|
df38f8893d | ||
|
|
8882bc677e | ||
|
|
f03d01113c | ||
|
|
8f431597d9 | ||
|
|
03bc78a068 | ||
|
|
ecf5d60db0 | ||
|
|
3a5fd2782a | ||
|
|
1d8416ebfe | ||
|
|
3672abe7a5 | ||
|
|
2753908b83 | ||
|
|
8495990ec2 | ||
|
|
471ce1b7af | ||
|
|
4a920176f4 | ||
|
|
74fd76ce77 | ||
|
|
c9f57d9a75 |
@@ -87,6 +87,15 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "xprnio",
|
||||||
|
"name": "Ragnar Laud",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/3042904?v=4",
|
||||||
|
"profile": "https://ragnarlaud.dev",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
|||||||
@@ -56,5 +56,5 @@ GOTENBERG_URL=http://gotenberg:3000
|
|||||||
GOTENBERG_DOCS_URL=http://server:3000/public/
|
GOTENBERG_DOCS_URL=http://server:3000/public/
|
||||||
|
|
||||||
# Gotenberg API - (development)
|
# Gotenberg API - (development)
|
||||||
# GOTENBERG_URL=http://gotenberg:3000
|
# GOTENBERG_URL=http://localhost:9000
|
||||||
# GOTENBERG_DOCS_URL=http://server:3000/public/
|
# GOTENBERG_DOCS_URL=http://host.docker.internal:3000/public/
|
||||||
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
All notable changes to Bigcapital server-side will be in this file.
|
All notable changes to Bigcapital server-side will be in this file.
|
||||||
|
|
||||||
|
## [0.13.1] - 15-01-2024
|
||||||
|
|
||||||
|
* feat(webapp): add approve/reject to action bar of estimate details dr… by @ANasouf in https://github.com/bigcapitalhq/bigcapital/pull/304
|
||||||
|
* docs: add ANasouf as a contributor for code by @allcontributors in https://github.com/bigcapitalhq/bigcapital/pull/305
|
||||||
|
* feat: Export general ledger & Journal to CSV and XLSX by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/303
|
||||||
|
* feat: Auto re-calculate the items rate once changing the invoice exchange rate. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/270
|
||||||
|
|
||||||
## [0.13.0] - 31-12-2023
|
## [0.13.0] - 31-12-2023
|
||||||
|
|
||||||
* feat: Send an invoice mail the customer email by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/292
|
* feat: Send an invoice mail the customer email by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/292
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<tr>
|
<tr>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="http://cschuijt.nl"><img src="https://avatars.githubusercontent.com/u/5460015?v=4?s=100" width="100px;" alt="Casper Schuijt"/><br /><sub><b>Casper Schuijt</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Acschuijt" title="Bug reports">🐛</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="http://cschuijt.nl"><img src="https://avatars.githubusercontent.com/u/5460015?v=4?s=100" width="100px;" alt="Casper Schuijt"/><br /><sub><b>Casper Schuijt</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Acschuijt" title="Bug reports">🐛</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ANasouf"><img src="https://avatars.githubusercontent.com/u/19536487?v=4?s=100" width="100px;" alt="ANasouf"/><br /><sub><b>ANasouf</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=ANasouf" title="Code">💻</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ANasouf"><img src="https://avatars.githubusercontent.com/u/19536487?v=4?s=100" width="100px;" alt="ANasouf"/><br /><sub><b>ANasouf</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=ANasouf" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://ragnarlaud.dev"><img src="https://avatars.githubusercontent.com/u/3042904?v=4?s=100" width="100px;" alt="Ragnar Laud"/><br /><sub><b>Ragnar Laud</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Axprnio" title="Bug reports">🐛</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ import { query, ValidationChain } from 'express-validator';
|
|||||||
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 InventoryValuationService from '@/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetService';
|
|
||||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||||
|
import { InventoryValuationSheetApplication } from '@/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetApplication';
|
||||||
|
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class InventoryValuationReportController extends BaseFinancialReportController {
|
export default class InventoryValuationReportController extends BaseFinancialReportController {
|
||||||
@Inject()
|
@Inject()
|
||||||
inventoryValuationService: InventoryValuationService;
|
private inventoryValuationApp: InventoryValuationSheetApplication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router constructor.
|
* Router constructor.
|
||||||
@@ -71,19 +72,45 @@ export default class InventoryValuationReportController extends BaseFinancialRep
|
|||||||
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.inventoryValuationService.inventoryValuationSheet(
|
const acceptType = accept.types([
|
||||||
tenantId,
|
ACCEPT_TYPE.APPLICATION_JSON,
|
||||||
filter
|
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||||
);
|
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||||
return res.status(200).send({
|
ACCEPT_TYPE.APPLICATION_CSV,
|
||||||
meta: this.transfromToResponse(meta),
|
]);
|
||||||
data: this.transfromToResponse(data),
|
|
||||||
query: this.transfromToResponse(query),
|
// Retrieves the json table format.
|
||||||
});
|
if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) {
|
||||||
} catch (error) {
|
const table = await this.inventoryValuationApp.table(tenantId, filter);
|
||||||
next(error);
|
|
||||||
|
return res.status(200).send(table);
|
||||||
|
// Retrieves the csv format.
|
||||||
|
} else if (ACCEPT_TYPE.APPLICATION_CSV == acceptType) {
|
||||||
|
const buffer = await this.inventoryValuationApp.csv(tenantId, filter);
|
||||||
|
|
||||||
|
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
||||||
|
res.setHeader('Content-Type', 'text/csv');
|
||||||
|
|
||||||
|
return res.send(buffer);
|
||||||
|
// Retrieves the xslx buffer format.
|
||||||
|
} else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) {
|
||||||
|
const buffer = await this.inventoryValuationApp.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);
|
||||||
|
// Retrieves the json format.
|
||||||
|
} else {
|
||||||
|
const { data, query, meta } = await this.inventoryValuationApp.sheet(
|
||||||
|
tenantId,
|
||||||
|
filter
|
||||||
|
);
|
||||||
|
return res.status(200).send({ meta, data, query });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { Router, Request, Response, NextFunction } from 'express';
|
import { Router, Request, Response, NextFunction } from 'express';
|
||||||
import { query, ValidationChain } from 'express-validator';
|
import { query, ValidationChain, ValidationSchema } 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 SalesByItemsReportService from '@/services/FinancialStatements/SalesByItems/SalesByItemsService';
|
|
||||||
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 { SalesByItemsApplication } from '@/services/FinancialStatements/SalesByItems/SalesByItemsApplication';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class SalesByItemsReportController extends BaseFinancialReportController {
|
export default class SalesByItemsReportController extends BaseFinancialReportController {
|
||||||
@Inject()
|
@Inject()
|
||||||
salesByItemsService: SalesByItemsReportService;
|
salesByItemsApp: SalesByItemsApplication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router constructor.
|
* Router constructor.
|
||||||
@@ -24,13 +24,14 @@ export default class SalesByItemsReportController extends BaseFinancialReportCon
|
|||||||
CheckPolicies(ReportsAction.READ_SALES_BY_ITEMS, AbilitySubject.Report),
|
CheckPolicies(ReportsAction.READ_SALES_BY_ITEMS, AbilitySubject.Report),
|
||||||
this.validationSchema,
|
this.validationSchema,
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.purchasesByItems.bind(this))
|
asyncMiddleware(this.salesByItems.bind(this))
|
||||||
);
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validation schema.
|
* Validation schema.
|
||||||
|
* @returns {ValidationChain[]}
|
||||||
*/
|
*/
|
||||||
private get validationSchema(): ValidationChain[] {
|
private get validationSchema(): ValidationChain[] {
|
||||||
return [
|
return [
|
||||||
@@ -60,26 +61,44 @@ export default class SalesByItemsReportController extends BaseFinancialReportCon
|
|||||||
* @param {Request} req -
|
* @param {Request} req -
|
||||||
* @param {Response} res -
|
* @param {Response} res -
|
||||||
*/
|
*/
|
||||||
private async purchasesByItems(
|
private async salesByItems(req: Request, res: Response, next: NextFunction) {
|
||||||
req: Request,
|
|
||||||
res: Response,
|
|
||||||
next: NextFunction
|
|
||||||
) {
|
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const filter = this.matchedQueryData(req);
|
const filter = this.matchedQueryData(req);
|
||||||
|
const accept = this.accepts(req);
|
||||||
|
|
||||||
try {
|
const acceptType = accept.types([
|
||||||
const { data, query, meta } = await this.salesByItemsService.salesByItems(
|
ACCEPT_TYPE.APPLICATION_JSON,
|
||||||
tenantId,
|
ACCEPT_TYPE.APPLICATION_JSON_TABLE,
|
||||||
filter
|
ACCEPT_TYPE.APPLICATION_CSV,
|
||||||
|
ACCEPT_TYPE.APPLICATION_XLSX,
|
||||||
|
]);
|
||||||
|
// Retrieves the csv format.
|
||||||
|
if (ACCEPT_TYPE.APPLICATION_CSV === acceptType) {
|
||||||
|
const buffer = await this.salesByItemsApp.csv(tenantId, filter);
|
||||||
|
|
||||||
|
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
||||||
|
res.setHeader('Content-Type', 'text/csv');
|
||||||
|
|
||||||
|
return res.send(buffer);
|
||||||
|
// Retrieves the json table format.
|
||||||
|
} else if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) {
|
||||||
|
const table = await this.salesByItemsApp.table(tenantId, filter);
|
||||||
|
|
||||||
|
return res.status(200).send(table);
|
||||||
|
// Retrieves the xlsx format.
|
||||||
|
} else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) {
|
||||||
|
const buffer = this.salesByItemsApp.xlsx(tenantId, filter);
|
||||||
|
|
||||||
|
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
|
||||||
|
res.setHeader(
|
||||||
|
'Content-Type',
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
);
|
);
|
||||||
return res.status(200).send({
|
return res.send(buffer);
|
||||||
meta: this.transfromToResponse(meta),
|
// Retrieves the json format.
|
||||||
data: this.transfromToResponse(data),
|
} else {
|
||||||
query: this.transfromToResponse(query),
|
const sheet = await this.salesByItemsApp.sheet(tenantId, filter);
|
||||||
});
|
return res.status(200).send(sheet);
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -418,44 +418,34 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
* @param {Request} req - Request object.
|
* @param {Request} req - Request object.
|
||||||
* @param {Response} res - Response object.
|
* @param {Response} res - Response object.
|
||||||
*/
|
*/
|
||||||
private async getSaleInvoice(
|
private async getSaleInvoice(req: Request, res: Response) {
|
||||||
req: Request,
|
|
||||||
res: Response,
|
|
||||||
next: NextFunction
|
|
||||||
) {
|
|
||||||
const { id: saleInvoiceId } = req.params;
|
const { id: saleInvoiceId } = req.params;
|
||||||
const { tenantId, user } = req;
|
const { tenantId, user } = req;
|
||||||
|
|
||||||
try {
|
// Response formatter.
|
||||||
const saleInvoice = await this.saleInvoiceApplication.getSaleInvoice(
|
return res.format({
|
||||||
tenantId,
|
// JSON content type.
|
||||||
saleInvoiceId,
|
[ACCEPT_TYPE.APPLICATION_JSON]: async () => {
|
||||||
user
|
const saleInvoice = await this.saleInvoiceApplication.getSaleInvoice(
|
||||||
);
|
tenantId,
|
||||||
// Response formatter.
|
saleInvoiceId,
|
||||||
res.format({
|
user
|
||||||
// JSON content type.
|
);
|
||||||
[ACCEPT_TYPE.APPLICATION_JSON]: () => {
|
return res.status(200).send(this.transfromToResponse({ saleInvoice }));
|
||||||
return res
|
},
|
||||||
.status(200)
|
// PDF content type.
|
||||||
.send(this.transfromToResponse({ saleInvoice }));
|
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
|
||||||
},
|
const pdfContent = await this.saleInvoiceApplication.saleInvoicePdf(
|
||||||
// PDF content type.
|
tenantId,
|
||||||
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
|
saleInvoiceId
|
||||||
const pdfContent = await this.saleInvoiceApplication.saleInvoicePdf(
|
);
|
||||||
tenantId,
|
res.set({
|
||||||
saleInvoice
|
'Content-Type': 'application/pdf',
|
||||||
);
|
'Content-Length': pdfContent.length,
|
||||||
res.set({
|
});
|
||||||
'Content-Type': 'application/pdf',
|
res.send(pdfContent);
|
||||||
'Content-Length': pdfContent.length,
|
},
|
||||||
});
|
});
|
||||||
res.send(pdfContent);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Retrieve paginated sales invoices with custom view metadata.
|
* Retrieve paginated sales invoices with custom view metadata.
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { INumberFormatQuery } from './FinancialStatements';
|
import { INumberFormatQuery } from './FinancialStatements';
|
||||||
|
import { IFinancialTable } from './Table';
|
||||||
|
|
||||||
export interface IInventoryValuationReportQuery {
|
export interface IInventoryValuationReportQuery {
|
||||||
asDate: Date | string;
|
asDate: Date | string;
|
||||||
@@ -39,9 +40,19 @@ export interface IInventoryValuationTotal {
|
|||||||
quantityFormatted: string;
|
quantityFormatted: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IInventoryValuationStatement =
|
export type IInventoryValuationStatement = {
|
||||||
| {
|
items: IInventoryValuationItem[];
|
||||||
items: IInventoryValuationItem[];
|
total: IInventoryValuationTotal;
|
||||||
total: IInventoryValuationTotal;
|
};
|
||||||
}
|
export type IInventoryValuationSheetData = IInventoryValuationStatement;
|
||||||
| {};
|
|
||||||
|
export interface IInventoryValuationSheet {
|
||||||
|
data: IInventoryValuationStatement;
|
||||||
|
meta: IInventoryValuationSheetMeta;
|
||||||
|
query: IInventoryValuationReportQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IInventoryValuationTable extends IFinancialTable {
|
||||||
|
meta: IInventoryValuationSheetMeta;
|
||||||
|
query: IInventoryValuationReportQuery;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,45 +1,54 @@
|
|||||||
import {
|
import { INumberFormatQuery } from './FinancialStatements';
|
||||||
INumberFormatQuery,
|
import { IFinancialTable } from './Table';
|
||||||
} from './FinancialStatements';
|
|
||||||
|
|
||||||
export interface ISalesByItemsReportQuery {
|
export interface ISalesByItemsReportQuery {
|
||||||
fromDate: Date | string;
|
fromDate: Date | string;
|
||||||
toDate: Date | string;
|
toDate: Date | string;
|
||||||
itemsIds: number[],
|
itemsIds: number[];
|
||||||
numberFormat: INumberFormatQuery;
|
numberFormat: INumberFormatQuery;
|
||||||
noneTransactions: boolean;
|
noneTransactions: boolean;
|
||||||
onlyActive: boolean;
|
onlyActive: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface ISalesByItemsSheetMeta {
|
export interface ISalesByItemsSheetMeta {
|
||||||
organizationName: string,
|
organizationName: string;
|
||||||
baseCurrency: string,
|
baseCurrency: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface ISalesByItemsItem {
|
export interface ISalesByItemsItem {
|
||||||
id: number,
|
id: number;
|
||||||
name: string,
|
name: string;
|
||||||
code: string,
|
code: string;
|
||||||
quantitySold: number,
|
quantitySold: number;
|
||||||
soldCost: number,
|
soldCost: number;
|
||||||
averageSellPrice: number,
|
averageSellPrice: number;
|
||||||
|
|
||||||
quantitySoldFormatted: string,
|
quantitySoldFormatted: string;
|
||||||
soldCostFormatted: string,
|
soldCostFormatted: string;
|
||||||
averageSellPriceFormatted: string,
|
averageSellPriceFormatted: string;
|
||||||
currencyCode: string,
|
currencyCode: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface ISalesByItemsTotal {
|
export interface ISalesByItemsTotal {
|
||||||
quantitySold: number,
|
quantitySold: number;
|
||||||
soldCost: number,
|
soldCost: number;
|
||||||
quantitySoldFormatted: string,
|
quantitySoldFormatted: string;
|
||||||
soldCostFormatted: string,
|
soldCostFormatted: string;
|
||||||
currencyCode: string,
|
currencyCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ISalesByItemsSheetData = {
|
||||||
|
items: ISalesByItemsItem[];
|
||||||
|
total: ISalesByItemsTotal;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ISalesByItemsSheetStatement = {
|
export interface ISalesByItemsSheet {
|
||||||
items: ISalesByItemsItem[],
|
data: ISalesByItemsSheetData;
|
||||||
total: ISalesByItemsTotal
|
query: ISalesByItemsReportQuery;
|
||||||
} | {};
|
meta: ISalesByItemsSheetMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISalesByItemsTable extends IFinancialTable {
|
||||||
|
query: ISalesByItemsReportQuery;
|
||||||
|
meta: ISalesByItemsSheetMeta;
|
||||||
|
}
|
||||||
@@ -7,8 +7,12 @@ import {
|
|||||||
} from '@/services/Cashflow/utils';
|
} from '@/services/Cashflow/utils';
|
||||||
import AccountTransaction from './AccountTransaction';
|
import AccountTransaction from './AccountTransaction';
|
||||||
import { CASHFLOW_DIRECTION } from '@/services/Cashflow/constants';
|
import { CASHFLOW_DIRECTION } from '@/services/Cashflow/constants';
|
||||||
|
import { getTransactionTypeLabel } from '@/utils/transactions-types';
|
||||||
export default class CashflowTransaction extends TenantModel {
|
export default class CashflowTransaction extends TenantModel {
|
||||||
|
transactionType: string;
|
||||||
|
amount: number;
|
||||||
|
exchangeRate: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
@@ -55,9 +59,10 @@ export default class CashflowTransaction extends TenantModel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Transaction type formatted.
|
* Transaction type formatted.
|
||||||
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
get transactionTypeFormatted() {
|
get transactionTypeFormatted() {
|
||||||
return AccountTransaction.getReferenceTypeFormatted(this.transactionType);
|
return getTransactionTypeLabel(this.transactionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
get typeMeta() {
|
get typeMeta() {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import { allPassedConditionsPass, transformToMap } from 'utils';
|
import { allPassedConditionsPass, transformToMap } from 'utils';
|
||||||
|
|
||||||
export default class InventoryValuationSheet extends FinancialSheet {
|
export class InventoryValuationSheet extends FinancialSheet {
|
||||||
readonly query: IInventoryValuationReportQuery;
|
readonly query: IInventoryValuationReportQuery;
|
||||||
readonly items: IItem[];
|
readonly items: IItem[];
|
||||||
readonly INInventoryCostLots: Map<number, InventoryCostLotTracker>;
|
readonly INInventoryCostLots: Map<number, InventoryCostLotTracker>;
|
||||||
@@ -259,6 +259,6 @@ export default class InventoryValuationSheet extends FinancialSheet {
|
|||||||
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,76 @@
|
|||||||
|
import {
|
||||||
|
IInventoryValuationReportQuery,
|
||||||
|
IInventoryValuationSheet,
|
||||||
|
IInventoryValuationTable,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { InventoryValuationSheetService } from './InventoryValuationSheetService';
|
||||||
|
import { InventoryValuationSheetTableInjectable } from './InventoryValuationSheetTableInjectable';
|
||||||
|
import { InventoryValuationSheetExportable } from './InventoryValuationSheetExportable';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class InventoryValuationSheetApplication {
|
||||||
|
@Inject()
|
||||||
|
private inventoryValuationSheet: InventoryValuationSheetService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private inventoryValuationTable: InventoryValuationSheetTableInjectable;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private inventoryValuationExport: InventoryValuationSheetExportable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the inventory valuation json format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IInventoryValuationReportQuery} query
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public sheet(
|
||||||
|
tenantId: number,
|
||||||
|
query: IInventoryValuationReportQuery
|
||||||
|
): Promise<IInventoryValuationSheet> {
|
||||||
|
return this.inventoryValuationSheet.inventoryValuationSheet(
|
||||||
|
tenantId,
|
||||||
|
query
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the inventory valuation json table format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IInventoryValuationReportQuery} query
|
||||||
|
* @returns {Promise<IInventoryValuationTable>}
|
||||||
|
*/
|
||||||
|
public table(
|
||||||
|
tenantId: number,
|
||||||
|
query: IInventoryValuationReportQuery
|
||||||
|
): Promise<IInventoryValuationTable> {
|
||||||
|
return this.inventoryValuationTable.table(tenantId, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the inventory valuation xlsx format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IInventoryValuationReportQuery} query
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public xlsx(
|
||||||
|
tenantId: number,
|
||||||
|
query: IInventoryValuationReportQuery
|
||||||
|
): Promise<Buffer> {
|
||||||
|
return this.inventoryValuationExport.xlsx(tenantId, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the inventory valuation csv format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IInventoryValuationReportQuery} query
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public csv(
|
||||||
|
tenantId: number,
|
||||||
|
query: IInventoryValuationReportQuery
|
||||||
|
): Promise<string> {
|
||||||
|
return this.inventoryValuationExport.csv(tenantId, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { IInventoryValuationReportQuery } from '@/interfaces';
|
||||||
|
import { InventoryValuationSheetTableInjectable } from './InventoryValuationSheetTableInjectable';
|
||||||
|
import { TableSheet } from '@/lib/Xlsx/TableSheet';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class InventoryValuationSheetExportable {
|
||||||
|
@Inject()
|
||||||
|
private inventoryValuationTable: InventoryValuationSheetTableInjectable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the trial balance sheet in XLSX format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IInventoryValuationReportQuery} query
|
||||||
|
* @returns {Promise<Buffer>}
|
||||||
|
*/
|
||||||
|
public async xlsx(
|
||||||
|
tenantId: number,
|
||||||
|
query: IInventoryValuationReportQuery
|
||||||
|
): Promise<Buffer> {
|
||||||
|
const table = await this.inventoryValuationTable.table(tenantId, query);
|
||||||
|
|
||||||
|
const tableSheet = new TableSheet(table.table);
|
||||||
|
const tableCsv = tableSheet.convertToXLSX();
|
||||||
|
|
||||||
|
return tableSheet.convertToBuffer(tableCsv, 'xlsx');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the trial balance sheet in CSV format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IInventoryValuationReportQuery} query
|
||||||
|
* @returns {Promise<Buffer>}
|
||||||
|
*/
|
||||||
|
public async csv(
|
||||||
|
tenantId: number,
|
||||||
|
query: IInventoryValuationReportQuery
|
||||||
|
): Promise<string> {
|
||||||
|
const table = await this.inventoryValuationTable.table(tenantId, query);
|
||||||
|
|
||||||
|
const tableSheet = new TableSheet(table.table);
|
||||||
|
const tableCsv = tableSheet.convertToCSV();
|
||||||
|
|
||||||
|
return tableCsv;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,15 +3,16 @@ import moment from 'moment';
|
|||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
IInventoryValuationReportQuery,
|
IInventoryValuationReportQuery,
|
||||||
|
IInventoryValuationSheet,
|
||||||
IInventoryValuationSheetMeta,
|
IInventoryValuationSheetMeta,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import InventoryValuationSheet from './InventoryValuationSheet';
|
import { InventoryValuationSheet } from './InventoryValuationSheet';
|
||||||
import InventoryService from '@/services/Inventory/Inventory';
|
import InventoryService from '@/services/Inventory/Inventory';
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class InventoryValuationSheetService {
|
export class InventoryValuationSheetService {
|
||||||
@Inject()
|
@Inject()
|
||||||
tenancy: TenancyService;
|
tenancy: TenancyService;
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ export default class InventoryValuationSheetService {
|
|||||||
public async inventoryValuationSheet(
|
public async inventoryValuationSheet(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
query: IInventoryValuationReportQuery
|
query: IInventoryValuationReportQuery
|
||||||
) {
|
): Promise<IInventoryValuationSheet> {
|
||||||
const { Item, InventoryCostLotTracker } = this.tenancy.models(tenantId);
|
const { Item, InventoryCostLotTracker } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const tenant = await Tenant.query()
|
const tenant = await Tenant.query()
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import * as R from 'ramda';
|
||||||
|
import {
|
||||||
|
IInventoryValuationItem,
|
||||||
|
IInventoryValuationSheetData,
|
||||||
|
IInventoryValuationTotal,
|
||||||
|
ITableColumn,
|
||||||
|
ITableColumnAccessor,
|
||||||
|
ITableRow,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import { tableRowMapper } from '@/utils';
|
||||||
|
import FinancialSheet from '../FinancialSheet';
|
||||||
|
import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||||
|
import { FinancialTable } from '../FinancialTable';
|
||||||
|
import { ROW_TYPE } from './_constants';
|
||||||
|
|
||||||
|
export class InventoryValuationSheetTable extends R.compose(
|
||||||
|
FinancialTable,
|
||||||
|
FinancialSheetStructure
|
||||||
|
)(FinancialSheet) {
|
||||||
|
private readonly data: IInventoryValuationSheetData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
* @param {IInventoryValuationSheetData} data
|
||||||
|
*/
|
||||||
|
constructor(data: IInventoryValuationSheetData) {
|
||||||
|
super();
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the common columns accessors.
|
||||||
|
* @returns {ITableColumnAccessor}
|
||||||
|
*/
|
||||||
|
private commonColumnsAccessors(): ITableColumnAccessor[] {
|
||||||
|
return [
|
||||||
|
{ key: 'item_name', accessor: 'name' },
|
||||||
|
{ key: 'quantity', accessor: 'quantityFormatted' },
|
||||||
|
{ key: 'valuation', accessor: 'valuationFormatted' },
|
||||||
|
{ key: 'average', accessor: 'averageFormatted' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the given total node to table row.
|
||||||
|
* @param {IInventoryValuationTotal} total
|
||||||
|
* @returns {ITableRow}
|
||||||
|
*/
|
||||||
|
private totalRowMapper = (total: IInventoryValuationTotal): ITableRow => {
|
||||||
|
const accessors = this.commonColumnsAccessors();
|
||||||
|
const meta = {
|
||||||
|
rowTypes: [ROW_TYPE.TOTAL],
|
||||||
|
};
|
||||||
|
return tableRowMapper(total, accessors, meta);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the given item node to table row.
|
||||||
|
* @param {IInventoryValuationItem} item
|
||||||
|
* @returns {ITableRow}
|
||||||
|
*/
|
||||||
|
private itemRowMapper = (item: IInventoryValuationItem): ITableRow => {
|
||||||
|
const accessors = this.commonColumnsAccessors();
|
||||||
|
const meta = {
|
||||||
|
rowTypes: [ROW_TYPE.ITEM],
|
||||||
|
};
|
||||||
|
return tableRowMapper(item, accessors, meta);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the given items nodes to table rowes.
|
||||||
|
* @param {IInventoryValuationItem[]} items
|
||||||
|
* @returns {ITableRow[]}
|
||||||
|
*/
|
||||||
|
private itemsRowsMapper = (items: IInventoryValuationItem[]): ITableRow[] => {
|
||||||
|
return R.map(this.itemRowMapper)(items);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the table rows.
|
||||||
|
* @returns {ITableRow[]}
|
||||||
|
*/
|
||||||
|
public tableRows(): ITableRow[] {
|
||||||
|
const itemsRows = this.itemsRowsMapper(this.data.items);
|
||||||
|
const totalRow = this.totalRowMapper(this.data.total);
|
||||||
|
|
||||||
|
return R.compose(
|
||||||
|
R.when(R.always(R.not(R.isEmpty(itemsRows))), R.append(totalRow))
|
||||||
|
)([...itemsRows]) as ITableRow[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the table columns.
|
||||||
|
* @returns {ITableColumn[]}
|
||||||
|
*/
|
||||||
|
public tableColumns(): ITableColumn[] {
|
||||||
|
const columns = [
|
||||||
|
{ key: 'item_name', label: 'Item Name' },
|
||||||
|
{ key: 'quantity', label: 'Quantity' },
|
||||||
|
{ key: 'valuation', label: 'Valuation' },
|
||||||
|
{ key: 'average', label: 'Average' },
|
||||||
|
];
|
||||||
|
return R.compose(this.tableColumnsCellIndexing)(columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { InventoryValuationSheetService } from './InventoryValuationSheetService';
|
||||||
|
import {
|
||||||
|
IInventoryValuationReportQuery,
|
||||||
|
IInventoryValuationTable,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import { InventoryValuationSheetTable } from './InventoryValuationSheetTable';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class InventoryValuationSheetTableInjectable {
|
||||||
|
@Inject()
|
||||||
|
private sheet: InventoryValuationSheetService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the inventory valuation json table format.
|
||||||
|
* @param {number} tenantId -
|
||||||
|
* @param {IInventoryValuationReportQuery} filter -
|
||||||
|
* @returns {Promise<IInventoryValuationTable>}
|
||||||
|
*/
|
||||||
|
public async table(
|
||||||
|
tenantId: number,
|
||||||
|
filter: IInventoryValuationReportQuery
|
||||||
|
): Promise<IInventoryValuationTable> {
|
||||||
|
const { data, query, meta } = await this.sheet.inventoryValuationSheet(
|
||||||
|
tenantId,
|
||||||
|
filter
|
||||||
|
);
|
||||||
|
const table = new InventoryValuationSheetTable(data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
table: {
|
||||||
|
columns: table.tableColumns(),
|
||||||
|
rows: table.tableRows(),
|
||||||
|
},
|
||||||
|
query,
|
||||||
|
meta,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export enum ROW_TYPE {
|
||||||
|
ITEM = 'ITEM',
|
||||||
|
TOTAL = 'TOTAL',
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
IAccountTransaction,
|
IAccountTransaction,
|
||||||
ISalesByItemsItem,
|
ISalesByItemsItem,
|
||||||
ISalesByItemsTotal,
|
ISalesByItemsTotal,
|
||||||
ISalesByItemsSheetStatement,
|
ISalesByItemsSheetData,
|
||||||
IItem,
|
IItem,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ export default class SalesByItemsReport extends FinancialSheet {
|
|||||||
* @param {IInventoryValuationItem[]} items
|
* @param {IInventoryValuationItem[]} items
|
||||||
* @returns {IInventoryValuationTotal}
|
* @returns {IInventoryValuationTotal}
|
||||||
*/
|
*/
|
||||||
totalSection(items: ISalesByItemsItem[]): ISalesByItemsTotal {
|
private totalSection(items: ISalesByItemsItem[]): ISalesByItemsTotal {
|
||||||
const quantitySold = sumBy(items, (item) => item.quantitySold);
|
const quantitySold = sumBy(items, (item) => item.quantitySold);
|
||||||
const soldCost = sumBy(items, (item) => item.soldCost);
|
const soldCost = sumBy(items, (item) => item.soldCost);
|
||||||
|
|
||||||
@@ -163,12 +163,12 @@ export default class SalesByItemsReport extends FinancialSheet {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the sheet data.
|
* Retrieve the sheet data.
|
||||||
* @returns {ISalesByItemsSheetStatement}
|
* @returns {ISalesByItemsSheetData}
|
||||||
*/
|
*/
|
||||||
reportData(): ISalesByItemsSheetStatement {
|
public reportData(): ISalesByItemsSheetData {
|
||||||
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,74 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import {
|
||||||
|
ISalesByItemsReportQuery,
|
||||||
|
ISalesByItemsSheet,
|
||||||
|
ISalesByItemsSheetData,
|
||||||
|
ISalesByItemsTable,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import { SalesByItemsReportService } from './SalesByItemsService';
|
||||||
|
import { SalesByItemsTableInjectable } from './SalesByItemsTableInjectable';
|
||||||
|
import { SalesByItemsExport } from './SalesByItemsExport';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class SalesByItemsApplication {
|
||||||
|
@Inject()
|
||||||
|
private salesByItemsSheet: SalesByItemsReportService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private salesByItemsTable: SalesByItemsTableInjectable;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private salesByItemsExport: SalesByItemsExport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the sales by items report in json format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ISalesByItemsReportQuery} filter
|
||||||
|
* @returns {Promise<ISalesByItemsSheetData>}
|
||||||
|
*/
|
||||||
|
public sheet(
|
||||||
|
tenantId: number,
|
||||||
|
filter: ISalesByItemsReportQuery
|
||||||
|
): Promise<ISalesByItemsSheet> {
|
||||||
|
return this.salesByItemsSheet.salesByItems(tenantId, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the sales by items report in table format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ISalesByItemsReportQuery} filter
|
||||||
|
* @returns {Promise<ISalesByItemsTable>}
|
||||||
|
*/
|
||||||
|
public table(
|
||||||
|
tenantId: number,
|
||||||
|
filter: ISalesByItemsReportQuery
|
||||||
|
): Promise<ISalesByItemsTable> {
|
||||||
|
return this.salesByItemsTable.table(tenantId, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the sales by items report in csv format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ISalesByItemsReportQuery} filter
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
public csv(
|
||||||
|
tenantId: number,
|
||||||
|
filter: ISalesByItemsReportQuery
|
||||||
|
): Promise<string> {
|
||||||
|
return this.salesByItemsExport.csv(tenantId, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the sales by items report in xlsx format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ISalesByItemsReportQuery} filter
|
||||||
|
* @returns {Promise<Buffer>}
|
||||||
|
*/
|
||||||
|
public xlsx(
|
||||||
|
tenantId: number,
|
||||||
|
filter: ISalesByItemsReportQuery
|
||||||
|
): Promise<Buffer> {
|
||||||
|
return this.salesByItemsExport.xlsx(tenantId, filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { TableSheet } from '@/lib/Xlsx/TableSheet';
|
||||||
|
import { ISalesByItemsReportQuery } from '@/interfaces';
|
||||||
|
import { SalesByItemsTableInjectable } from './SalesByItemsTableInjectable';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class SalesByItemsExport {
|
||||||
|
@Inject()
|
||||||
|
private salesByItemsTable: SalesByItemsTableInjectable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the trial balance sheet in XLSX format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ISalesByItemsReportQuery} query
|
||||||
|
* @returns {Promise<Buffer>}
|
||||||
|
*/
|
||||||
|
public async xlsx(tenantId: number, query: ISalesByItemsReportQuery) {
|
||||||
|
const table = await this.salesByItemsTable.table(tenantId, query);
|
||||||
|
|
||||||
|
const tableSheet = new TableSheet(table.table);
|
||||||
|
const tableCsv = tableSheet.convertToXLSX();
|
||||||
|
|
||||||
|
return tableSheet.convertToBuffer(tableCsv, 'xlsx');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the trial balance sheet in CSV format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ISalesByItemsReportQuery} query
|
||||||
|
* @returns {Promise<Buffer>}
|
||||||
|
*/
|
||||||
|
public async csv(
|
||||||
|
tenantId: number,
|
||||||
|
query: ISalesByItemsReportQuery
|
||||||
|
): Promise<string> {
|
||||||
|
const table = await this.salesByItemsTable.table(tenantId, query);
|
||||||
|
|
||||||
|
const tableSheet = new TableSheet(table.table);
|
||||||
|
const tableCsv = tableSheet.convertToCSV();
|
||||||
|
|
||||||
|
return tableCsv;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,15 @@ import { Service, Inject } from 'typedi';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {
|
import {
|
||||||
ISalesByItemsReportQuery,
|
ISalesByItemsReportQuery,
|
||||||
ISalesByItemsSheetStatement,
|
ISalesByItemsSheetMeta,
|
||||||
ISalesByItemsSheetMeta
|
ISalesByItemsSheet,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import SalesByItems from './SalesByItems';
|
import SalesByItems from './SalesByItems';
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class SalesByItemsReportService {
|
export class SalesByItemsReportService {
|
||||||
@Inject()
|
@Inject()
|
||||||
tenancy: TenancyService;
|
tenancy: TenancyService;
|
||||||
|
|
||||||
@@ -63,20 +63,14 @@ export default class SalesByItemsReportService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve balance sheet statement.
|
* Retrieve balance sheet statement.
|
||||||
* -------------
|
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {IBalanceSheetQuery} query
|
* @param {IBalanceSheetQuery} query
|
||||||
*
|
* @return {Promise<ISalesByItemsSheet>}
|
||||||
* @return {IBalanceSheetStatement}
|
|
||||||
*/
|
*/
|
||||||
public async salesByItems(
|
public async salesByItems(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
query: ISalesByItemsReportQuery
|
query: ISalesByItemsReportQuery
|
||||||
): Promise<{
|
): Promise<ISalesByItemsSheet> {
|
||||||
data: ISalesByItemsSheetStatement,
|
|
||||||
query: ISalesByItemsReportQuery,
|
|
||||||
meta: ISalesByItemsSheetMeta,
|
|
||||||
}> {
|
|
||||||
const { Item, InventoryTransaction } = this.tenancy.models(tenantId);
|
const { Item, InventoryTransaction } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const tenant = await Tenant.query()
|
const tenant = await Tenant.query()
|
||||||
@@ -107,20 +101,19 @@ export default class SalesByItemsReportService {
|
|||||||
builder.whereIn('itemId', inventoryItemsIds);
|
builder.whereIn('itemId', inventoryItemsIds);
|
||||||
|
|
||||||
// Filter the date range of the sheet.
|
// Filter the date range of the sheet.
|
||||||
builder.modify('filterDateRange', filter.fromDate, filter.toDate)
|
builder.modify('filterDateRange', filter.fromDate, filter.toDate);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const sheet = new SalesByItems(
|
||||||
const purchasesByItemsInstance = new SalesByItems(
|
|
||||||
filter,
|
filter,
|
||||||
inventoryItems,
|
inventoryItems,
|
||||||
inventoryTransactions,
|
inventoryTransactions,
|
||||||
tenant.metadata.baseCurrency,
|
tenant.metadata.baseCurrency
|
||||||
);
|
);
|
||||||
const purchasesByItemsData = purchasesByItemsInstance.reportData();
|
const salesByItemsData = sheet.reportData();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: purchasesByItemsData,
|
data: salesByItemsData,
|
||||||
query: filter,
|
query: filter,
|
||||||
meta: this.reportMetadata(tenantId),
|
meta: this.reportMetadata(tenantId),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import * as R from 'ramda';
|
||||||
|
import {
|
||||||
|
ISalesByItemsItem,
|
||||||
|
ISalesByItemsSheetStatement,
|
||||||
|
ISalesByItemsTotal,
|
||||||
|
ITableColumn,
|
||||||
|
ITableRow,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import { tableRowMapper } from '@/utils';
|
||||||
|
import FinancialSheet from '../FinancialSheet';
|
||||||
|
import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||||
|
import { FinancialTable } from '../FinancialTable';
|
||||||
|
import { ROW_TYPE } from './constants';
|
||||||
|
|
||||||
|
export class SalesByItemsTable extends R.compose(
|
||||||
|
FinancialTable,
|
||||||
|
FinancialSheetStructure
|
||||||
|
)(FinancialSheet) {
|
||||||
|
private readonly data: ISalesByItemsSheetStatement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
* @param {ISalesByItemsSheetStatement} data
|
||||||
|
*/
|
||||||
|
constructor(data: ISalesByItemsSheetStatement) {
|
||||||
|
super();
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the common table accessors.
|
||||||
|
* @returns {ITableColumn[]}
|
||||||
|
*/
|
||||||
|
private commonTableAccessors() {
|
||||||
|
return [
|
||||||
|
{ key: 'item_name', accessor: 'name' },
|
||||||
|
{ key: 'sold_quantity', accessor: 'quantitySoldFormatted' },
|
||||||
|
{ key: 'sold_amount', accessor: 'soldCostFormatted' },
|
||||||
|
{ key: 'average_price', accessor: 'averageSellPriceFormatted' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the given item node to table row.
|
||||||
|
* @param {ISalesByItemsItem} item
|
||||||
|
* @returns {ITableRow}
|
||||||
|
*/
|
||||||
|
private itemMap = (item: ISalesByItemsItem): 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 {ISalesByItemsItem[]} items
|
||||||
|
* @returns {ITableRow[]}
|
||||||
|
*/
|
||||||
|
private itemsMap = (items: ISalesByItemsItem[]): ITableRow[] => {
|
||||||
|
return R.map(this.itemMap, items);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the given total node to table row.
|
||||||
|
* @param {ISalesByItemsTotal} total
|
||||||
|
* @returns {ITableRow[]}
|
||||||
|
*/
|
||||||
|
private totalMap = (total: ISalesByItemsTotal) => {
|
||||||
|
const columns = this.commonTableAccessors();
|
||||||
|
const meta = {
|
||||||
|
rowTypes: [ROW_TYPE.TOTAL],
|
||||||
|
};
|
||||||
|
return tableRowMapper(total, columns, meta);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the table rows.
|
||||||
|
* @returns {ITableRow[]}
|
||||||
|
*/
|
||||||
|
public tableData(): ITableRow[] {
|
||||||
|
const itemsRows = this.itemsMap(this.data.items);
|
||||||
|
const totalRow = this.totalMap(this.data.total);
|
||||||
|
|
||||||
|
return R.compose(
|
||||||
|
R.when(R.always(R.not(R.isEmpty(itemsRows))), R.append(totalRow))
|
||||||
|
)([...itemsRows]) as ITableRow[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the table columns.
|
||||||
|
* @returns {ITableColumn[]}
|
||||||
|
*/
|
||||||
|
public tableColumns(): ITableColumn[] {
|
||||||
|
const columns = [
|
||||||
|
{ key: 'item_name', label: 'Item name' },
|
||||||
|
{ key: 'sold_quantity', label: 'Sold quantity' },
|
||||||
|
{ key: 'sold_amount', label: 'Sold amount' },
|
||||||
|
{ key: 'average_price', label: 'Average price' },
|
||||||
|
];
|
||||||
|
return R.compose(this.tableColumnsCellIndexing)(columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { ISalesByItemsReportQuery } from '@/interfaces';
|
||||||
|
import { SalesByItemsReportService } from './SalesByItemsService';
|
||||||
|
import { SalesByItemsTable } from './SalesByItemsTable';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class SalesByItemsTableInjectable {
|
||||||
|
@Inject()
|
||||||
|
private salesByItemSheet: SalesByItemsReportService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the sales by items report in table format.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ISalesByItemsReportQuery} filter
|
||||||
|
* @returns {Promise<ISalesByItemsTable>}
|
||||||
|
*/
|
||||||
|
public async table(tenantId: number, filter: ISalesByItemsReportQuery) {
|
||||||
|
const { data, query, meta } = await this.salesByItemSheet.salesByItems(
|
||||||
|
tenantId,
|
||||||
|
filter
|
||||||
|
);
|
||||||
|
const table = new SalesByItemsTable(data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
table: {
|
||||||
|
columns: table.tableColumns(),
|
||||||
|
rows: table.tableData(),
|
||||||
|
},
|
||||||
|
meta,
|
||||||
|
query,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export enum ROW_TYPE {
|
||||||
|
ITEM = 'ITEM',
|
||||||
|
TOTAL = 'TOTAL',
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
||||||
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import { GetSaleInvoice } from './GetSaleInvoice';
|
||||||
import { CommandSaleInvoiceValidators } from './CommandSaleInvoiceValidators';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleInvoicePdf {
|
export class SaleInvoicePdf {
|
||||||
@@ -13,10 +12,7 @@ export class SaleInvoicePdf {
|
|||||||
private templateInjectable: TemplateInjectable;
|
private templateInjectable: TemplateInjectable;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private validators: CommandSaleInvoiceValidators;
|
private getInvoiceService: GetSaleInvoice;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private tenancy: HasTenancyService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice pdf content.
|
* Retrieve sale invoice pdf content.
|
||||||
@@ -28,18 +24,10 @@ export class SaleInvoicePdf {
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
invoiceId: number
|
invoiceId: number
|
||||||
): Promise<Buffer> {
|
): Promise<Buffer> {
|
||||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
const saleInvoice = await this.getInvoiceService.getSaleInvoice(
|
||||||
|
tenantId,
|
||||||
const saleInvoice = await SaleInvoice.query()
|
invoiceId
|
||||||
.findById(invoiceId)
|
);
|
||||||
.withGraphFetched('entries.item')
|
|
||||||
.withGraphFetched('entries.tax')
|
|
||||||
.withGraphFetched('customer')
|
|
||||||
.withGraphFetched('taxes.taxRate');
|
|
||||||
|
|
||||||
// Validates the given sale invoice existance.
|
|
||||||
this.validators.validateInvoiceExistance(saleInvoice);
|
|
||||||
|
|
||||||
const htmlContent = await this.templateInjectable.render(
|
const htmlContent = await this.templateInjectable.render(
|
||||||
tenantId,
|
tenantId,
|
||||||
'modules/invoice-regular',
|
'modules/invoice-regular',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export class PaymentReceiveEntryTransfromer extends Transformer {
|
|||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
public includeAttributes = (): string[] => {
|
public includeAttributes = (): string[] => {
|
||||||
return ['paymentAmountFormatted', 'entry'];
|
return ['paymentAmountFormatted', 'invoice'];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { IPaymentReceive, IPaymentReceiveEntry } from '@/interfaces';
|
import { IPaymentReceive, IPaymentReceiveEntry } from '@/interfaces';
|
||||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||||
import { formatNumber } from 'utils';
|
import { formatNumber } from 'utils';
|
||||||
import { SaleInvoiceTransformer } from '../Invoices/SaleInvoiceTransformer';
|
|
||||||
import { PaymentReceiveEntryTransfromer } from './PaymentReceiveEntryTransformer';
|
import { PaymentReceiveEntryTransfromer } from './PaymentReceiveEntryTransformer';
|
||||||
|
|
||||||
export class PaymentReceiveTransfromer extends Transformer {
|
export class PaymentReceiveTransfromer extends Transformer {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ function CustomerSelectRoot({
|
|||||||
<FSelect
|
<FSelect
|
||||||
items={items}
|
items={items}
|
||||||
textAccessor={'display_name'}
|
textAccessor={'display_name'}
|
||||||
labelAccessor={'currency_code'}
|
labelAccessor={'formatted_balance'}
|
||||||
valueAccessor={'id'}
|
valueAccessor={'id'}
|
||||||
popoverProps={{ minimal: true, usePortal: true, inline: false }}
|
popoverProps={{ minimal: true, usePortal: true, inline: false }}
|
||||||
createNewItemRenderer={maybeCreateNewItemRenderer}
|
createNewItemRenderer={maybeCreateNewItemRenderer}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function VendorsSelectRoot({
|
|||||||
<FSelect
|
<FSelect
|
||||||
items={items}
|
items={items}
|
||||||
textAccessor={'display_name'}
|
textAccessor={'display_name'}
|
||||||
labelAccessor={'code'}
|
labelAccessor={'formatted_balance'}
|
||||||
valueAccessor={'id'}
|
valueAccessor={'id'}
|
||||||
popoverProps={{ minimal: true, usePortal: true, inline: false }}
|
popoverProps={{ minimal: true, usePortal: true, inline: false }}
|
||||||
createNewItemRenderer={maybeCreateNewItemRenderer}
|
createNewItemRenderer={maybeCreateNewItemRenderer}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const AccountInactivateAlert = React.lazy(
|
|||||||
() => import('@/containers/Alerts/Accounts/AccountInactivateAlert'),
|
() => import('@/containers/Alerts/Accounts/AccountInactivateAlert'),
|
||||||
);
|
);
|
||||||
const AccountActivateAlert = React.lazy(
|
const AccountActivateAlert = React.lazy(
|
||||||
() => import('@/containers/Alerts/Accounts/AccountDeleteAlert'),
|
() => import('@/containers/Alerts/Accounts/AccountActivateAlert'),
|
||||||
);
|
);
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
|
||||||
import { MoneyInDialogProvider } from './MoneyInDialogProvider';
|
import { MoneyInDialogProvider } from './MoneyInDialogProvider';
|
||||||
import MoneyInForm from './MoneyInForm';
|
import MoneyInForm from './MoneyInForm';
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ function MoneyInForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Handles the form submit.
|
// Handles the form submit.
|
||||||
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
|
const handleFormSubmit = (values, { setSubmitting }) => {
|
||||||
const form = {
|
const form = {
|
||||||
...omit(values, ['currency_code']),
|
...omit(values, ['currency_code']),
|
||||||
publish: true,
|
publish: true,
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ function MoneyOutForm({
|
|||||||
accountId,
|
accountId,
|
||||||
accountType,
|
accountType,
|
||||||
createCashflowTransactionMutate,
|
createCashflowTransactionMutate,
|
||||||
submitPayload,
|
|
||||||
} = useMoneyOutDialogContext();
|
} = useMoneyOutDialogContext();
|
||||||
|
|
||||||
// transaction number.
|
// transaction number.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { useUpdateEffect } from '@/hooks';
|
|||||||
|
|
||||||
import withSettings from '@/containers/Settings/withSettings';
|
import withSettings from '@/containers/Settings/withSettings';
|
||||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||||
|
import { transactionNumber } from '@/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Syncs cashflow auto-increment settings to the form once update.
|
* Syncs cashflow auto-increment settings to the form once update.
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import ReferenceNumberForm from '@/containers/JournalNumber/ReferenceNumberForm'
|
|||||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||||
import withSettings from '@/containers/Settings/withSettings';
|
import withSettings from '@/containers/Settings/withSettings';
|
||||||
import withSettingsActions from '@/containers/Settings/withSettingsActions';
|
import withSettingsActions from '@/containers/Settings/withSettingsActions';
|
||||||
import { compose } from '@/utils';
|
|
||||||
import {
|
import {
|
||||||
transformFormToSettings,
|
transformFormToSettings,
|
||||||
transformSettingsToForm,
|
transformSettingsToForm,
|
||||||
} from '@/containers/JournalNumber/utils';
|
} from '@/containers/JournalNumber/utils';
|
||||||
|
import { compose } from '@/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transaction number dialog content.
|
* Transaction number dialog content.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import withInventoryValuationActions from './withInventoryValuationActions';
|
|||||||
import { useInventoryValuationContext } from './InventoryValuationProvider';
|
import { useInventoryValuationContext } from './InventoryValuationProvider';
|
||||||
|
|
||||||
import { compose, saveInvoke } from '@/utils';
|
import { compose, saveInvoke } from '@/utils';
|
||||||
|
import { InventoryValuationExportMenu } from './components';
|
||||||
|
|
||||||
function InventoryValuationActionsBar({
|
function InventoryValuationActionsBar({
|
||||||
// #withInventoryValuation
|
// #withInventoryValuation
|
||||||
@@ -109,11 +110,18 @@ function InventoryValuationActionsBar({
|
|||||||
icon={<Icon icon="print-16" iconSize={16} />}
|
icon={<Icon icon="print-16" iconSize={16} />}
|
||||||
text={<T id={'print'} />}
|
text={<T id={'print'} />}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Popover
|
||||||
className={Classes.MINIMAL}
|
content={<InventoryValuationExportMenu />}
|
||||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
text={<T id={'export'} />}
|
position={Position.BOTTOM_LEFT}
|
||||||
/>
|
minimal
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||||
|
text={<T id={'export'} />}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</DashboardActionsBar>
|
</DashboardActionsBar>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FinancialReportPage from '../FinancialReportPage';
|
import FinancialReportPage from '../FinancialReportPage';
|
||||||
import { useInventoryValuation } from '@/hooks/query';
|
import { useInventoryValuationTable } from '@/hooks/query';
|
||||||
import { transformFilterFormToQuery } from '../common';
|
import { transformFilterFormToQuery } from '../common';
|
||||||
|
|
||||||
const InventoryValuationContext = React.createContext();
|
const InventoryValuationContext = React.createContext();
|
||||||
@@ -21,7 +21,7 @@ function InventoryValuationProvider({ query, ...props }) {
|
|||||||
isFetching,
|
isFetching,
|
||||||
isLoading,
|
isLoading,
|
||||||
refetch,
|
refetch,
|
||||||
} = useInventoryValuation(requestQuery, {
|
} = useInventoryValuationTable(requestQuery, {
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,23 +8,23 @@ import { ReportDataTable, FinancialSheet } from '@/components';
|
|||||||
import { tableRowTypesToClassnames } from '@/utils';
|
import { tableRowTypesToClassnames } from '@/utils';
|
||||||
|
|
||||||
import { useInventoryValuationContext } from './InventoryValuationProvider';
|
import { useInventoryValuationContext } from './InventoryValuationProvider';
|
||||||
import { useInventoryValuationTableColumns } from './components';
|
import { useInventoryValuationColumns } from './dynamicColumns';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* inventory valuation data table.
|
* Inventory valuation data table.
|
||||||
*/
|
*/
|
||||||
export default function InventoryValuationTable({
|
export default function InventoryValuationTable({
|
||||||
//#ownProps
|
// #ownProps
|
||||||
companyName,
|
companyName,
|
||||||
}) {
|
}) {
|
||||||
// inventory valuation context.
|
// Inventory valuation context.
|
||||||
const {
|
const {
|
||||||
inventoryValuation: { tableRows, query },
|
inventoryValuation: { table, query },
|
||||||
isLoading,
|
isLoading,
|
||||||
} = useInventoryValuationContext();
|
} = useInventoryValuationContext();
|
||||||
|
|
||||||
// inventory valuation table columns.
|
// Inventory valuation table columns.
|
||||||
const columns = useInventoryValuationTableColumns();
|
const columns = useInventoryValuationColumns();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InventoryValuationSheet
|
<InventoryValuationSheet
|
||||||
@@ -35,7 +35,7 @@ export default function InventoryValuationTable({
|
|||||||
>
|
>
|
||||||
<InventoryValuationDataTable
|
<InventoryValuationDataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={tableRows}
|
data={table.rows}
|
||||||
expandable={true}
|
expandable={true}
|
||||||
expandToggleColumn={1}
|
expandToggleColumn={1}
|
||||||
expandColumnSpace={1}
|
expandColumnSpace={1}
|
||||||
@@ -62,7 +62,7 @@ const InventoryValuationDataTable = styled(ReportDataTable)`
|
|||||||
padding-top: 0.4rem;
|
padding-top: 0.4rem;
|
||||||
padding-bottom: 0.4rem;
|
padding-bottom: 0.4rem;
|
||||||
}
|
}
|
||||||
.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,13 +1,25 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useMemo } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { If } from '@/components';
|
import { AppToaster, If, Stack } from '@/components';
|
||||||
import { Align } from '@/constants';
|
import { Align } from '@/constants';
|
||||||
import { getColumnWidth } from '@/utils';
|
import { getColumnWidth } from '@/utils';
|
||||||
import { CellTextSpan } from '@/components/Datatable/Cells';
|
import { CellTextSpan } from '@/components/Datatable/Cells';
|
||||||
import { useInventoryValuationContext } from './InventoryValuationProvider';
|
import { useInventoryValuationContext } from './InventoryValuationProvider';
|
||||||
import FinancialLoadingBar from '../FinancialLoadingBar';
|
import FinancialLoadingBar from '../FinancialLoadingBar';
|
||||||
|
import {
|
||||||
|
Classes,
|
||||||
|
Intent,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
ProgressBar,
|
||||||
|
Text,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
import {
|
||||||
|
useInventoryValuationCsvExport,
|
||||||
|
useInventoryValuationXlsxExport,
|
||||||
|
} from '@/hooks/query';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve inventory valuation table columns.
|
* Retrieve inventory valuation table columns.
|
||||||
@@ -77,3 +89,87 @@ export function InventoryValuationLoadingBar() {
|
|||||||
</If>
|
</If>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the inventory valuation sheet export menu.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export const InventoryValuationExportMenu = () => {
|
||||||
|
const toastKey = useRef(null);
|
||||||
|
const commonToastConfig = {
|
||||||
|
isCloseButtonShown: true,
|
||||||
|
timeout: 2000,
|
||||||
|
};
|
||||||
|
const { query } = useInventoryValuationContext();
|
||||||
|
|
||||||
|
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 } = useInventoryValuationXlsxExport(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 } = useInventoryValuationCsvExport(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,97 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { Align } from '@/constants';
|
||||||
|
import { getColumnWidth } from '@/utils';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import { useInventoryValuationContext } from './InventoryValuationProvider';
|
||||||
|
|
||||||
|
const getTableCellValueAccessor = (index) => `cells[${index}].value`;
|
||||||
|
|
||||||
|
const getReportColWidth = (data, accessor, headerText) => {
|
||||||
|
return getColumnWidth(
|
||||||
|
data,
|
||||||
|
accessor,
|
||||||
|
{ magicSpacing: 10, minWidth: 100 },
|
||||||
|
headerText,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common column mapper.
|
||||||
|
*/
|
||||||
|
const commonAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: column.key,
|
||||||
|
Header: column.label,
|
||||||
|
accessor,
|
||||||
|
className: column.key,
|
||||||
|
textOverview: true,
|
||||||
|
align: Align.Left,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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: 240,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamic column mapper.
|
||||||
|
* @param {} data -
|
||||||
|
* @param {} column -
|
||||||
|
*/
|
||||||
|
const dynamicColumnMapper = R.curry((data, column) => {
|
||||||
|
const _commonAccessor = commonAccessor(data);
|
||||||
|
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'), _numericColumnAccessor),
|
||||||
|
R.when(R.pathEq(['key'], 'valuation'), _numericColumnAccessor),
|
||||||
|
R.when(R.pathEq(['key'], 'average'), _numericColumnAccessor),
|
||||||
|
_commonAccessor,
|
||||||
|
)(column);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composes the fetched dynamic columns from the server to the columns to pass it
|
||||||
|
* to the table component.
|
||||||
|
*/
|
||||||
|
export const dynamicColumns = (columns, data) => {
|
||||||
|
return R.map(dynamicColumnMapper(data), columns);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the table columns of inventory valuation sheet.
|
||||||
|
*/
|
||||||
|
export const useInventoryValuationColumns = () => {
|
||||||
|
const { inventoryValuation } = useInventoryValuationContext();
|
||||||
|
|
||||||
|
if (!inventoryValuation) {
|
||||||
|
throw new Error('The inventory valuation is not loaded');
|
||||||
|
}
|
||||||
|
const { table } = inventoryValuation;
|
||||||
|
|
||||||
|
return dynamicColumns(table.columns, table.rows);
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { createContext, useContext } from 'react';
|
import { createContext, useContext } from 'react';
|
||||||
import FinancialReportPage from '../FinancialReportPage';
|
import FinancialReportPage from '../FinancialReportPage';
|
||||||
import { useSalesByItems } from '@/hooks/query';
|
import { useSalesByItemsTable } from '@/hooks/query';
|
||||||
import { transformFilterFormToQuery } from '../common';
|
import { transformFilterFormToQuery } from '../common';
|
||||||
|
|
||||||
const SalesByItemsContext = createContext();
|
const SalesByItemsContext = createContext();
|
||||||
@@ -12,7 +12,7 @@ function SalesByItemProvider({ query, ...props }) {
|
|||||||
isFetching,
|
isFetching,
|
||||||
isLoading,
|
isLoading,
|
||||||
refetch,
|
refetch,
|
||||||
} = useSalesByItems(
|
} = useSalesByItemsTable(
|
||||||
{
|
{
|
||||||
...transformFilterFormToQuery(query),
|
...transformFilterFormToQuery(query),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import withSalesByItemsActions from './withSalesByItemsActions';
|
|||||||
|
|
||||||
import { compose, saveInvoke } from '@/utils';
|
import { compose, saveInvoke } from '@/utils';
|
||||||
import { useSalesByItemsContext } from './SalesByItemProvider';
|
import { useSalesByItemsContext } from './SalesByItemProvider';
|
||||||
|
import { SalesByItemsSheetExportMenu } from './components';
|
||||||
|
|
||||||
function SalesByItemsActionsBar({
|
function SalesByItemsActionsBar({
|
||||||
// #withSalesByItems
|
// #withSalesByItems
|
||||||
@@ -108,11 +109,18 @@ function SalesByItemsActionsBar({
|
|||||||
icon={<Icon icon="print-16" iconSize={16} />}
|
icon={<Icon icon="print-16" iconSize={16} />}
|
||||||
text={<T id={'print'} />}
|
text={<T id={'print'} />}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Popover
|
||||||
className={Classes.MINIMAL}
|
content={<SalesByItemsSheetExportMenu />}
|
||||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
text={<T id={'export'} />}
|
placement="bottom-start"
|
||||||
/>
|
minimal
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||||
|
text={<T id={'export'} />}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</DashboardActionsBar>
|
</DashboardActionsBar>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import { ReportDataTable, FinancialSheet } from '@/components';
|
import { ReportDataTable, FinancialSheet } from '@/components';
|
||||||
import { useSalesByItemsContext } from './SalesByItemProvider';
|
import { useSalesByItemsContext } from './SalesByItemProvider';
|
||||||
import { useSalesByItemsTableColumns } from './components';
|
import { useSalesByItemsTableColumns } from './dynamicColumns';
|
||||||
import { tableRowTypesToClassnames } from '@/utils';
|
import { tableRowTypesToClassnames } from '@/utils';
|
||||||
import { TableStyle } from '@/constants';
|
import { TableStyle } from '@/constants';
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import { TableStyle } from '@/constants';
|
|||||||
export default function SalesByItemsTable({ companyName }) {
|
export default function SalesByItemsTable({ companyName }) {
|
||||||
// Sales by items context.
|
// Sales by items context.
|
||||||
const {
|
const {
|
||||||
salesByItems: { tableRows, query },
|
salesByItems: { table, query },
|
||||||
isLoading,
|
isLoading,
|
||||||
} = useSalesByItemsContext();
|
} = useSalesByItemsContext();
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export default function SalesByItemsTable({ companyName }) {
|
|||||||
>
|
>
|
||||||
<SalesByItemsDataTable
|
<SalesByItemsDataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={tableRows}
|
data={table.rows}
|
||||||
expandable={true}
|
expandable={true}
|
||||||
expandToggleColumn={1}
|
expandToggleColumn={1}
|
||||||
expandColumnSpace={1}
|
expandColumnSpace={1}
|
||||||
@@ -59,7 +59,7 @@ const SalesByItemsDataTable = styled(ReportDataTable)`
|
|||||||
padding-top: 0.4rem;
|
padding-top: 0.4rem;
|
||||||
padding-bottom: 0.4rem;
|
padding-bottom: 0.4rem;
|
||||||
}
|
}
|
||||||
.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,70 +1,20 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useMemo } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { Classes } from '@blueprintjs/core';
|
||||||
|
|
||||||
import { getColumnWidth } from '@/utils';
|
import { getColumnWidth } from '@/utils';
|
||||||
import { If } from '@/components';
|
import { AppToaster, If, Stack } from '@/components';
|
||||||
import { Align } from '@/constants';
|
import { Align } from '@/constants';
|
||||||
import { CellTextSpan } from '@/components/Datatable/Cells';
|
import { CellTextSpan } from '@/components/Datatable/Cells';
|
||||||
import { useSalesByItemsContext } from './SalesByItemProvider';
|
import { useSalesByItemsContext } from './SalesByItemProvider';
|
||||||
import FinancialLoadingBar from '../FinancialLoadingBar';
|
import FinancialLoadingBar from '../FinancialLoadingBar';
|
||||||
|
import { Intent, Menu, MenuItem, ProgressBar, Text } from '@blueprintjs/core';
|
||||||
|
import {
|
||||||
/**
|
useSalesByItemsCsvExport,
|
||||||
* Retrieve sales by items table columns.
|
useSalesByItemsXlsxExport,
|
||||||
*/
|
} from '@/hooks/query';
|
||||||
export const useSalesByItemsTableColumns = () => {
|
|
||||||
//sales by items context.
|
|
||||||
const {
|
|
||||||
salesByItems: { tableRows },
|
|
||||||
} = useSalesByItemsContext();
|
|
||||||
|
|
||||||
return 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('sold_quantity'),
|
|
||||||
accessor: 'quantity_sold_formatted',
|
|
||||||
Cell: CellTextSpan,
|
|
||||||
className: 'quantity_sold',
|
|
||||||
width: getColumnWidth(tableRows, `quantity_sold_formatted`, {
|
|
||||||
minWidth: 150,
|
|
||||||
}),
|
|
||||||
textOverview: true,
|
|
||||||
align: Align.Right,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: intl.get('sold_amount'),
|
|
||||||
accessor: 'sold_cost_formatted',
|
|
||||||
Cell: CellTextSpan,
|
|
||||||
className: 'sold_cost',
|
|
||||||
width: getColumnWidth(tableRows, `sold_cost_formatted`, {
|
|
||||||
minWidth: 150,
|
|
||||||
}),
|
|
||||||
textOverview: true,
|
|
||||||
align: Align.Right,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: intl.get('average_price'),
|
|
||||||
accessor: 'average_sell_price_formatted',
|
|
||||||
Cell: CellTextSpan,
|
|
||||||
className: 'average_sell_price',
|
|
||||||
width: getColumnWidth(tableRows, `average_sell_price_formatted`, {
|
|
||||||
minWidth: 150,
|
|
||||||
}),
|
|
||||||
textOverview: true,
|
|
||||||
align: Align.Right,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[tableRows],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sales by items progress loading bar.
|
* sales by items progress loading bar.
|
||||||
@@ -77,3 +27,88 @@ export function SalesByItemsLoadingBar() {
|
|||||||
</If>
|
</If>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the sales by items export menu.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export const SalesByItemsSheetExportMenu = () => {
|
||||||
|
const toastKey = useRef(null);
|
||||||
|
const commonToastConfig = {
|
||||||
|
isCloseButtonShown: true,
|
||||||
|
timeout: 2000,
|
||||||
|
};
|
||||||
|
const { query } = useSalesByItemsContext();
|
||||||
|
|
||||||
|
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 } = useSalesByItemsXlsxExport(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 } = useSalesByItemsCsvExport(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 { useSalesByItemsContext } from './SalesByItemProvider';
|
||||||
|
|
||||||
|
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'], 'sold_quantity'), _numericColumnAccessor),
|
||||||
|
R.when(R.pathEq(['key'], 'sold_amount'), _numericColumnAccessor),
|
||||||
|
R.when(R.pathEq(['key'], 'average_price'), _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 G/L sheet table columns for table component.
|
||||||
|
*/
|
||||||
|
export const useSalesByItemsTableColumns = () => {
|
||||||
|
const { salesByItems } = useSalesByItemsContext();
|
||||||
|
|
||||||
|
if (!salesByItems) {
|
||||||
|
throw new Error('Sales by items context not found');
|
||||||
|
}
|
||||||
|
const { table } = salesByItems;
|
||||||
|
|
||||||
|
return dynamicColumns(table.rows, table.columns);
|
||||||
|
};
|
||||||
@@ -1,194 +1,4 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
|
||||||
import moment from 'moment';
|
|
||||||
import { chain } from 'lodash';
|
|
||||||
import { FormattedMessage as T } from '@/components';
|
|
||||||
|
|
||||||
export const trialBalanceSheetReducer = (sheet) => {
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
if (sheet.accounts) {
|
|
||||||
sheet.accounts.forEach((account) => {
|
|
||||||
results.push(account);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (sheet.total) {
|
|
||||||
results.push({
|
|
||||||
row_types: 'total',
|
|
||||||
...sheet.total,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const journalTableRowsReducer = (journal) => {
|
|
||||||
const TYPES = {
|
|
||||||
ENTRY: 'ENTRY',
|
|
||||||
TOTAL_ENTRIES: 'TOTAL_ENTRIES',
|
|
||||||
EMPTY_ROW: 'EMPTY_ROW',
|
|
||||||
};
|
|
||||||
|
|
||||||
const entriesMapper = (transaction) => {
|
|
||||||
return transaction.entries.map((entry, index) => ({
|
|
||||||
...(index === 0
|
|
||||||
? {
|
|
||||||
date: transaction.date,
|
|
||||||
reference_type: transaction.reference_type,
|
|
||||||
reference_id: transaction.reference_id,
|
|
||||||
reference_type_formatted: transaction.reference_type_formatted,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
row_types: TYPES.ENTRY,
|
|
||||||
...entry,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return chain(journal)
|
|
||||||
.map((transaction) => {
|
|
||||||
const entries = entriesMapper(transaction);
|
|
||||||
|
|
||||||
return [
|
|
||||||
...entries,
|
|
||||||
{
|
|
||||||
row_types: TYPES.TOTAL_ENTRIES,
|
|
||||||
currency_code: transaction.currency_code,
|
|
||||||
credit: transaction.credit,
|
|
||||||
debit: transaction.debit,
|
|
||||||
formatted_credit: transaction.formatted_credit,
|
|
||||||
formatted_debit: transaction.formatted_debit,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
row_types: TYPES.EMPTY_ROW,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.value();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generalLedgerTableRowsReducer = (accounts) => {
|
|
||||||
return chain(accounts)
|
|
||||||
.map((account) => {
|
|
||||||
return {
|
|
||||||
name: '',
|
|
||||||
code: account.code,
|
|
||||||
row_types: 'ACCOUNT_ROW',
|
|
||||||
date: account.name,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
...account.opening_balance,
|
|
||||||
name: <T id={'opening_balance'} />,
|
|
||||||
row_types: 'OPENING_BALANCE',
|
|
||||||
date: moment(account.opening_balance.date).format('DD MMM YYYY'),
|
|
||||||
},
|
|
||||||
...account.transactions.map((transaction) => ({
|
|
||||||
...transaction,
|
|
||||||
name: account.name,
|
|
||||||
code: account.code,
|
|
||||||
date: moment(transaction.date).format('DD MMM YYYY'),
|
|
||||||
})),
|
|
||||||
{
|
|
||||||
...account.closing_balance,
|
|
||||||
name: <T id={'closing_balance'} />,
|
|
||||||
row_types: 'CLOSING_BALANCE',
|
|
||||||
date: moment(account.closing_balance.date).format('DD MMM YYYY'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
amount: account.closing_balance.amount,
|
|
||||||
formatted_amount: account.closing_balance.formatted_amount,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.value();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ARAgingSummaryTableRowsMapper = (sheet, total) => {
|
|
||||||
const rows = [];
|
|
||||||
|
|
||||||
const mapAging = (agingPeriods) => {
|
|
||||||
return agingPeriods.reduce((acc, aging, index) => {
|
|
||||||
acc[`aging-${index}`] = aging.total.formatted_amount;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
sheet.customers.forEach((customer) => {
|
|
||||||
const agingRow = mapAging(customer.aging);
|
|
||||||
|
|
||||||
rows.push({
|
|
||||||
row_types: 'customer',
|
|
||||||
name: customer.customer_name,
|
|
||||||
...agingRow,
|
|
||||||
current: customer.current.formatted_amount,
|
|
||||||
total: customer.total.formatted_amount,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (rows.length <= 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
...rows,
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
row_types: 'total',
|
|
||||||
current: sheet.total.current.formatted_amount,
|
|
||||||
...mapAging(sheet.total.aging),
|
|
||||||
total: sheet.total.total.formatted_amount,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const APAgingSummaryTableRowsMapper = (sheet, total) => {
|
|
||||||
const rows = [];
|
|
||||||
|
|
||||||
const mapAging = (agingPeriods) => {
|
|
||||||
return agingPeriods.reduce((acc, aging, index) => {
|
|
||||||
acc[`aging-${index}`] = aging.total.formatted_amount;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
sheet.vendors.forEach((vendor) => {
|
|
||||||
const agingRow = mapAging(vendor.aging);
|
|
||||||
|
|
||||||
rows.push({
|
|
||||||
row_types: 'vendor',
|
|
||||||
name: vendor.vendor_name,
|
|
||||||
...agingRow,
|
|
||||||
current: vendor.current.formatted_amount,
|
|
||||||
total: vendor.total.formatted_amount,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (rows.length <= 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
...rows,
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
row_types: 'total',
|
|
||||||
current: sheet.total.current.formatted_amount,
|
|
||||||
...mapAging(sheet.total.aging),
|
|
||||||
total: sheet.total.total.formatted_amount,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const inventoryValuationReducer = (sheet) => {
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
if (sheet.items) {
|
|
||||||
sheet.items.forEach((item) => {
|
|
||||||
results.push(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (sheet.total) {
|
|
||||||
results.push({
|
|
||||||
row_types: 'total',
|
|
||||||
...sheet.total,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const purchasesByItemsReducer = (sheet) => {
|
export const purchasesByItemsReducer = (sheet) => {
|
||||||
const results = [];
|
const results = [];
|
||||||
@@ -206,19 +16,3 @@ export const purchasesByItemsReducer = (sheet) => {
|
|||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
export const salesByItemsReducer = (sheet) => {
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
if (sheet.items) {
|
|
||||||
sheet.items.forEach((item) => {
|
|
||||||
results.push(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (sheet.total) {
|
|
||||||
results.push({
|
|
||||||
row_types: 'total',
|
|
||||||
...sheet.total,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import useApiRequest from '../useRequest';
|
|||||||
import t from './types';
|
import t from './types';
|
||||||
|
|
||||||
const commonInvalidateQueries = (queryClient) => {
|
const commonInvalidateQueries = (queryClient) => {
|
||||||
|
// Invalidate settings.
|
||||||
|
queryClient.invalidateQueries([t.SETTING, t.SETTING_CASHFLOW]);
|
||||||
|
|
||||||
// Invalidate accounts.
|
// Invalidate accounts.
|
||||||
queryClient.invalidateQueries(t.ACCOUNTS);
|
queryClient.invalidateQueries(t.ACCOUNTS);
|
||||||
queryClient.invalidateQueries(t.ACCOUNT);
|
queryClient.invalidateQueries(t.ACCOUNT);
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { useRequestQuery } from '../useQueryRequest';
|
import { useRequestQuery } from '../useQueryRequest';
|
||||||
import {
|
import { purchasesByItemsReducer } from '@/containers/FinancialStatements/reducers';
|
||||||
inventoryValuationReducer,
|
|
||||||
purchasesByItemsReducer,
|
|
||||||
salesByItemsReducer,
|
|
||||||
} from '@/containers/FinancialStatements/reducers';
|
|
||||||
import t from './types';
|
|
||||||
import { useDownloadFile } from '../useDownloadFile';
|
import { useDownloadFile } from '../useDownloadFile';
|
||||||
|
import t from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve balance sheet.
|
* Retrieve balance sheet.
|
||||||
@@ -176,34 +172,33 @@ export function useGeneralLedgerSheet(query, props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
export const useGeneralLedgerSheetXlsxExport = (query, args) => {
|
export const useGeneralLedgerSheetXlsxExport = (query, args) => {
|
||||||
return useDownloadFile({
|
return useDownloadFile({
|
||||||
url: '/financial_statements/general_ledger',
|
url: '/financial_statements/general_ledger',
|
||||||
config: {
|
config: {
|
||||||
headers: {
|
headers: {
|
||||||
accept: 'application/xlsx',
|
accept: 'application/xlsx',
|
||||||
|
},
|
||||||
|
params: query,
|
||||||
},
|
},
|
||||||
params: query,
|
filename: 'general_ledger.xlsx',
|
||||||
},
|
...args,
|
||||||
filename: 'general_ledger.xlsx',
|
});
|
||||||
...args,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGeneralLedgerSheetCsvExport = (query, args) => {
|
export const useGeneralLedgerSheetCsvExport = (query, args) => {
|
||||||
return useDownloadFile({
|
return useDownloadFile({
|
||||||
url: '/financial_statements/general_ledger',
|
url: '/financial_statements/general_ledger',
|
||||||
config: {
|
config: {
|
||||||
headers: {
|
headers: {
|
||||||
accept: 'application/csv',
|
accept: 'application/csv',
|
||||||
|
},
|
||||||
|
params: query,
|
||||||
},
|
},
|
||||||
params: query,
|
filename: 'general_ledger.csv',
|
||||||
},
|
...args,
|
||||||
filename: 'general_ledger.csv',
|
});
|
||||||
...args,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve journal sheet.
|
* Retrieve journal sheet.
|
||||||
*/
|
*/
|
||||||
@@ -363,19 +358,62 @@ export function useInventoryValuation(query, props) {
|
|||||||
params: query,
|
params: query,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
select: (res) => ({
|
select: (res) => res.data,
|
||||||
tableRows: inventoryValuationReducer(res.data.data),
|
|
||||||
...res.data,
|
|
||||||
}),
|
|
||||||
defaultData: {
|
|
||||||
tableRows: [],
|
|
||||||
data: [],
|
|
||||||
query: {},
|
|
||||||
},
|
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve inventory valuation.
|
||||||
|
*/
|
||||||
|
export function useInventoryValuationTable(query, props) {
|
||||||
|
return useRequestQuery(
|
||||||
|
[t.FINANCIAL_REPORT, t.INVENTORY_VALUATION, query],
|
||||||
|
{
|
||||||
|
method: 'get',
|
||||||
|
url: '/financial_statements/inventory-valuation',
|
||||||
|
params: query,
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json+table',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: (res) => res.data,
|
||||||
|
...props,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useInventoryValuationXlsxExport = (query, args) => {
|
||||||
|
return useDownloadFile({
|
||||||
|
url: '/financial_statements/inventory-valuation',
|
||||||
|
config: {
|
||||||
|
headers: {
|
||||||
|
accept: 'application/xlsx',
|
||||||
|
},
|
||||||
|
params: query,
|
||||||
|
},
|
||||||
|
filename: 'inventory_valuation.xlsx',
|
||||||
|
...args,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useInventoryValuationCsvExport = (query, args) => {
|
||||||
|
return useDownloadFile({
|
||||||
|
url: '/financial_statements/inventory-valuation',
|
||||||
|
config: {
|
||||||
|
headers: {
|
||||||
|
accept: 'application/csv',
|
||||||
|
},
|
||||||
|
params: query,
|
||||||
|
},
|
||||||
|
filename: 'inventory_valuation.csv',
|
||||||
|
...args,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve purchases by items.
|
* Retrieve purchases by items.
|
||||||
*/
|
*/
|
||||||
@@ -414,20 +452,60 @@ export function useSalesByItems(query, props) {
|
|||||||
params: query,
|
params: query,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
select: (res) => ({
|
|
||||||
tableRows: salesByItemsReducer(res.data.data),
|
|
||||||
...res.data,
|
|
||||||
}),
|
|
||||||
defaultData: {
|
|
||||||
tableRows: [],
|
|
||||||
data: [],
|
|
||||||
query: {},
|
|
||||||
},
|
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves sales by items table format.
|
||||||
|
*/
|
||||||
|
export function useSalesByItemsTable(query, props) {
|
||||||
|
return useRequestQuery(
|
||||||
|
[t.FINANCIAL_REPORT, t.SALES_BY_ITEMS, query],
|
||||||
|
{
|
||||||
|
method: 'get',
|
||||||
|
url: '/financial_statements/sales-by-items',
|
||||||
|
params: query,
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json+table',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: (res) => res.data,
|
||||||
|
...props,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSalesByItemsCsvExport = (query, args) => {
|
||||||
|
return useDownloadFile({
|
||||||
|
url: '/financial_statements/sales-by-items',
|
||||||
|
config: {
|
||||||
|
headers: {
|
||||||
|
accept: 'application/csv',
|
||||||
|
},
|
||||||
|
params: query,
|
||||||
|
},
|
||||||
|
filename: 'sales_by_items.csv',
|
||||||
|
...args,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSalesByItemsXlsxExport = (query, args) => {
|
||||||
|
return useDownloadFile({
|
||||||
|
url: '/financial_statements/sales-by-items',
|
||||||
|
config: {
|
||||||
|
headers: {
|
||||||
|
accept: 'application/xlsx',
|
||||||
|
},
|
||||||
|
params: query,
|
||||||
|
},
|
||||||
|
filename: 'sales_by_items.xlsx',
|
||||||
|
...args,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve customers balance summary report.
|
* Retrieve customers balance summary report.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,191 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { omit, chain } from 'lodash';
|
|
||||||
import moment from 'moment';
|
|
||||||
|
|
||||||
export const mapBalanceSheetToTableRows = (accounts) => {
|
|
||||||
return accounts.map((account) => {
|
|
||||||
return {
|
|
||||||
...account,
|
|
||||||
children: mapBalanceSheetToTableRows([
|
|
||||||
...(account.children ? account.children : []),
|
|
||||||
...(account.total && account.children && account.children.length > 0
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
name: `Total ${account.name}`,
|
|
||||||
row_types: ['total-row', account.section_type],
|
|
||||||
total: { ...account.total },
|
|
||||||
...(account.total_periods && {
|
|
||||||
total_periods: account.total_periods,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
]),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const profitLossToTableRowsMapper = () => {};
|
|
||||||
|
|
||||||
export const journalToTableRowsMapper = (journal) => {
|
|
||||||
const TYPES = {
|
|
||||||
ENTRY: 'ENTRY',
|
|
||||||
TOTAL_ENTRIES: 'TOTAL_ENTRIES',
|
|
||||||
EMPTY_ROW: 'EMPTY_ROW',
|
|
||||||
};
|
|
||||||
|
|
||||||
const entriesMapper = (transaction) => {
|
|
||||||
return transaction.entries.map((entry, index) => ({
|
|
||||||
...(index === 0
|
|
||||||
? {
|
|
||||||
date: transaction.date,
|
|
||||||
reference_type: transaction.reference_type,
|
|
||||||
reference_id: transaction.reference_id,
|
|
||||||
reference_type_formatted: transaction.reference_type_formatted,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
rowType: TYPES.ENTRY,
|
|
||||||
...entry,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return chain(journal)
|
|
||||||
.map((transaction) => {
|
|
||||||
const entries = entriesMapper(transaction);
|
|
||||||
|
|
||||||
return [
|
|
||||||
...entries,
|
|
||||||
{
|
|
||||||
rowType: TYPES.TOTAL_ENTRIES,
|
|
||||||
currency_code: transaction.currency_code,
|
|
||||||
credit: transaction.credit,
|
|
||||||
debit: transaction.debit,
|
|
||||||
formatted_credit: transaction.formatted_credit,
|
|
||||||
formatted_debit: transaction.formatted_debit,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rowType: TYPES.EMPTY_ROW,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.value();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generalLedgerToTableRows = (accounts) => {
|
|
||||||
return chain(accounts)
|
|
||||||
.map((account) => {
|
|
||||||
return {
|
|
||||||
name: '',
|
|
||||||
code: account.code,
|
|
||||||
rowType: 'ACCOUNT_ROW',
|
|
||||||
date: account.name,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
...account.opening_balance,
|
|
||||||
name: 'Opening balance',
|
|
||||||
rowType: 'OPENING_BALANCE',
|
|
||||||
},
|
|
||||||
...account.transactions.map((transaction) => ({
|
|
||||||
...transaction,
|
|
||||||
name: account.name,
|
|
||||||
code: account.code,
|
|
||||||
date: moment(transaction.date).format('DD MMM YYYY'),
|
|
||||||
})),
|
|
||||||
{
|
|
||||||
...account.closing_balance,
|
|
||||||
name: 'Closing balance',
|
|
||||||
rowType: 'CLOSING_BALANCE',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.value();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ARAgingSummaryTableRowsMapper = (sheet, total) => {
|
|
||||||
const rows = [];
|
|
||||||
|
|
||||||
const mapAging = (agingPeriods) => {
|
|
||||||
return agingPeriods.reduce((acc, aging, index) => {
|
|
||||||
acc[`aging-${index}`] = aging.total.formatted_amount;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
sheet.customers.forEach((customer) => {
|
|
||||||
const agingRow = mapAging(customer.aging);
|
|
||||||
|
|
||||||
rows.push({
|
|
||||||
rowType: 'customer',
|
|
||||||
name: customer.customer_name,
|
|
||||||
...agingRow,
|
|
||||||
current: customer.current.formatted_amount,
|
|
||||||
total: customer.total.formatted_amount,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (rows.length <= 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
...rows,
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
rowType: 'total',
|
|
||||||
current: sheet.total.current.formatted_amount,
|
|
||||||
...mapAging(sheet.total.aging),
|
|
||||||
total: sheet.total.total.formatted_amount,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const APAgingSummaryTableRowsMapper = (sheet, total) => {
|
|
||||||
const rows = [];
|
|
||||||
|
|
||||||
const mapAging = (agingPeriods) => {
|
|
||||||
return agingPeriods.reduce((acc, aging, index) => {
|
|
||||||
acc[`aging-${index}`] = aging.total.formatted_amount;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
sheet.vendors.forEach((vendor) => {
|
|
||||||
const agingRow = mapAging(vendor.aging);
|
|
||||||
|
|
||||||
rows.push({
|
|
||||||
rowType: 'vendor',
|
|
||||||
name: vendor.vendor_name,
|
|
||||||
...agingRow,
|
|
||||||
current: vendor.current.formatted_amount,
|
|
||||||
total: vendor.total.formatted_amount,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (rows.length <= 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
...rows,
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
rowType: 'total',
|
|
||||||
current: sheet.total.current.formatted_amount,
|
|
||||||
...mapAging(sheet.total.aging),
|
|
||||||
total: sheet.total.total.formatted_amount,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mapTrialBalanceSheetToRows = (sheet) => {
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
if (sheet.accounts) {
|
|
||||||
sheet.accounts.forEach((account) => {
|
|
||||||
results.push(account);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (sheet.total) {
|
|
||||||
results.push({
|
|
||||||
rowType: 'total',
|
|
||||||
...sheet.total,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user