mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
Merge branch 'feature/comparisons' into develop
This commit is contained in:
@@ -72,6 +72,7 @@
|
|||||||
"postcss-preset-env": "6.7.0",
|
"postcss-preset-env": "6.7.0",
|
||||||
"postcss-rtl": "^1.7.3",
|
"postcss-rtl": "^1.7.3",
|
||||||
"postcss-safe-parser": "4.0.1",
|
"postcss-safe-parser": "4.0.1",
|
||||||
|
"query-string": "^7.1.1",
|
||||||
"ramda": "^0.27.1",
|
"ramda": "^0.27.1",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-app-polyfill": "^1.0.6",
|
"react-app-polyfill": "^1.0.6",
|
||||||
@@ -93,7 +94,7 @@
|
|||||||
"react-sortablejs": "^2.0.11",
|
"react-sortablejs": "^2.0.11",
|
||||||
"react-split-pane": "^0.1.91",
|
"react-split-pane": "^0.1.91",
|
||||||
"react-table": "^7.6.3",
|
"react-table": "^7.6.3",
|
||||||
"react-table-sticky": "^1.1.2",
|
"react-table-sticky": "^1.1.3",
|
||||||
"react-transition-group": "^4.4.1",
|
"react-transition-group": "^4.4.1",
|
||||||
"react-use": "^13.26.1",
|
"react-use": "^13.26.1",
|
||||||
"react-use-context-menu": "^0.1.4",
|
"react-use-context-menu": "^0.1.4",
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
-->
|
-->
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
|
||||||
|
<% if (process.env.NODE_ENV === 'production') { %>
|
||||||
<!-- Hotjar Tracking Code for https://app.bigcapital.ly/ -->
|
<!-- Hotjar Tracking Code for https://app.bigcapital.ly/ -->
|
||||||
<script>
|
<script>
|
||||||
(function (h, o, t, j, a, r) {
|
(function (h, o, t, j, a, r) {
|
||||||
@@ -35,6 +37,7 @@
|
|||||||
a.appendChild(r);
|
a.appendChild(r);
|
||||||
})(window, document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');
|
})(window, document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');
|
||||||
</script>
|
</script>
|
||||||
|
<% } %>
|
||||||
<!--
|
<!--
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
export * from './TableStyle';
|
export * from './TableStyle';
|
||||||
|
|
||||||
|
export const Align = { Left: 'left', Right: 'right', Center: 'center' };
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ function TableHeaderCell({ column, index }) {
|
|||||||
<div
|
<div
|
||||||
{...column.getHeaderProps({
|
{...column.getHeaderProps({
|
||||||
className: classNames(column.className || '', 'th', {
|
className: classNames(column.className || '', 'th', {
|
||||||
'align-right': column.align === 'right',
|
[`align-${column.align}`]: column.align,
|
||||||
}),
|
}),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
@@ -89,6 +89,7 @@ export default function TableHeader() {
|
|||||||
return (
|
return (
|
||||||
<ScrollSyncPane>
|
<ScrollSyncPane>
|
||||||
<div className="thead">
|
<div className="thead">
|
||||||
|
<div className={'thead-inner'}>
|
||||||
{headerGroups.map((headerGroup, index) => (
|
{headerGroups.map((headerGroup, index) => (
|
||||||
<TableHeaderGroup key={index} headerGroup={headerGroup} />
|
<TableHeaderGroup key={index} headerGroup={headerGroup} />
|
||||||
))}
|
))}
|
||||||
@@ -96,6 +97,7 @@ export default function TableHeader() {
|
|||||||
<MaterialProgressBar />
|
<MaterialProgressBar />
|
||||||
</If>
|
</If>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</ScrollSyncPane>
|
</ScrollSyncPane>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import clsx from 'classnames';
|
||||||
import TableContext from './TableContext';
|
import TableContext from './TableContext';
|
||||||
import { Skeleton } from 'components';
|
import { Skeleton } from 'components';
|
||||||
|
|
||||||
@@ -8,7 +9,13 @@ function TableHeaderCell({ column }) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...column.getHeaderProps({
|
{...column.getHeaderProps({
|
||||||
className: 'th',
|
className: clsx(
|
||||||
|
'th',
|
||||||
|
{
|
||||||
|
[`align-${column.align}`]: column.align,
|
||||||
|
},
|
||||||
|
column.className,
|
||||||
|
),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Skeleton minWidth={skeletonWidthMin} maxWidth={skeletonWidthMax} />
|
<Skeleton minWidth={skeletonWidthMin} maxWidth={skeletonWidthMax} />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import clsx from 'classnames';
|
||||||
import TableContext from './TableContext';
|
import TableContext from './TableContext';
|
||||||
import { Skeleton } from 'components';
|
import { Skeleton } from 'components';
|
||||||
|
|
||||||
@@ -11,7 +12,13 @@ function TableHeaderCell({ column }) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...column.getHeaderProps({
|
{...column.getHeaderProps({
|
||||||
className: 'td',
|
className: clsx(
|
||||||
|
'td',
|
||||||
|
{
|
||||||
|
[`align-${column.align}`]: column.align,
|
||||||
|
},
|
||||||
|
column.className,
|
||||||
|
),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Skeleton minWidth={skeletonWidthMin} maxWidth={skeletonWidthMax} />
|
<Skeleton minWidth={skeletonWidthMin} maxWidth={skeletonWidthMax} />
|
||||||
|
|||||||
23
src/components/FinancialReport/index.js
Normal file
23
src/components/FinancialReport/index.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const FinancialStatementRoot = styled.div``;
|
||||||
|
const FinancialStatementBodyRoot = styled.div``;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {React.JSX}
|
||||||
|
*/
|
||||||
|
export function FinancialReport({ children, className }) {
|
||||||
|
return <FinancialStatementRoot children={children} className={className} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {React.JSX}
|
||||||
|
*/
|
||||||
|
export function FinancialReportBody({ children, className }) {
|
||||||
|
return (
|
||||||
|
<FinancialStatementBodyRoot children={children} className={className} />
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
import React, { useMemo, useCallback } from 'react';
|
|
||||||
import moment from 'moment';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import { FormattedMessage as T } from 'components';
|
|
||||||
import intl from 'react-intl-universal';
|
|
||||||
|
|
||||||
import 'style/pages/FinancialStatements/FinancialSheet.scss';
|
|
||||||
|
|
||||||
import { If, LoadingIndicator, MODIFIER } from 'components';
|
|
||||||
|
|
||||||
export default function FinancialSheet({
|
|
||||||
companyName,
|
|
||||||
sheetType,
|
|
||||||
fromDate,
|
|
||||||
toDate,
|
|
||||||
asDate,
|
|
||||||
children,
|
|
||||||
accountingBasis,
|
|
||||||
name,
|
|
||||||
loading,
|
|
||||||
className,
|
|
||||||
basis,
|
|
||||||
minimal = false,
|
|
||||||
fullWidth = false,
|
|
||||||
currentDate = true,
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const format = 'DD MMMM YYYY';
|
|
||||||
const formattedFromDate = useMemo(() => moment(fromDate).format(format), [
|
|
||||||
fromDate,
|
|
||||||
]);
|
|
||||||
const formattedToDate = useMemo(() => moment(toDate).format(format), [
|
|
||||||
toDate,
|
|
||||||
]);
|
|
||||||
const formattedAsDate = useMemo(() => moment(asDate).format(format), [
|
|
||||||
asDate,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const nameModifer = name ? `financial-sheet--${name}` : '';
|
|
||||||
const methodsLabels = useMemo(
|
|
||||||
() => ({
|
|
||||||
cash: intl.get('cash'),
|
|
||||||
accrual: intl.get('accrual'),
|
|
||||||
}),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
const getBasisLabel = useCallback((b) => methodsLabels[b], [methodsLabels]);
|
|
||||||
const basisLabel = useMemo(() => getBasisLabel(basis), [
|
|
||||||
getBasisLabel,
|
|
||||||
basis,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classnames('financial-sheet', nameModifer, className, {
|
|
||||||
[MODIFIER.FINANCIAL_SHEET_MINIMAL]: minimal,
|
|
||||||
'is-full-width': fullWidth,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{loading ? (
|
|
||||||
<LoadingIndicator loading={loading} spinnerSize={34} />
|
|
||||||
) : (
|
|
||||||
<div className={classnames('financial-sheet__inner')}>
|
|
||||||
<If condition={!!companyName}>
|
|
||||||
<h1 class="financial-sheet__title">{companyName}</h1>
|
|
||||||
</If>
|
|
||||||
|
|
||||||
<If condition={!!sheetType}>
|
|
||||||
<h6 class="financial-sheet__sheet-type">{sheetType}</h6>
|
|
||||||
</If>
|
|
||||||
|
|
||||||
<div class="financial-sheet__date">
|
|
||||||
<If condition={asDate}>
|
|
||||||
<T id={'as'} /> {formattedAsDate}
|
|
||||||
</If>
|
|
||||||
|
|
||||||
<If condition={fromDate && toDate}>
|
|
||||||
<T id={'from'} /> {formattedFromDate} | <T id={'to'} />{' '}
|
|
||||||
{formattedToDate}
|
|
||||||
</If>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="financial-sheet__table">{children}</div>
|
|
||||||
<div class="financial-sheet__accounting-basis">{accountingBasis}</div>
|
|
||||||
|
|
||||||
<div class="financial-sheet__footer">
|
|
||||||
<If condition={basisLabel}>
|
|
||||||
<span class="financial-sheet__basis">
|
|
||||||
<T id={'accounting_basis'} /> {basisLabel}
|
|
||||||
</span>
|
|
||||||
</If>
|
|
||||||
|
|
||||||
<If condition={currentDate}>
|
|
||||||
<span class="financial-sheet__current-date">
|
|
||||||
{moment().format('YYYY MMM DD HH:MM')}
|
|
||||||
</span>
|
|
||||||
</If>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
95
src/components/FinancialSheet/FinancialSheet.js
Normal file
95
src/components/FinancialSheet/FinancialSheet.js
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import React, { useMemo, useCallback } from 'react';
|
||||||
|
import moment from 'moment';
|
||||||
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
|
import { If, FormattedMessage as T } from 'components';
|
||||||
|
import {
|
||||||
|
FinancialSheetRoot,
|
||||||
|
FinancialSheetFooterCurrentTime,
|
||||||
|
FinancialSheetFooterBasis,
|
||||||
|
FinancialSheetFooter,
|
||||||
|
FinancialSheetAccountingBasis,
|
||||||
|
FinancialSheetTable,
|
||||||
|
FinancialSheetDate,
|
||||||
|
FinancialSheetType,
|
||||||
|
FinancialSheetTitle,
|
||||||
|
} from './StyledFinancialSheet';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Financial sheet.
|
||||||
|
* @returns {React.JSX}
|
||||||
|
*/
|
||||||
|
export function FinancialSheet({
|
||||||
|
companyName,
|
||||||
|
sheetType,
|
||||||
|
fromDate,
|
||||||
|
toDate,
|
||||||
|
asDate,
|
||||||
|
children,
|
||||||
|
accountingBasis,
|
||||||
|
basis,
|
||||||
|
minimal = false,
|
||||||
|
fullWidth = false,
|
||||||
|
currentDate = true,
|
||||||
|
}) {
|
||||||
|
const format = 'DD MMMM YYYY';
|
||||||
|
const formattedFromDate = useMemo(
|
||||||
|
() => moment(fromDate).format(format),
|
||||||
|
[fromDate],
|
||||||
|
);
|
||||||
|
const formattedToDate = useMemo(
|
||||||
|
() => moment(toDate).format(format),
|
||||||
|
[toDate],
|
||||||
|
);
|
||||||
|
const formattedAsDate = useMemo(
|
||||||
|
() => moment(asDate).format(format),
|
||||||
|
[asDate],
|
||||||
|
);
|
||||||
|
const methodsLabels = useMemo(
|
||||||
|
() => ({
|
||||||
|
cash: intl.get('cash'),
|
||||||
|
accrual: intl.get('accrual'),
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const getBasisLabel = useCallback((b) => methodsLabels[b], [methodsLabels]);
|
||||||
|
const basisLabel = useMemo(
|
||||||
|
() => getBasisLabel(basis),
|
||||||
|
[getBasisLabel, basis],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FinancialSheetRoot minimal={minimal} fullWidth={fullWidth}>
|
||||||
|
{companyName && <FinancialSheetTitle>{companyName}</FinancialSheetTitle>}
|
||||||
|
{sheetType && <FinancialSheetType>{sheetType}</FinancialSheetType>}
|
||||||
|
|
||||||
|
<FinancialSheetDate>
|
||||||
|
<If condition={asDate}>
|
||||||
|
<T id={'as'} /> {formattedAsDate}
|
||||||
|
</If>
|
||||||
|
<If condition={fromDate && toDate}>
|
||||||
|
<T id={'from'} /> {formattedFromDate} | <T id={'to'} />{' '}
|
||||||
|
{formattedToDate}
|
||||||
|
</If>
|
||||||
|
</FinancialSheetDate>
|
||||||
|
|
||||||
|
<FinancialSheetTable>{children}</FinancialSheetTable>
|
||||||
|
<FinancialSheetAccountingBasis>
|
||||||
|
{accountingBasis}
|
||||||
|
</FinancialSheetAccountingBasis>
|
||||||
|
|
||||||
|
<FinancialSheetFooter>
|
||||||
|
{basisLabel && (
|
||||||
|
<FinancialSheetFooterBasis>
|
||||||
|
<T id={'accounting_basis'} /> {basisLabel}
|
||||||
|
</FinancialSheetFooterBasis>
|
||||||
|
)}
|
||||||
|
{currentDate && (
|
||||||
|
<FinancialSheetFooterCurrentTime>
|
||||||
|
{moment().format('YYYY MMM DD HH:MM')}
|
||||||
|
</FinancialSheetFooterCurrentTime>
|
||||||
|
)}
|
||||||
|
</FinancialSheetFooter>
|
||||||
|
</FinancialSheetRoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
84
src/components/FinancialSheet/FinancialSheetSkeleton.js
Normal file
84
src/components/FinancialSheet/FinancialSheetSkeleton.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { Align } from 'common';
|
||||||
|
import { SkeletonText, DataTable } from 'components';
|
||||||
|
|
||||||
|
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||||
|
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
|
||||||
|
import { TableStyle } from 'common';
|
||||||
|
import {
|
||||||
|
FinancialSheetRoot,
|
||||||
|
FinancialSheetTitle,
|
||||||
|
FinancialSheetType,
|
||||||
|
FinancialSheetDate,
|
||||||
|
FinancialSheetTable,
|
||||||
|
} from './StyledFinancialSheet';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Financial sheet paper skeleton.
|
||||||
|
* @returns {React.JSX}
|
||||||
|
*/
|
||||||
|
export function FinancialSheetSkeleton({
|
||||||
|
minimal,
|
||||||
|
fullWidth,
|
||||||
|
titleCharsLength,
|
||||||
|
typeCharsLength,
|
||||||
|
dateCharsLength,
|
||||||
|
skeletonTableColumns,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<FinancialSheetRoot minimal={minimal} fullWidth={fullWidth}>
|
||||||
|
<FinancialSheetTitle>
|
||||||
|
<SkeletonText charsLength={titleCharsLength} />
|
||||||
|
</FinancialSheetTitle>
|
||||||
|
|
||||||
|
<FinancialSheetType>
|
||||||
|
<SkeletonText charsLength={typeCharsLength} />
|
||||||
|
</FinancialSheetType>
|
||||||
|
|
||||||
|
<FinancialSheetDate>
|
||||||
|
<SkeletonText charsLength={dateCharsLength} />
|
||||||
|
</FinancialSheetDate>
|
||||||
|
|
||||||
|
<FinancialSheetTable>
|
||||||
|
<FinancialSkeletonTable
|
||||||
|
columns={skeletonTableColumns}
|
||||||
|
data={[]}
|
||||||
|
noInitialFetch={true}
|
||||||
|
expandable={true}
|
||||||
|
styleName={TableStyle.Constrant}
|
||||||
|
TableLoadingRenderer={TableSkeletonRows}
|
||||||
|
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||||
|
headerLoading={true}
|
||||||
|
loading={true}
|
||||||
|
/>
|
||||||
|
</FinancialSheetTable>
|
||||||
|
</FinancialSheetRoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FinancialSheetSkeleton.defaultProps = {
|
||||||
|
titleCharsLength: 20,
|
||||||
|
typeCharsLength: 40,
|
||||||
|
dateCharsLength: 20,
|
||||||
|
skeletonTableColumns: [
|
||||||
|
{
|
||||||
|
id: 'skeleton-1',
|
||||||
|
className: 'skeleton-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'skeleton-2',
|
||||||
|
className: 'skeleton-2',
|
||||||
|
align: Align.Right,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const FinancialSkeletonTable = styled(DataTable)`
|
||||||
|
.table .th .skeleton,
|
||||||
|
.table .td .skeleton {
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
82
src/components/FinancialSheet/StyledFinancialSheet.js
Normal file
82
src/components/FinancialSheet/StyledFinancialSheet.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const FinancialSheetRoot = styled.div`
|
||||||
|
border: 2px solid #f0f0f0;
|
||||||
|
border-radius: 10px;
|
||||||
|
min-width: 640px;
|
||||||
|
width: auto;
|
||||||
|
padding: 30px 18px;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 35px auto;
|
||||||
|
min-height: 400px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.fullWidth &&
|
||||||
|
`
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 25px;`}
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.minimal &&
|
||||||
|
`
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
${FinancialSheetTitle} {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
${FinancialSheetTitle} + ${FinancialSheetDate} {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
${FinancialSheetDate} {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FinancialSheetTitle = styled.h1`
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #464646;
|
||||||
|
text-align: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FinancialSheetType = styled.h6`
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 6px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FinancialSheetDate = styled.div`
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 6px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FinancialSheetFooter = styled.div`
|
||||||
|
color: #888;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 18px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
> span + span {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const FinancialSheetTable = styled.div`
|
||||||
|
margin-top: 24px;
|
||||||
|
`;
|
||||||
|
export const FinancialSheetFooterBasis = styled.span``;
|
||||||
|
export const FinancialSheetFooterCurrentTime = styled.span``;
|
||||||
|
|
||||||
|
export const FinancialSheetAccountingBasis = styled.div``;
|
||||||
2
src/components/FinancialSheet/index.js
Normal file
2
src/components/FinancialSheet/index.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './FinancialSheet';
|
||||||
|
export * from './FinancialSheetSkeleton';
|
||||||
@@ -1,15 +1,24 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import className from 'classnames';
|
import styled from 'styled-components';
|
||||||
import 'style/containers/FinancialStatements/FinancialSheet.scss';
|
|
||||||
|
|
||||||
export default function FinancialStatements({ name, children }) {
|
const FinancialStatementRoot = styled.div``;
|
||||||
|
const FinancialStatementBodyRoot = styled.div``;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function FinancialStatement({ children, className }) {
|
||||||
|
return <FinancialStatementRoot children={children} className={className} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {React.JSX}
|
||||||
|
*/
|
||||||
|
export function FinancialStatementBody({ children, className }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<FinancialStatementBodyRoot children={children} className={className} />
|
||||||
className={className('financial-statement', {
|
|
||||||
[`financial-statement--${name}`]: name,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,36 @@ import { randomNumber } from 'utils';
|
|||||||
/**
|
/**
|
||||||
* Skeleton component.
|
* Skeleton component.
|
||||||
*/
|
*/
|
||||||
export default function Skeleton({
|
export function Skeleton({
|
||||||
Tag = 'span',
|
Tag = 'span',
|
||||||
minWidth = 40,
|
minWidth = 40,
|
||||||
maxWidth = 100,
|
maxWidth = 100,
|
||||||
|
children,
|
||||||
}) {
|
}) {
|
||||||
const randomWidth = useMemo(() => randomNumber(minWidth, maxWidth), [
|
const randomWidth = useMemo(
|
||||||
minWidth,
|
() => randomNumber(minWidth, maxWidth),
|
||||||
maxWidth,
|
[minWidth, maxWidth],
|
||||||
]);
|
);
|
||||||
return <Tag className={'skeleton'} style={{ width: `${randomWidth}%` }} />;
|
return (
|
||||||
|
<Tag
|
||||||
|
className={'skeleton'}
|
||||||
|
style={{ width: `${randomWidth}%` }}
|
||||||
|
children={children}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SkeletonText({
|
||||||
|
Tag = 'span',
|
||||||
|
charsLength,
|
||||||
|
minChars = 40,
|
||||||
|
maxChars = 100,
|
||||||
|
}) {
|
||||||
|
const computedCharLength = useMemo(
|
||||||
|
() => (charsLength ? charsLength : randomNumber(minChars, maxChars)),
|
||||||
|
[charsLength, minChars, maxChars],
|
||||||
|
);
|
||||||
|
const randamText = 'X'.repeat(computedCharLength);
|
||||||
|
|
||||||
|
return <Tag className={'skeleton'}>{randamText}</Tag>;
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/components/SkeletonText.js
Normal file
0
src/components/SkeletonText.js
Normal file
@@ -5,9 +5,6 @@ import Choose from './Utils/Choose';
|
|||||||
import For from './Utils/For';
|
import For from './Utils/For';
|
||||||
import { FormattedMessage, FormattedHTMLMessage } from './FormattedMessage';
|
import { FormattedMessage, FormattedHTMLMessage } from './FormattedMessage';
|
||||||
import ListSelect from './ListSelect';
|
import ListSelect from './ListSelect';
|
||||||
import FinancialStatement from './FinancialStatement';
|
|
||||||
// import DynamicFilterValueField from './DynamicFilter/DynamicFilterValueField';
|
|
||||||
// import DynamicFilterCompatatorField from './DynamicFilter/DynamicFilterCompatatorField';
|
|
||||||
import ErrorMessage from './ErrorMessage';
|
import ErrorMessage from './ErrorMessage';
|
||||||
import MODIFIER from './modifiers';
|
import MODIFIER from './modifiers';
|
||||||
import FieldHint from './FieldHint';
|
import FieldHint from './FieldHint';
|
||||||
@@ -41,7 +38,6 @@ import InputPrependText from './Forms/InputPrependText';
|
|||||||
import PageFormBigNumber from './PageFormBigNumber';
|
import PageFormBigNumber from './PageFormBigNumber';
|
||||||
import AccountsMultiSelect from './AccountsMultiSelect';
|
import AccountsMultiSelect from './AccountsMultiSelect';
|
||||||
import ContactsMultiSelect from './ContactsMultiSelect';
|
import ContactsMultiSelect from './ContactsMultiSelect';
|
||||||
import Skeleton from './Skeleton';
|
|
||||||
import ContextMenu from './ContextMenu';
|
import ContextMenu from './ContextMenu';
|
||||||
import TableFastCell from './Datatable/TableFastCell';
|
import TableFastCell from './Datatable/TableFastCell';
|
||||||
import DashboardContentTable from './Dashboard/DashboardContentTable';
|
import DashboardContentTable from './Dashboard/DashboardContentTable';
|
||||||
@@ -95,6 +91,10 @@ export * from './Card';
|
|||||||
export * from './Customers'
|
export * from './Customers'
|
||||||
export * from './Vendors'
|
export * from './Vendors'
|
||||||
export * from './Table';
|
export * from './Table';
|
||||||
|
export * from './Skeleton';
|
||||||
|
export * from './FinancialStatement';
|
||||||
|
export * from './FinancialReport';
|
||||||
|
export * from './FinancialSheet';
|
||||||
|
|
||||||
const Hint = FieldHint;
|
const Hint = FieldHint;
|
||||||
|
|
||||||
@@ -110,7 +110,6 @@ export {
|
|||||||
T,
|
T,
|
||||||
Money,
|
Money,
|
||||||
ListSelect,
|
ListSelect,
|
||||||
FinancialStatement,
|
|
||||||
// DynamicFilterValueField,
|
// DynamicFilterValueField,
|
||||||
// DynamicFilterCompatatorField,
|
// DynamicFilterCompatatorField,
|
||||||
MODIFIER,
|
MODIFIER,
|
||||||
@@ -148,7 +147,6 @@ export {
|
|||||||
DataTableEditable,
|
DataTableEditable,
|
||||||
ContactsMultiSelect,
|
ContactsMultiSelect,
|
||||||
TableFastCell,
|
TableFastCell,
|
||||||
Skeleton,
|
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
DashboardContentTable,
|
DashboardContentTable,
|
||||||
DashboardPageContent,
|
DashboardPageContent,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage as T } from 'components';
|
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { DataTable } from 'components';
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
|
|
||||||
import { useAPAgingSummaryContext } from './APAgingSummaryProvider';
|
import { useAPAgingSummaryContext } from './APAgingSummaryProvider';
|
||||||
import { useAPAgingSummaryColumns } from './components';
|
import { useAPAgingSummaryColumns } from './components';
|
||||||
@@ -14,8 +13,6 @@ export default function APAgingSummaryTable({
|
|||||||
//#ownProps
|
//#ownProps
|
||||||
organizationName,
|
organizationName,
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
|
|
||||||
// AP aging summary report content.
|
// AP aging summary report content.
|
||||||
const {
|
const {
|
||||||
APAgingSummary: { tableRows },
|
APAgingSummary: { tableRows },
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import DataTable from 'components/DataTable';
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
|
|
||||||
import { useARAgingSummaryContext } from './ARAgingSummaryProvider';
|
import { useARAgingSummaryContext } from './ARAgingSummaryProvider';
|
||||||
import { useARAgingSummaryColumns } from './components';
|
import { useARAgingSummaryColumns } from './components';
|
||||||
|
|||||||
@@ -1,57 +1,47 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import 'style/pages/FinancialStatements/BalanceSheet.scss';
|
|
||||||
|
|
||||||
import { BalanceSheetAlerts, BalanceSheetLoadingBar } from './components';
|
import { BalanceSheetAlerts, BalanceSheetLoadingBar } from './components';
|
||||||
import { FinancialStatement } from 'components';
|
import { FinancialStatement } from 'components';
|
||||||
|
|
||||||
import BalanceSheetHeader from './BalanceSheetHeader';
|
import BalanceSheetHeader from './BalanceSheetHeader';
|
||||||
import BalanceSheetTable from './BalanceSheetTable';
|
|
||||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||||
import BalanceSheetActionsBar from './BalanceSheetActionsBar';
|
import BalanceSheetActionsBar from './BalanceSheetActionsBar';
|
||||||
import { BalanceSheetProvider } from './BalanceSheetProvider';
|
import { BalanceSheetProvider } from './BalanceSheetProvider';
|
||||||
|
import { BalanceSheetBody } from './BalanceSheetBody';
|
||||||
|
|
||||||
import withBalanceSheetActions from './withBalanceSheetActions';
|
import withBalanceSheetActions from './withBalanceSheetActions';
|
||||||
import withCurrentOrganization from '../../../containers/Organization/withCurrentOrganization';
|
|
||||||
|
|
||||||
|
import { useBalanceSheetQuery } from './utils';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Balance sheet.
|
* Balance sheet.
|
||||||
|
* @returns {React.JSX}
|
||||||
*/
|
*/
|
||||||
function BalanceSheet({
|
function BalanceSheet({
|
||||||
// #withCurrentOrganization
|
|
||||||
organizationName,
|
|
||||||
|
|
||||||
// #withBalanceSheetActions
|
// #withBalanceSheetActions
|
||||||
toggleBalanceSheetFilterDrawer,
|
toggleBalanceSheetFilterDrawer,
|
||||||
}) {
|
}) {
|
||||||
const [filter, setFilter] = useState({
|
// Balance sheet query.
|
||||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
const { query, setLocationQuery } = useBalanceSheetQuery();
|
||||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
|
||||||
basis: 'cash',
|
|
||||||
displayColumnsType: 'total',
|
|
||||||
filterByOption: 'without-zero-balance',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle re-fetch balance sheet after filter change.
|
// Handle re-fetch balance sheet after filter change.
|
||||||
const handleFilterSubmit = (filter) => {
|
const handleFilterSubmit = (filter) => {
|
||||||
const _filter = {
|
const newFilter = {
|
||||||
...filter,
|
...filter,
|
||||||
fromDate: moment(filter.fromDate).format('YYYY-MM-DD'),
|
fromDate: moment(filter.fromDate).format('YYYY-MM-DD'),
|
||||||
toDate: moment(filter.toDate).format('YYYY-MM-DD'),
|
toDate: moment(filter.toDate).format('YYYY-MM-DD'),
|
||||||
};
|
};
|
||||||
setFilter({ ..._filter });
|
setLocationQuery({ ...newFilter });
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle number format submit.
|
// Handle number format submit.
|
||||||
const handleNumberFormatSubmit = (values) => {
|
const handleNumberFormatSubmit = (values) => {
|
||||||
setFilter({
|
setLocationQuery({
|
||||||
...filter,
|
...query,
|
||||||
numberFormat: values,
|
numberFormat: values,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hides the balance sheet filter drawer once the page unmount.
|
// Hides the balance sheet filter drawer once the page unmount.
|
||||||
useEffect(
|
useEffect(
|
||||||
() => () => {
|
() => () => {
|
||||||
@@ -61,9 +51,9 @@ function BalanceSheet({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BalanceSheetProvider filter={filter}>
|
<BalanceSheetProvider filter={query}>
|
||||||
<BalanceSheetActionsBar
|
<BalanceSheetActionsBar
|
||||||
numberFormat={filter.numberFormat}
|
numberFormat={query.numberFormat}
|
||||||
onNumberFormatSubmit={handleNumberFormatSubmit}
|
onNumberFormatSubmit={handleNumberFormatSubmit}
|
||||||
/>
|
/>
|
||||||
<BalanceSheetLoadingBar />
|
<BalanceSheetLoadingBar />
|
||||||
@@ -72,22 +62,14 @@ function BalanceSheet({
|
|||||||
<DashboardPageContent>
|
<DashboardPageContent>
|
||||||
<FinancialStatement>
|
<FinancialStatement>
|
||||||
<BalanceSheetHeader
|
<BalanceSheetHeader
|
||||||
pageFilter={filter}
|
pageFilter={query}
|
||||||
onSubmitFilter={handleFilterSubmit}
|
onSubmitFilter={handleFilterSubmit}
|
||||||
/>
|
/>
|
||||||
<div class="financial-statement__body">
|
<BalanceSheetBody />
|
||||||
<BalanceSheetTable companyName={organizationName} />
|
|
||||||
</div>
|
|
||||||
</FinancialStatement>
|
</FinancialStatement>
|
||||||
</DashboardPageContent>
|
</DashboardPageContent>
|
||||||
|
|
||||||
</BalanceSheetProvider>
|
</BalanceSheetProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(withBalanceSheetActions)(BalanceSheet);
|
||||||
withCurrentOrganization(({ organization }) => ({
|
|
||||||
organizationName: organization.name,
|
|
||||||
})),
|
|
||||||
withBalanceSheetActions,
|
|
||||||
)(BalanceSheet);
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import BalanceSheetTable from './BalanceSheetTable';
|
||||||
|
import { FinancialReportBody } from '../FinancialReportPage';
|
||||||
|
|
||||||
|
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||||
|
import { useBalanceSheetContext } from './BalanceSheetProvider';
|
||||||
|
import { FinancialSheetSkeleton } from '../../../components/FinancialSheet';
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Balance sheet body JSX.
|
||||||
|
* @returns {React.JSX}
|
||||||
|
*/
|
||||||
|
function BalanceSheetBodyJSX({
|
||||||
|
// #withCurrentOrganization
|
||||||
|
organizationName,
|
||||||
|
}) {
|
||||||
|
const { isLoading } = useBalanceSheetContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FinancialReportBody>
|
||||||
|
{isLoading ? (
|
||||||
|
<FinancialSheetSkeleton />
|
||||||
|
) : (
|
||||||
|
<BalanceSheetTable companyName={organizationName} />
|
||||||
|
)}
|
||||||
|
</FinancialReportBody>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BalanceSheetBody = compose(
|
||||||
|
withCurrentOrganization(({ organization }) => ({
|
||||||
|
organizationName: organization.name,
|
||||||
|
})),
|
||||||
|
)(BalanceSheetBodyJSX);
|
||||||
@@ -8,12 +8,13 @@ import withBalanceSheet from './withBalanceSheet';
|
|||||||
import withBalanceSheetActions from './withBalanceSheetActions';
|
import withBalanceSheetActions from './withBalanceSheetActions';
|
||||||
|
|
||||||
import BalanceSheetHeaderGeneralPanal from './BalanceSheetHeaderGeneralPanal';
|
import BalanceSheetHeaderGeneralPanal from './BalanceSheetHeaderGeneralPanal';
|
||||||
|
import BalanceSheetHeaderComparisonPanal from './BalanceSheetHeaderComparisonPanal';
|
||||||
import FinancialStatementHeader from '../../FinancialStatements/FinancialStatementHeader';
|
import FinancialStatementHeader from '../../FinancialStatements/FinancialStatementHeader';
|
||||||
|
|
||||||
import { compose, transformToForm } from 'utils';
|
import { compose, transformToForm } from 'utils';
|
||||||
import {
|
import {
|
||||||
getBalanceSheetHeaderDefaultValues,
|
|
||||||
getBalanceSheetHeaderValidationSchema,
|
getBalanceSheetHeaderValidationSchema,
|
||||||
|
getDefaultBalanceSheetQuery,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +31,7 @@ function BalanceSheetHeader({
|
|||||||
// #withBalanceSheetActions
|
// #withBalanceSheetActions
|
||||||
toggleBalanceSheetFilterDrawer: toggleFilterDrawer,
|
toggleBalanceSheetFilterDrawer: toggleFilterDrawer,
|
||||||
}) {
|
}) {
|
||||||
const defaultValues = getBalanceSheetHeaderDefaultValues();
|
const defaultValues = getDefaultBalanceSheetQuery();
|
||||||
|
|
||||||
// Filter form initial values.
|
// Filter form initial values.
|
||||||
const initialValues = transformToForm(
|
const initialValues = transformToForm(
|
||||||
@@ -42,7 +43,6 @@ function BalanceSheetHeader({
|
|||||||
},
|
},
|
||||||
defaultValues,
|
defaultValues,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Validation schema.
|
// Validation schema.
|
||||||
const validationSchema = getBalanceSheetHeaderValidationSchema();
|
const validationSchema = getBalanceSheetHeaderValidationSchema();
|
||||||
|
|
||||||
@@ -80,6 +80,11 @@ function BalanceSheetHeader({
|
|||||||
title={<T id={'general'} />}
|
title={<T id={'general'} />}
|
||||||
panel={<BalanceSheetHeaderGeneralPanal />}
|
panel={<BalanceSheetHeaderGeneralPanal />}
|
||||||
/>
|
/>
|
||||||
|
<Tab
|
||||||
|
id="comparison"
|
||||||
|
title={<T id={'balance_sheet.comparisons'} />}
|
||||||
|
panel={<BalanceSheetHeaderComparisonPanal />}
|
||||||
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
<div class="financial-header-drawer__footer">
|
<div class="financial-header-drawer__footer">
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { FastField, Field } from 'formik';
|
||||||
|
import { FormGroup, Checkbox } from '@blueprintjs/core';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { Row, Col, FieldHint, FormattedMessage as T } from 'components';
|
||||||
|
import {
|
||||||
|
handlePreviousYearCheckBoxChange,
|
||||||
|
handlePreviousYearChangeCheckboxChange,
|
||||||
|
handlePreviousPeriodCheckBoxChange,
|
||||||
|
handlePreivousPeriodPercentageCheckboxChange,
|
||||||
|
handlePreviousYearPercentageCheckboxChange,
|
||||||
|
handlePreviousPeriodChangeCheckboxChange,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Balance sheet header - Comparison panal.
|
||||||
|
*/
|
||||||
|
export default function BalanceSheetHeaderComparisonPanal() {
|
||||||
|
return (
|
||||||
|
<BalanceSheetComparisonWrap>
|
||||||
|
{/**----------- Previous Year -----------*/}
|
||||||
|
<Field name={'previousYear'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
label={<T id={'balance_sheet.previous_year'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousYearCheckBoxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
<Row>
|
||||||
|
<Col xs={3}>
|
||||||
|
<Field name={'previousYearAmountChange'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'balance_sheet.total_change'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousYearChangeCheckboxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
</Col>
|
||||||
|
<Col xs={3}>
|
||||||
|
<FastField name={'previousYearPercentageChange'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
label={<T id={'balance_sheet.change'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousYearPercentageCheckboxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{/*------------ Previous Period -----------*/}
|
||||||
|
<FastField name={'previousPeriod'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'balance_sheet.previous_period'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousPeriodCheckBoxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
<Row>
|
||||||
|
<Col xs={3}>
|
||||||
|
<FastField name={'previousPeriodAmountChange'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'balance_sheet.total_change'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousPeriodChangeCheckboxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
<Col xs={3}>
|
||||||
|
<FastField name={'previousPeriodPercentageChange'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
label={<T id={'balance_sheet.change'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreivousPeriodPercentageCheckboxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
{/**----------- % of Column -----------*/}
|
||||||
|
<FastField name={'percentageOfColumn'} type={'checkbox'}>
|
||||||
|
{({ field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'balance_sheet.percentage_of_column'} />}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
|
||||||
|
{/**----------- % of Row -----------*/}
|
||||||
|
<FastField name={'percentageOfRow'} type={'checkbox'}>
|
||||||
|
{({ field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'balance_sheet.percentage_of_row'} />}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</BalanceSheetComparisonWrap>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const BalanceSheetComparisonWrap = styled.div`
|
||||||
|
.row {
|
||||||
|
margin-left: 0.15rem;
|
||||||
|
.col {
|
||||||
|
min-width: 150px !important;
|
||||||
|
max-width: 190px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bp3-form-group {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
|
|
||||||
import FinancialReportPage from '../FinancialReportPage';
|
import FinancialReportPage from '../FinancialReportPage';
|
||||||
import { useBalanceSheet } from 'hooks/query';
|
import { useBalanceSheet } from 'hooks/query';
|
||||||
import { transformFilterFormToQuery } from '../common';
|
import { transformFilterFormToQuery } from '../common';
|
||||||
@@ -10,7 +11,6 @@ function BalanceSheetProvider({ filter, ...props }) {
|
|||||||
const query = React.useMemo(() => transformFilterFormToQuery(filter), [
|
const query = React.useMemo(() => transformFilterFormToQuery(filter), [
|
||||||
filter,
|
filter,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Fetches the balance sheet report.
|
// Fetches the balance sheet report.
|
||||||
const {
|
const {
|
||||||
data: balanceSheet,
|
data: balanceSheet,
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import React, { useMemo, useCallback } from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import DataTable from 'components/DataTable';
|
|
||||||
import { CellTextSpan } from 'components/Datatable/Cells';
|
|
||||||
import { useBalanceSheetContext } from './BalanceSheetProvider';
|
import { useBalanceSheetContext } from './BalanceSheetProvider';
|
||||||
|
|
||||||
import { defaultExpanderReducer, getColumnWidth } from 'utils';
|
import { defaultExpanderReducer, tableRowTypesToClassnames } from 'utils';
|
||||||
|
import { TableStyle } from 'common';
|
||||||
|
import { useBalanceSheetColumns } from './components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Balance sheet table.
|
* Balance sheet table.
|
||||||
@@ -16,91 +17,74 @@ export default function BalanceSheetTable({
|
|||||||
// #ownProps
|
// #ownProps
|
||||||
companyName,
|
companyName,
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
|
|
||||||
// Balance sheet context.
|
// Balance sheet context.
|
||||||
const {
|
const {
|
||||||
balanceSheet: { tableRows, columns, query },
|
balanceSheet: { table, query },
|
||||||
isLoading,
|
|
||||||
} = useBalanceSheetContext();
|
} = useBalanceSheetContext();
|
||||||
|
|
||||||
const tableColumns = useMemo(
|
// Retrieve the database columns.
|
||||||
() => [
|
const tableColumns = useBalanceSheetColumns();
|
||||||
{
|
|
||||||
Header: intl.get('account_name'),
|
// Retrieve default expanded rows of balance sheet.
|
||||||
accessor: (row) => (row.code ? `${row.name} - ${row.code}` : row.name),
|
const expandedRows = React.useMemo(
|
||||||
className: 'account_name',
|
() => defaultExpanderReducer(table?.rows || [], 3),
|
||||||
textOverview: true,
|
[table],
|
||||||
width: 240,
|
|
||||||
},
|
|
||||||
...(query.display_columns_type === 'total'
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
Header: intl.get('total'),
|
|
||||||
accessor: 'total.formatted_amount',
|
|
||||||
Cell: CellTextSpan,
|
|
||||||
className: 'total',
|
|
||||||
width: 140,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(query.display_columns_type === 'date_periods'
|
|
||||||
? columns.map((column, index) => ({
|
|
||||||
id: `date_period_${index}`,
|
|
||||||
Header: column,
|
|
||||||
Cell: CellTextSpan,
|
|
||||||
accessor: `total_periods[${index}].formatted_amount`,
|
|
||||||
className: classNames('total-period', `total-periods-${index}`),
|
|
||||||
width: getColumnWidth(
|
|
||||||
tableRows,
|
|
||||||
`total_periods.${index}.formatted_amount`,
|
|
||||||
{ minWidth: 100 },
|
|
||||||
),
|
|
||||||
}))
|
|
||||||
: []),
|
|
||||||
],
|
|
||||||
[query, columns, tableRows],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Calculates the default expanded rows of balance sheet table.
|
|
||||||
const expandedRows = useMemo(() => defaultExpanderReducer(tableRows, 4), [tableRows]);
|
|
||||||
|
|
||||||
const rowClassNames = useCallback((row) => {
|
|
||||||
const { original } = row;
|
|
||||||
const rowTypes = Array.isArray(original.row_types)
|
|
||||||
? original.row_types
|
|
||||||
: [original.row_types];
|
|
||||||
|
|
||||||
return {
|
|
||||||
...rowTypes.reduce((acc, rowType) => {
|
|
||||||
acc[`row_type--${rowType}`] = rowType;
|
|
||||||
return acc;
|
|
||||||
}, {}),
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FinancialSheet
|
<FinancialSheet
|
||||||
name="balance-sheet"
|
name="balance-sheet"
|
||||||
companyName={companyName}
|
companyName={companyName}
|
||||||
sheetType={intl.get('balance_sheet')}
|
sheetType={intl.get('balance_sheet')}
|
||||||
fromDate={query.from_date}
|
asDate={query.to_date}
|
||||||
toDate={query.to_date}
|
|
||||||
basis={query.basis}
|
basis={query.basis}
|
||||||
loading={isLoading}
|
|
||||||
>
|
>
|
||||||
<DataTable
|
<BalanceSheetDataTable
|
||||||
className="bigcapital-datatable--financial-report"
|
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
data={tableRows}
|
data={table.rows}
|
||||||
rowClassNames={rowClassNames}
|
rowClassNames={tableRowTypesToClassnames}
|
||||||
noInitialFetch={true}
|
noInitialFetch={true}
|
||||||
expandable={true}
|
expandable={true}
|
||||||
expanded={expandedRows}
|
expanded={expandedRows}
|
||||||
expandToggleColumn={1}
|
expandToggleColumn={1}
|
||||||
expandColumnSpace={0.8}
|
expandColumnSpace={0.8}
|
||||||
// sticky={true}
|
headerLoading={true}
|
||||||
|
sticky={true}
|
||||||
|
styleName={TableStyle.Constrant}
|
||||||
/>
|
/>
|
||||||
</FinancialSheet>
|
</FinancialSheet>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BalanceSheetDataTable = styled(DataTable)`
|
||||||
|
.table {
|
||||||
|
.tbody .tr {
|
||||||
|
.td {
|
||||||
|
border-bottom: 0;
|
||||||
|
padding-top: 0.32rem;
|
||||||
|
padding-bottom: 0.32rem;
|
||||||
|
}
|
||||||
|
&.is-expanded {
|
||||||
|
.td:not(.name) .cell-inner {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.row_type--TOTAL {
|
||||||
|
.td {
|
||||||
|
font-weight: 500;
|
||||||
|
border-top: 1px solid #bbb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-of-type .td {
|
||||||
|
border-bottom: 1px solid #bbb;
|
||||||
|
}
|
||||||
|
&.row_type--TOTAL.row-id--ASSETS,
|
||||||
|
&.row_type--TOTAL.row-id--LIABILITY_EQUITY {
|
||||||
|
.td{
|
||||||
|
border-bottom: 3px double #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Button } from '@blueprintjs/core';
|
import { Button } from '@blueprintjs/core';
|
||||||
import { Icon, If } from 'components';
|
|
||||||
|
import { FormattedMessage as T, Icon, If } from 'components';
|
||||||
|
|
||||||
import { useBalanceSheetContext } from './BalanceSheetProvider';
|
import { useBalanceSheetContext } from './BalanceSheetProvider';
|
||||||
import { FormattedMessage as T } from 'components';
|
|
||||||
import FinancialLoadingBar from '../FinancialLoadingBar';
|
import FinancialLoadingBar from '../FinancialLoadingBar';
|
||||||
|
import { FinancialComputeAlert } from '../FinancialReportPage';
|
||||||
|
import { dynamicColumns } from './dynamicColumns';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Balance sheet alerts.
|
* Balance sheet alerts.
|
||||||
@@ -17,19 +20,18 @@ export function BalanceSheetAlerts() {
|
|||||||
refetchBalanceSheet();
|
refetchBalanceSheet();
|
||||||
};
|
};
|
||||||
// Can't display any error if the report is loading.
|
// Can't display any error if the report is loading.
|
||||||
if (isLoading) {
|
if (isLoading) return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<If condition={balanceSheet.meta.is_cost_compute_running}>
|
<If condition={balanceSheet.meta.is_cost_compute_running}>
|
||||||
<div class="alert-compute-running">
|
<FinancialComputeAlert>
|
||||||
<Icon icon="info-block" iconSize={12} />{' '}
|
<Icon icon="info-block" iconSize={12} />{' '}
|
||||||
<T id={'just_a_moment_we_re_calculating_your_cost_transactions'} />
|
<T id={'just_a_moment_we_re_calculating_your_cost_transactions'} />
|
||||||
|
|
||||||
<Button onClick={handleRecalcReport} minimal={true} small={true}>
|
<Button onClick={handleRecalcReport} minimal={true} small={true}>
|
||||||
<T id={'report.compute_running.refresh'} />
|
<T id={'report.compute_running.refresh'} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</FinancialComputeAlert>
|
||||||
</If>
|
</If>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -46,3 +48,18 @@ export function BalanceSheetLoadingBar() {
|
|||||||
</If>
|
</If>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve balance sheet columns.
|
||||||
|
*/
|
||||||
|
export const useBalanceSheetColumns = () => {
|
||||||
|
// Balance sheet context.
|
||||||
|
const {
|
||||||
|
balanceSheet: { table },
|
||||||
|
} = useBalanceSheetContext();
|
||||||
|
|
||||||
|
return React.useMemo(
|
||||||
|
() => dynamicColumns(table.columns, table.rows),
|
||||||
|
[table],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,333 @@
|
|||||||
|
import * as R from 'ramda';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
|
||||||
|
import { Align } from 'common';
|
||||||
|
import { CellTextSpan } from 'components/Datatable/Cells';
|
||||||
|
import { getColumnWidth } from 'utils';
|
||||||
|
|
||||||
|
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 accountNameMapper = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: column.key,
|
||||||
|
Header: column.label,
|
||||||
|
accessor,
|
||||||
|
className: column.key,
|
||||||
|
textOverview: true,
|
||||||
|
width: Math.max(width, 300),
|
||||||
|
sticky: Align.Left,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assoc columns to total column.
|
||||||
|
*/
|
||||||
|
const assocColumnsToTotalColumn = R.curry((data, column, columnAccessor) => {
|
||||||
|
const columns = totalColumnsComposer(data, column);
|
||||||
|
|
||||||
|
return R.assoc('columns', columns, columnAccessor);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the given column has children columns.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
const isColumnHasColumns = (column) => !isEmpty(column.children);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} data
|
||||||
|
* @param {*} column
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const dateRangeSoloColumnAttrs = (data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessor,
|
||||||
|
width: getReportColWidth(data, accessor),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date range columns mapper.
|
||||||
|
*/
|
||||||
|
const dateRangeMapper = R.curry((data, column) => {
|
||||||
|
const isDateColumnHasColumns = isColumnHasColumns(column);
|
||||||
|
|
||||||
|
const columnAccessor = {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
align: isDateColumnHasColumns ? Align.Center : Align.Right,
|
||||||
|
};
|
||||||
|
return R.compose(
|
||||||
|
R.when(
|
||||||
|
R.always(isDateColumnHasColumns),
|
||||||
|
assocColumnsToTotalColumn(data, column),
|
||||||
|
),
|
||||||
|
R.when(
|
||||||
|
R.always(!isDateColumnHasColumns),
|
||||||
|
R.mergeLeft(dateRangeSoloColumnAttrs(data, column)),
|
||||||
|
),
|
||||||
|
)(columnAccessor);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total column mapper.
|
||||||
|
*/
|
||||||
|
const totalMapper = R.curry((data, column) => {
|
||||||
|
const hasChildren = !isEmpty(column.children);
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
const columnAccessor = {
|
||||||
|
key: column.key,
|
||||||
|
Header: column.label,
|
||||||
|
accessor,
|
||||||
|
textOverview: true,
|
||||||
|
Cell: CellTextSpan,
|
||||||
|
width,
|
||||||
|
disableSortBy: true,
|
||||||
|
align: hasChildren ? Align.Center : Align.Right,
|
||||||
|
};
|
||||||
|
return R.compose(
|
||||||
|
R.when(R.always(hasChildren), assocColumnsToTotalColumn(data, column)),
|
||||||
|
)(columnAccessor);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Percentage of column` column accessor.
|
||||||
|
*/
|
||||||
|
const percentageOfColumnAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Percentage of row` column accessor.
|
||||||
|
*/
|
||||||
|
const percentageOfRowAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous year column accessor.
|
||||||
|
*/
|
||||||
|
const previousYearAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pervious year change column accessor.
|
||||||
|
*/
|
||||||
|
const previousYearChangeAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous year percentage column accessor.
|
||||||
|
*/
|
||||||
|
const previousYearPercentageAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous period column accessor.
|
||||||
|
*/
|
||||||
|
const previousPeriodAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous period change column accessor.
|
||||||
|
*/
|
||||||
|
const previousPeriodChangeAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous period percentage column accessor.
|
||||||
|
*/
|
||||||
|
const previousPeriodPercentageAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} column
|
||||||
|
* @param {*} index
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const totalColumnsMapper = R.curry((data, column) => {
|
||||||
|
return R.compose(
|
||||||
|
R.when(R.pathEq(['key'], 'total'), totalMapper(data)),
|
||||||
|
// Percetage of column/row.
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'percentage_of_column'),
|
||||||
|
percentageOfColumnAccessor(data),
|
||||||
|
),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'percentage_of_row'),
|
||||||
|
percentageOfRowAccessor(data),
|
||||||
|
),
|
||||||
|
// Previous year.
|
||||||
|
R.when(R.pathEq(['key'], 'previous_year'), previousYearAccessor(data)),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'previous_year_change'),
|
||||||
|
previousYearChangeAccessor(data),
|
||||||
|
),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'previous_year_percentage'),
|
||||||
|
previousYearPercentageAccessor(data),
|
||||||
|
),
|
||||||
|
// Pervious period.
|
||||||
|
R.when(R.pathEq(['key'], 'previous_period'), previousPeriodAccessor(data)),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'previous_period_change'),
|
||||||
|
previousPeriodChangeAccessor(data),
|
||||||
|
),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'previous_period_percentage'),
|
||||||
|
previousPeriodPercentageAccessor(data),
|
||||||
|
),
|
||||||
|
)(column);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total sub-columns composer.
|
||||||
|
*/
|
||||||
|
const totalColumnsComposer = R.curry((data, column) => {
|
||||||
|
return R.map(totalColumnsMapper(data), column.children);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines the given string starts with `date-range` string.
|
||||||
|
*/
|
||||||
|
const isMatchesDateRange = (r) => R.match(/^date-range/g, r).length > 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamic column mapper.
|
||||||
|
*/
|
||||||
|
const dynamicColumnMapper = R.curry((data, column) => {
|
||||||
|
const indexTotalMapper = totalMapper(data);
|
||||||
|
const indexAccountNameMapper = accountNameMapper(data);
|
||||||
|
const indexDatePeriodMapper = dateRangeMapper(data);
|
||||||
|
|
||||||
|
return R.compose(
|
||||||
|
R.when(R.pathSatisfies(isMatchesDateRange, ['key']), indexDatePeriodMapper),
|
||||||
|
R.when(R.pathEq(['key'], 'name'), indexAccountNameMapper),
|
||||||
|
R.when(R.pathEq(['key'], 'total'), indexTotalMapper),
|
||||||
|
)(column);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cash flow dynamic columns.
|
||||||
|
*/
|
||||||
|
export const dynamicColumns = (columns, data) => {
|
||||||
|
return R.map(dynamicColumnMapper(data), columns);
|
||||||
|
};
|
||||||
@@ -1,7 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import moment from 'moment';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import moment from 'moment';
|
|
||||||
|
|
||||||
|
import { transformToForm } from 'utils';
|
||||||
|
import { useAppQueryString } from 'hooks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the default balance sheet query.
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
|
export const getDefaultBalanceSheetQuery = () => ({
|
||||||
|
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||||
|
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||||
|
basis: 'cash',
|
||||||
|
displayColumnsType: 'total',
|
||||||
|
filterByOption: 'without-zero-balance',
|
||||||
|
|
||||||
|
previousYear: false,
|
||||||
|
previousYearAmountChange: false,
|
||||||
|
previousYearPercentageChange: false,
|
||||||
|
|
||||||
|
previousPeriod: false,
|
||||||
|
previousPeriodAmountChange: false,
|
||||||
|
previousPeriodPercentageChange: false,
|
||||||
|
|
||||||
|
// Percentage columns.
|
||||||
|
percentageOfColumn: false,
|
||||||
|
percentageOfRow: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the balance sheet query.
|
||||||
|
*/
|
||||||
|
export const useBalanceSheetQuery = () => {
|
||||||
|
// Retrieves location query.
|
||||||
|
const [locationQuery, setLocationQuery] = useAppQueryString();
|
||||||
|
|
||||||
|
// Merges the default filter query with location URL query.
|
||||||
|
const query = React.useMemo(() => {
|
||||||
|
const defaultQuery = getDefaultBalanceSheetQuery();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...defaultQuery,
|
||||||
|
...transformToForm(locationQuery, defaultQuery),
|
||||||
|
};
|
||||||
|
}, [locationQuery]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
query,
|
||||||
|
locationQuery,
|
||||||
|
setLocationQuery,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the balance sheet header default values.
|
||||||
|
*/
|
||||||
export const getBalanceSheetHeaderDefaultValues = () => {
|
export const getBalanceSheetHeaderDefaultValues = () => {
|
||||||
return {
|
return {
|
||||||
basic: 'cash',
|
basic: 'cash',
|
||||||
@@ -12,6 +68,9 @@ export const getBalanceSheetHeaderDefaultValues = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the balance sheet header validation schema.
|
||||||
|
*/
|
||||||
export const getBalanceSheetHeaderValidationSchema = () =>
|
export const getBalanceSheetHeaderValidationSchema = () =>
|
||||||
Yup.object().shape({
|
Yup.object().shape({
|
||||||
dateRange: Yup.string().optional(),
|
dateRange: Yup.string().optional(),
|
||||||
@@ -23,3 +82,83 @@ export const getBalanceSheetHeaderValidationSchema = () =>
|
|||||||
filterByOption: Yup.string(),
|
filterByOption: Yup.string(),
|
||||||
displayColumnsType: Yup.string(),
|
displayColumnsType: Yup.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles previous year checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousYearCheckBoxChange = R.curry((form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
form.setFieldValue('previousYear', isChecked);
|
||||||
|
|
||||||
|
if (!isChecked) {
|
||||||
|
form.setFieldValue('previousYearAmountChange', isChecked);
|
||||||
|
form.setFieldValue('previousYearPercentageChange', isChecked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles previous period checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousPeriodCheckBoxChange = R.curry((form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
form.setFieldValue('previousPeriod', isChecked);
|
||||||
|
|
||||||
|
if (!isChecked) {
|
||||||
|
form.setFieldValue('previousPeriodAmountChange', isChecked);
|
||||||
|
form.setFieldValue('previousPeriodPercentageChange', isChecked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles previous year change checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousYearChangeCheckboxChange = R.curry((form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
form.setFieldValue('previousYear', event.currentTarget.checked);
|
||||||
|
}
|
||||||
|
form.setFieldValue('previousYearAmountChange', event.currentTarget.checked);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles preivous year percentage checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousYearPercentageCheckboxChange = R.curry(
|
||||||
|
(form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
form.setFieldValue('previousYear', event.currentTarget.checked);
|
||||||
|
}
|
||||||
|
form.setFieldValue('previousYearPercentageChange', isChecked);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles previous period percentage checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreivousPeriodPercentageCheckboxChange = R.curry(
|
||||||
|
(form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
form.setFieldValue('previousPeriod', isChecked);
|
||||||
|
}
|
||||||
|
form.setFieldValue('previousPeriodPercentageChange', isChecked);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle previous period change checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousPeriodChangeCheckboxChange = R.curry(
|
||||||
|
(form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
form.setFieldValue('previousPeriod', isChecked);
|
||||||
|
}
|
||||||
|
form.setFieldValue('previousPeriodAmountChange', isChecked);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import { DataTable } from 'components';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
|
||||||
import { useCashFlowStatementColumns } from './components';
|
import { useCashFlowStatementColumns } from './components';
|
||||||
import { useCashFlowStatementContext } from './CashFlowStatementProvider';
|
import { useCashFlowStatementContext } from './CashFlowStatementProvider';
|
||||||
|
|
||||||
@@ -15,8 +15,6 @@ export default function CashFlowStatementTable({
|
|||||||
// #ownProps
|
// #ownProps
|
||||||
companyName,
|
companyName,
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
cashFlowStatement: { tableRows },
|
cashFlowStatement: { tableRows },
|
||||||
isCashFlowLoading,
|
isCashFlowLoading,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import DataTable from 'components/DataTable';
|
|
||||||
|
|
||||||
import { useCustomersBalanceSummaryContext } from './CustomersBalanceSummaryProvider';
|
import { useCustomersBalanceSummaryContext } from './CustomersBalanceSummaryProvider';
|
||||||
import { useCustomersSummaryColumns } from './components';
|
import { useCustomersSummaryColumns } from './components';
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import DataTable from 'components/DataTable';
|
|
||||||
import { useCustomersTransactionsColumns } from './components';
|
import { useCustomersTransactionsColumns } from './components';
|
||||||
import { useCustomersTransactionsContext } from './CustomersTransactionsProvider';
|
import { useCustomersTransactionsContext } from './CustomersTransactionsProvider';
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,60 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { DashboardInsider } from 'components';
|
import { DashboardInsider } from 'components';
|
||||||
|
import styled from 'styled-components';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import 'style/pages/FinancialStatements/FinancialReportPage.scss';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Financial report page.
|
* Financial report page.
|
||||||
*/
|
*/
|
||||||
export default function FinancialReportPage(props) {
|
export default function FinancialReportPage(props) {
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<FinancialReportPageRoot
|
||||||
{...props}
|
{...props}
|
||||||
className={classNames(CLASSES.FINANCIAL_REPORT_INSIDER, props.className)}
|
className={classNames(CLASSES.FINANCIAL_REPORT_INSIDER, props.className)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const FinancialComputeAlert = styled.div`
|
||||||
|
position: relative;
|
||||||
|
padding: 8px 20px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: #fdecda;
|
||||||
|
color: #342515;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 12px;
|
||||||
|
min-height: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
|
||||||
|
&,
|
||||||
|
&:hover {
|
||||||
|
color: #824400;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
svg {
|
||||||
|
margin-right: 6px;
|
||||||
|
position: relative;
|
||||||
|
top: -2px;
|
||||||
|
fill: #975f19;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FinancialProgressbar = styled.div`
|
||||||
|
.progress-materializecss {
|
||||||
|
top: -2px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FinancialReportPageRoot = styled(DashboardInsider)``;
|
||||||
|
|
||||||
|
export const FinancialReportBody = styled.div`
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { useMemo } from 'react';
|
|||||||
import { defaultExpanderReducer } from 'utils';
|
import { defaultExpanderReducer } from 'utils';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { FinancialSheet } from 'components';
|
||||||
import DataTable from 'components/DataTable';
|
import DataTable from 'components/DataTable';
|
||||||
import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows';
|
import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows';
|
||||||
import TableFastCell from 'components/Datatable/TableFastCell';
|
import TableFastCell from 'components/Datatable/TableFastCell';
|
||||||
@@ -37,7 +37,6 @@ export default function GeneralLedgerTable({ companyName }) {
|
|||||||
sheetType={intl.get('general_ledger_sheet')}
|
sheetType={intl.get('general_ledger_sheet')}
|
||||||
fromDate={query.from_date}
|
fromDate={query.from_date}
|
||||||
toDate={query.to_date}
|
toDate={query.to_date}
|
||||||
name="general-ledger"
|
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import { DataTable } from 'components';
|
|
||||||
import { useInventoryItemDetailsColumns } from './components';
|
import { useInventoryItemDetailsColumns } from './components';
|
||||||
import { useInventoryItemDetailsContext } from './InventoryItemDetailsProvider';
|
import { useInventoryItemDetailsContext } from './InventoryItemDetailsProvider';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl, { init } from 'react-intl-universal';
|
import intl, { init } from 'react-intl-universal';
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import { DataTable } from 'components';
|
|
||||||
|
|
||||||
import { useInventoryValuationContext } from './InventoryValuationProvider';
|
import { useInventoryValuationContext } from './InventoryValuationProvider';
|
||||||
import { useInventoryValuationTableColumns } from './components';
|
import { useInventoryValuationTableColumns } from './components';
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import DataTable from 'components/DataTable';
|
|
||||||
import { useJournalSheetContext } from './JournalProvider';
|
|
||||||
import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows';
|
import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows';
|
||||||
import TableFastCell from 'components/Datatable/TableFastCell';
|
import TableFastCell from 'components/Datatable/TableFastCell';
|
||||||
import { defaultExpanderReducer } from 'utils';
|
|
||||||
import { useJournalTableColumns } from './components';
|
import { useJournalTableColumns } from './components';
|
||||||
|
import { useJournalSheetContext } from './JournalProvider';
|
||||||
|
|
||||||
|
import { defaultExpanderReducer } from 'utils';
|
||||||
|
|
||||||
export default function JournalSheetTable({
|
export default function JournalSheetTable({
|
||||||
// #ownProps
|
// #ownProps
|
||||||
onFetchData,
|
onFetchData,
|
||||||
companyName,
|
companyName,
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
|
|
||||||
// Journal sheet context.
|
// Journal sheet context.
|
||||||
const {
|
const {
|
||||||
journalSheet: { tableRows, query },
|
journalSheet: { tableRows, query },
|
||||||
isLoading
|
isLoading,
|
||||||
} = useJournalSheetContext();
|
} = useJournalSheetContext();
|
||||||
|
|
||||||
// Retreive the journal table columns.
|
// Retreive the journal table columns.
|
||||||
@@ -42,8 +41,6 @@ export default function JournalSheetTable({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FinancialSheet
|
<FinancialSheet
|
||||||
companyName={companyName}
|
companyName={companyName}
|
||||||
@@ -60,14 +57,15 @@ export default function JournalSheetTable({
|
|||||||
columns={columns}
|
columns={columns}
|
||||||
data={tableRows}
|
data={tableRows}
|
||||||
rowClassNames={rowClassNames}
|
rowClassNames={rowClassNames}
|
||||||
noResults={intl.get('this_report_does_not_contain_any_data_between_date_period')}
|
noResults={intl.get(
|
||||||
|
'this_report_does_not_contain_any_data_between_date_period',
|
||||||
|
)}
|
||||||
expanded={expandedRows}
|
expanded={expandedRows}
|
||||||
sticky={true}
|
sticky={true}
|
||||||
TableRowsRenderer={TableVirtualizedListRows}
|
TableRowsRenderer={TableVirtualizedListRows}
|
||||||
// #TableVirtualizedListRows props.
|
// #TableVirtualizedListRows props.
|
||||||
vListrowHeight={28}
|
vListrowHeight={28}
|
||||||
vListOverscanRowCount={2}
|
vListOverscanRowCount={2}
|
||||||
|
|
||||||
TableCellRenderer={TableFastCell}
|
TableCellRenderer={TableFastCell}
|
||||||
id={'journal'}
|
id={'journal'}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { FinancialSheetSkeleton } from '../../../components/FinancialSheet';
|
||||||
|
import ProfitLossSheetTable from './ProfitLossSheetTable';
|
||||||
|
import { FinancialReportBody } from '../FinancialReportPage';
|
||||||
|
|
||||||
|
import withCurrentOrganization from '../../Organization/withCurrentOrganization';
|
||||||
|
import { useProfitLossSheetContext } from './ProfitLossProvider';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {React.JSX}
|
||||||
|
*/
|
||||||
|
function ProfitLossBodyJSX({
|
||||||
|
// #withPreferences
|
||||||
|
organizationName,
|
||||||
|
}) {
|
||||||
|
const { isLoading } = useProfitLossSheetContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FinancialReportBody>
|
||||||
|
{isLoading ? (
|
||||||
|
<FinancialSheetSkeleton />
|
||||||
|
) : (
|
||||||
|
<ProfitLossSheetTable companyName={organizationName} />
|
||||||
|
)}
|
||||||
|
</FinancialReportBody>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProfitLossBody = compose(
|
||||||
|
withCurrentOrganization(({ organization }) => ({
|
||||||
|
organizationName: organization.name,
|
||||||
|
})),
|
||||||
|
)(ProfitLossBodyJSX);
|
||||||
@@ -5,6 +5,10 @@ import { transformFilterFormToQuery } from '../common';
|
|||||||
|
|
||||||
const ProfitLossSheetContext = createContext();
|
const ProfitLossSheetContext = createContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profit/loss sheet provider.
|
||||||
|
* @returns {React.JSX}
|
||||||
|
*/
|
||||||
function ProfitLossSheetProvider({ query, ...props }) {
|
function ProfitLossSheetProvider({ query, ...props }) {
|
||||||
const {
|
const {
|
||||||
data: profitLossSheet,
|
data: profitLossSheet,
|
||||||
|
|||||||
@@ -1,57 +1,47 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { compose } from 'utils';
|
import * as R from 'ramda';
|
||||||
|
|
||||||
import ProfitLossSheetHeader from './ProfitLossSheetHeader';
|
import ProfitLossSheetHeader from './ProfitLossSheetHeader';
|
||||||
import ProfitLossSheetTable from './ProfitLossSheetTable';
|
|
||||||
import ProfitLossActionsBar from './ProfitLossActionsBar';
|
import ProfitLossActionsBar from './ProfitLossActionsBar';
|
||||||
|
|
||||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||||
|
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
import withProfitLossActions from './withProfitLossActions';
|
import withProfitLossActions from './withProfitLossActions';
|
||||||
import withCurrentOrganization from '../../Organization/withCurrentOrganization';
|
|
||||||
|
|
||||||
import 'style/pages/FinancialStatements/ProfitLossSheet.scss';
|
import { useProfitLossSheetQuery } from './utils';
|
||||||
import { ProfitLossSheetProvider } from './ProfitLossProvider';
|
import { ProfitLossSheetProvider } from './ProfitLossProvider';
|
||||||
import { ProfitLossSheetLoadingBar, ProfitLossSheetAlerts } from './components';
|
import { ProfitLossSheetLoadingBar } from './components';
|
||||||
|
import { ProfitLossBody } from './ProfitLossBody';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Profit/Loss financial statement sheet.
|
* Profit/Loss financial statement sheet.
|
||||||
|
* @returns {React.JSX}
|
||||||
*/
|
*/
|
||||||
function ProfitLossSheet({
|
function ProfitLossSheet({
|
||||||
// #withPreferences
|
|
||||||
organizationName,
|
|
||||||
|
|
||||||
// #withProfitLossActions
|
// #withProfitLossActions
|
||||||
toggleProfitLossFilterDrawer: toggleDisplayFilterDrawer,
|
toggleProfitLossFilterDrawer: toggleDisplayFilterDrawer,
|
||||||
}) {
|
}) {
|
||||||
const [filter, setFilter] = useState({
|
// Profit/loss sheet query.
|
||||||
basis: 'cash',
|
const { query, setLocationQuery } = useProfitLossSheetQuery();
|
||||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
|
||||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
|
||||||
displayColumnsType: 'total',
|
|
||||||
filterByOption: 'with-transactions',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle submit filter.
|
// Handle submit filter.
|
||||||
const handleSubmitFilter = (filter) => {
|
const handleSubmitFilter = (filter) => {
|
||||||
const _filter = {
|
const newFilter = {
|
||||||
...filter,
|
...filter,
|
||||||
fromDate: moment(filter.fromDate).format('YYYY-MM-DD'),
|
fromDate: moment(filter.fromDate).format('YYYY-MM-DD'),
|
||||||
toDate: moment(filter.toDate).format('YYYY-MM-DD'),
|
toDate: moment(filter.toDate).format('YYYY-MM-DD'),
|
||||||
};
|
};
|
||||||
setFilter(_filter);
|
setLocationQuery(newFilter);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle number format submit.
|
// Handle number format submit.
|
||||||
const handleNumberFormatSubmit = (numberFormat) => {
|
const handleNumberFormatSubmit = (numberFormat) => {
|
||||||
setFilter({
|
setLocationQuery({
|
||||||
...filter,
|
...query,
|
||||||
numberFormat,
|
numberFormat,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hide the filter drawer once the page unmount.
|
// Hide the filter drawer once the page unmount.
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
() => () => {
|
() => () => {
|
||||||
@@ -61,34 +51,26 @@ function ProfitLossSheet({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProfitLossSheetProvider query={filter}>
|
<ProfitLossSheetProvider query={query}>
|
||||||
<ProfitLossActionsBar
|
<ProfitLossActionsBar
|
||||||
numberFormat={filter.numberFormat}
|
numberFormat={query.numberFormat}
|
||||||
onNumberFormatSubmit={handleNumberFormatSubmit}
|
onNumberFormatSubmit={handleNumberFormatSubmit}
|
||||||
/>
|
/>
|
||||||
<ProfitLossSheetLoadingBar />
|
<ProfitLossSheetLoadingBar />
|
||||||
<ProfitLossSheetAlerts />
|
{/* <ProfitLossSheetAlerts /> */}
|
||||||
|
|
||||||
<DashboardPageContent>
|
<DashboardPageContent>
|
||||||
<div class="financial-statement">
|
|
||||||
<ProfitLossSheetHeader
|
<ProfitLossSheetHeader
|
||||||
pageFilter={filter}
|
pageFilter={query}
|
||||||
onSubmitFilter={handleSubmitFilter}
|
onSubmitFilter={handleSubmitFilter}
|
||||||
/>
|
/>
|
||||||
|
<ProfitLossBody />
|
||||||
<div class="financial-statement__body">
|
|
||||||
<ProfitLossSheetTable companyName={organizationName} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DashboardPageContent>
|
</DashboardPageContent>
|
||||||
</ProfitLossSheetProvider>
|
</ProfitLossSheetProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default R.compose(
|
||||||
withDashboardActions,
|
withDashboardActions,
|
||||||
withProfitLossActions,
|
withProfitLossActions,
|
||||||
withCurrentOrganization(({ organization }) => ({
|
|
||||||
organizationName: organization.name,
|
|
||||||
})),
|
|
||||||
)(ProfitLossSheet);
|
)(ProfitLossSheet);
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { FormattedMessage as T } from 'components';
|
import * as R from 'ramda';
|
||||||
import intl from 'react-intl-universal';
|
|
||||||
import * as Yup from 'yup';
|
|
||||||
import { Tabs, Tab, Button, Intent } from '@blueprintjs/core';
|
import { Tabs, Tab, Button, Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
import { FormattedMessage as T } from 'components';
|
||||||
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||||
import ProfitLossSheetHeaderGeneralPane from './ProfitLossSheetHeaderGeneralPane';
|
import ProfitLossSheetHeaderGeneralPane from './ProfitLossSheetHeaderGeneralPane';
|
||||||
|
import ProfitLossSheetHeaderComparisonPanel from './ProfitLossSheetHeaderComparisonPanel';
|
||||||
|
|
||||||
import withProfitLoss from './withProfitLoss';
|
import withProfitLoss from './withProfitLoss';
|
||||||
import withProfitLossActions from './withProfitLossActions';
|
import withProfitLossActions from './withProfitLossActions';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { useProfitLossHeaderValidationSchema } from './utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profit/loss header.
|
||||||
|
* @returns {React.JSX}
|
||||||
|
*/
|
||||||
function ProfitLossHeader({
|
function ProfitLossHeader({
|
||||||
// #ownProps
|
// #ownProps
|
||||||
pageFilter,
|
pageFilter,
|
||||||
@@ -26,15 +30,7 @@ function ProfitLossHeader({
|
|||||||
toggleProfitLossFilterDrawer: toggleFilterDrawer,
|
toggleProfitLossFilterDrawer: toggleFilterDrawer,
|
||||||
}) {
|
}) {
|
||||||
// Validation schema.
|
// Validation schema.
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = useProfitLossHeaderValidationSchema();
|
||||||
fromDate: Yup.date().required().label(intl.get('from_date')),
|
|
||||||
toDate: Yup.date()
|
|
||||||
.min(Yup.ref('fromDate'))
|
|
||||||
.required()
|
|
||||||
.label(intl.get('to_date')),
|
|
||||||
filterByOption: Yup.string(),
|
|
||||||
displayColumnsType: Yup.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initial values.
|
// Initial values.
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
@@ -42,13 +38,11 @@ function ProfitLossHeader({
|
|||||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||||
toDate: moment(pageFilter.toDate).toDate(),
|
toDate: moment(pageFilter.toDate).toDate(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle form submit.
|
// Handle form submit.
|
||||||
const handleSubmit = (values, actions) => {
|
const handleSubmit = (values, actions) => {
|
||||||
onSubmitFilter(values);
|
onSubmitFilter(values);
|
||||||
toggleFilterDrawer(false);
|
toggleFilterDrawer(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles the cancel button click.
|
// Handles the cancel button click.
|
||||||
const handleCancelClick = () => {
|
const handleCancelClick = () => {
|
||||||
toggleFilterDrawer(false);
|
toggleFilterDrawer(false);
|
||||||
@@ -75,6 +69,11 @@ function ProfitLossHeader({
|
|||||||
title={<T id={'general'} />}
|
title={<T id={'general'} />}
|
||||||
panel={<ProfitLossSheetHeaderGeneralPane />}
|
panel={<ProfitLossSheetHeaderGeneralPane />}
|
||||||
/>
|
/>
|
||||||
|
<Tab
|
||||||
|
id="comparison"
|
||||||
|
title={<T id={'profit_loss_sheet.comparisons'} />}
|
||||||
|
panel={<ProfitLossSheetHeaderComparisonPanel />}
|
||||||
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
<div class="financial-header-drawer__footer">
|
<div class="financial-header-drawer__footer">
|
||||||
@@ -91,7 +90,7 @@ function ProfitLossHeader({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default R.compose(
|
||||||
withProfitLoss(({ profitLossDrawerFilter }) => ({
|
withProfitLoss(({ profitLossDrawerFilter }) => ({
|
||||||
profitLossDrawerFilter,
|
profitLossDrawerFilter,
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FastField, Field } from 'formik';
|
||||||
|
import { FormGroup, Checkbox } from '@blueprintjs/core';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { FormattedMessage as T } from 'components';
|
||||||
|
|
||||||
|
import { Row, Col, FieldHint } from '../../../components';
|
||||||
|
import {
|
||||||
|
handlePreviousYearCheckBoxChange,
|
||||||
|
handlePreviousPeriodCheckBoxChange,
|
||||||
|
handlePreviousYearChangeCheckboxChange,
|
||||||
|
handlePreviousYearPercentageCheckboxChange,
|
||||||
|
handlePreviousPeriodChangeCheckboxChange,
|
||||||
|
handlePreviousPeriodPercentageCheckboxChange,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProfitLoss sheet header -comparison panel.
|
||||||
|
*/
|
||||||
|
export default function ProfitLossSheetHeaderComparisonPanel() {
|
||||||
|
return (
|
||||||
|
<ProfitLossSheetComparisonWrap>
|
||||||
|
{/**----------- Previous Year -----------*/}
|
||||||
|
<FastField name={'previousYear'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'profit_loss_sheet.previous_year'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousYearCheckBoxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
<Row>
|
||||||
|
<Col xs={3}>
|
||||||
|
<FastField name={'previousYearAmountChange'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'profit_loss_sheet.total_change'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousYearChangeCheckboxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
<Col xs={3}>
|
||||||
|
<FastField name={'previousYearPercentageChange'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'profit_loss_sheet.perentage_change'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousYearPercentageCheckboxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{/**----------- Previous Period (PP) -----------*/}
|
||||||
|
<FastField name={'previousPeriod'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'profit_loss_sheet.previous_period'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousPeriodCheckBoxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
<Row>
|
||||||
|
<Col xs={3}>
|
||||||
|
<FastField name={'previousPeriodAmountChange'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'profit_loss_sheet.total_change'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousPeriodChangeCheckboxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
<Col xs={3}>
|
||||||
|
<FastField name={'previousPeriodPercentageChange'} type={'checkbox'}>
|
||||||
|
{({ form, field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'profit_loss_sheet.perentage_change'} />}
|
||||||
|
{...field}
|
||||||
|
onChange={handlePreviousPeriodPercentageCheckboxChange(form)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{/**----------- % of Column -----------*/}
|
||||||
|
<FastField name={'percentageColumn'} type={'checkbox'}>
|
||||||
|
{({ field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'profit_loss_sheet.percentage_of_column'} />}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
{/**----------- % of Row -----------*/}
|
||||||
|
<FastField name={'percentageRow'} type={'checkbox'}>
|
||||||
|
{({ field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'profit_loss_sheet.percentage_of_row'} />}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
{/**----------- % of Expense -----------*/}
|
||||||
|
<FastField name={'percentageExpense'} type={'checkbox'}>
|
||||||
|
{({ field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'profit_loss_sheet.percentage_of_expense'} />}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
{/**----------- % of Income -----------*/}
|
||||||
|
<FastField name={'percentageIncome'} type={'checkbox'}>
|
||||||
|
{({ field }) => (
|
||||||
|
<FormGroup labelInfo={<FieldHint />}>
|
||||||
|
<Checkbox
|
||||||
|
inline={true}
|
||||||
|
small={true}
|
||||||
|
label={<T id={'profit_loss_sheet.percentage_of_income'} />}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</ProfitLossSheetComparisonWrap>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProfitLossSheetComparisonWrap = styled.div`
|
||||||
|
.row {
|
||||||
|
margin-left: 0.15rem;
|
||||||
|
.col {
|
||||||
|
min-width: 150px !important;
|
||||||
|
max-width: 190px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bp3-form-group {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -1,87 +1,32 @@
|
|||||||
import React, { useMemo, useCallback } from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage as T } from 'components';
|
import styled from 'styled-components';
|
||||||
import intl from 'react-intl-universal';
|
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { TableStyle } from 'common';
|
||||||
import DataTable from 'components/DataTable';
|
import { DataTable, FinancialSheet, FormattedMessage as T } from 'components';
|
||||||
import { CellTextSpan } from 'components/Datatable/Cells';
|
|
||||||
|
|
||||||
import { defaultExpanderReducer, getColumnWidth } from 'utils';
|
import { tableRowTypesToClassnames, defaultExpanderReducer } from 'utils';
|
||||||
|
import { useProfitLossSheetColumns } from './hooks';
|
||||||
import { useProfitLossSheetContext } from './ProfitLossProvider';
|
import { useProfitLossSheetContext } from './ProfitLossProvider';
|
||||||
|
|
||||||
export default function ProfitLossSheetTable({
|
export default function ProfitLossSheetTable({
|
||||||
// #ownProps
|
// #ownProps
|
||||||
companyName,
|
companyName,
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
|
|
||||||
// Profit/Loss sheet context.
|
// Profit/Loss sheet context.
|
||||||
const {
|
const {
|
||||||
profitLossSheet: { tableRows, query, columns },
|
profitLossSheet: { table, query },
|
||||||
isLoading
|
isLoading,
|
||||||
} = useProfitLossSheetContext();
|
} = useProfitLossSheetContext();
|
||||||
|
|
||||||
const tableColumns = useMemo(
|
// Retrieves the profit/loss table columns.
|
||||||
() => [
|
const tableColumns = useProfitLossSheetColumns();
|
||||||
{
|
|
||||||
Header: intl.get('account'),
|
|
||||||
accessor: (row) => (row.code ? `${row.name} - ${row.code}` : row.name),
|
|
||||||
className: 'name',
|
|
||||||
textOverview: true,
|
|
||||||
width: 240,
|
|
||||||
},
|
|
||||||
...(query.display_columns_type === 'total'
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
Header: intl.get('total'),
|
|
||||||
Cell: CellTextSpan,
|
|
||||||
accessor: 'total.formatted_amount',
|
|
||||||
className: 'total',
|
|
||||||
width: 140,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(query.display_columns_type === 'date_periods'
|
|
||||||
? columns.map((column, index) => ({
|
|
||||||
id: `date_period_${index}`,
|
|
||||||
Header: column,
|
|
||||||
Cell: CellTextSpan,
|
|
||||||
accessor: `total_periods[${index}].formatted_amount`,
|
|
||||||
width: getColumnWidth(
|
|
||||||
tableRows,
|
|
||||||
`total_periods.${index}.formatted_amount`,
|
|
||||||
{ minWidth: 100 },
|
|
||||||
),
|
|
||||||
className: 'total-period',
|
|
||||||
}))
|
|
||||||
: []),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
query.display_columns_type,
|
|
||||||
tableRows,
|
|
||||||
columns,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Retrieve default expanded rows of balance sheet.
|
// Retrieve default expanded rows of balance sheet.
|
||||||
const expandedRows = useMemo(
|
const expandedRows = React.useMemo(
|
||||||
() => defaultExpanderReducer(tableRows, 3),
|
() => defaultExpanderReducer(table?.rows || [], 3),
|
||||||
[tableRows],
|
[table],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Retrieve conditional datatable row classnames.
|
|
||||||
const rowClassNames = useCallback((row) => {
|
|
||||||
const { original } = row;
|
|
||||||
const rowTypes = Array.isArray(original.rowTypes) ? original.rowTypes : [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
...rowTypes.reduce((acc, rowType) => {
|
|
||||||
acc[`row_type--${rowType}`] = rowType;
|
|
||||||
return acc;
|
|
||||||
}, {}),
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FinancialSheet
|
<FinancialSheet
|
||||||
companyName={companyName}
|
companyName={companyName}
|
||||||
@@ -92,18 +37,43 @@ export default function ProfitLossSheetTable({
|
|||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
basis={query.basis}
|
basis={query.basis}
|
||||||
>
|
>
|
||||||
<DataTable
|
<ProfitLossDataTable
|
||||||
className="bigcapital-datatable--financial-report"
|
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
data={tableRows}
|
data={table.rows}
|
||||||
noInitialFetch={true}
|
noInitialFetch={true}
|
||||||
expanded={expandedRows}
|
expanded={expandedRows}
|
||||||
rowClassNames={rowClassNames}
|
rowClassNames={tableRowTypesToClassnames}
|
||||||
expandable={true}
|
expandable={true}
|
||||||
expandToggleColumn={1}
|
expandToggleColumn={1}
|
||||||
|
|
||||||
sticky={true}
|
sticky={true}
|
||||||
|
styleName={TableStyle.Constrant}
|
||||||
/>
|
/>
|
||||||
</FinancialSheet>
|
</FinancialSheet>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ProfitLossDataTable = styled(DataTable)`
|
||||||
|
.table {
|
||||||
|
.tbody .tr {
|
||||||
|
.td {
|
||||||
|
border-bottom: 0;
|
||||||
|
padding-top: 0.32rem;
|
||||||
|
padding-bottom: 0.32rem;
|
||||||
|
}
|
||||||
|
&.is-expanded {
|
||||||
|
.td:not(.name) .cell-inner {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.row_type--TOTAL {
|
||||||
|
.td {
|
||||||
|
font-weight: 500;
|
||||||
|
border-top: 1px solid #bbb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:last-of-type .td {
|
||||||
|
border-bottom: 3px double #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -0,0 +1,383 @@
|
|||||||
|
import * as R from 'ramda';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
|
||||||
|
import { Align } from 'common';
|
||||||
|
import { CellTextSpan } from 'components/Datatable/Cells';
|
||||||
|
import { getColumnWidth } from 'utils';
|
||||||
|
|
||||||
|
const getTableCellValueAccessor = (index) => `cells[${index}].value`;
|
||||||
|
|
||||||
|
const getReportColWidth = (data, accessor, labelText) => {
|
||||||
|
return getColumnWidth(
|
||||||
|
data,
|
||||||
|
accessor,
|
||||||
|
{ magicSpacing: 10, minWidth: 100 },
|
||||||
|
labelText,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isNodeHasChildren = (node) => !isEmpty(node.children);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Percentage of income` column accessor.
|
||||||
|
*/
|
||||||
|
const percentageOfIncomeAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Percentage of expense` column accessor.
|
||||||
|
*/
|
||||||
|
const percentageOfExpenseAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Percentage of column` column accessor.
|
||||||
|
*/
|
||||||
|
const percentageOfColumnAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Percentage of row` column accessor.
|
||||||
|
*/
|
||||||
|
const percentageOfRowAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous year column accessor.
|
||||||
|
*/
|
||||||
|
const previousYearAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pervious year change column accessor.
|
||||||
|
*/
|
||||||
|
const previousYearChangeAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous year percentage column accessor.
|
||||||
|
*/
|
||||||
|
const previousYearPercentageAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous period column accessor.
|
||||||
|
*/
|
||||||
|
const previousPeriodAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous period change column accessor.
|
||||||
|
*/
|
||||||
|
const previousPeriodChangeAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous period percentage column accessor.
|
||||||
|
*/
|
||||||
|
const previousPeriodPercentageAccessor = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
accessor,
|
||||||
|
width,
|
||||||
|
align: Align.Right,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} column
|
||||||
|
* @param {*} index
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const totalColumnsMapper = R.curry((data, column) => {
|
||||||
|
return R.compose(
|
||||||
|
R.when(R.pathEq(['key'], 'total'), totalColumn(data)),
|
||||||
|
// Percetage of column/row.
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'percentage_column'),
|
||||||
|
percentageOfColumnAccessor(data),
|
||||||
|
),
|
||||||
|
R.when(R.pathEq(['key'], 'percentage_row'), percentageOfRowAccessor(data)),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'percentage_income'),
|
||||||
|
percentageOfIncomeAccessor(data),
|
||||||
|
),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'percentage_expenses'),
|
||||||
|
percentageOfExpenseAccessor(data),
|
||||||
|
),
|
||||||
|
// Previous year.
|
||||||
|
R.when(R.pathEq(['key'], 'previous_year'), previousYearAccessor(data)),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'previous_year_change'),
|
||||||
|
previousYearChangeAccessor(data),
|
||||||
|
),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'previous_year_percentage'),
|
||||||
|
previousYearPercentageAccessor(data),
|
||||||
|
),
|
||||||
|
// Pervious period.
|
||||||
|
R.when(R.pathEq(['key'], 'previous_period'), previousPeriodAccessor(data)),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'previous_period_change'),
|
||||||
|
previousPeriodChangeAccessor(data),
|
||||||
|
),
|
||||||
|
R.when(
|
||||||
|
R.pathEq(['key'], 'previous_period_percentage'),
|
||||||
|
previousPeriodPercentageAccessor(data),
|
||||||
|
),
|
||||||
|
)(column);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total sub-columns composer.
|
||||||
|
*/
|
||||||
|
const totalColumnsComposer = R.curry((data, column) => {
|
||||||
|
return R.map(totalColumnsMapper(data), column.children);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assoc columns to total column.
|
||||||
|
*/
|
||||||
|
const assocColumnsToTotalColumn = R.curry((data, column, columnAccessor) => {
|
||||||
|
const columns = totalColumnsComposer(data, column);
|
||||||
|
|
||||||
|
return R.assoc('columns', columns, columnAccessor);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the total column.
|
||||||
|
*/
|
||||||
|
const totalColumn = R.curry((data, column) => {
|
||||||
|
const hasChildren = isNodeHasChildren(column);
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: column.key,
|
||||||
|
Header: column.label,
|
||||||
|
accessor,
|
||||||
|
textOverview: true,
|
||||||
|
Cell: CellTextSpan,
|
||||||
|
width,
|
||||||
|
disableSortBy: true,
|
||||||
|
align: hasChildren ? Align.Center : Align.Right,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const totalColumnCompose = R.curry((data, column) => {
|
||||||
|
const hasChildren = isNodeHasChildren(column);
|
||||||
|
|
||||||
|
return R.compose(
|
||||||
|
R.when(R.always(hasChildren), assocColumnsToTotalColumn(data, column)),
|
||||||
|
totalColumn(data),
|
||||||
|
)(column);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account name column mapper.
|
||||||
|
*/
|
||||||
|
const accountNameColumn = R.curry((data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
const width = getReportColWidth(data, accessor, column.label);
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: column.key,
|
||||||
|
Header: column.label,
|
||||||
|
accessor,
|
||||||
|
className: column.key,
|
||||||
|
textOverview: true,
|
||||||
|
width: Math.max(width, 300),
|
||||||
|
sticky: Align.Left,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} data
|
||||||
|
* @param {*} column
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const dateRangeSoloColumnAttrs = (data, column) => {
|
||||||
|
const accessor = getTableCellValueAccessor(column.cell_index);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessor,
|
||||||
|
width: getReportColWidth(data, accessor),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves date range column.
|
||||||
|
*/
|
||||||
|
const dateRangeColumn = R.curry((data, column) => {
|
||||||
|
const isDateColumnHasColumns = isNodeHasChildren(column);
|
||||||
|
|
||||||
|
const columnAccessor = {
|
||||||
|
Header: column.label,
|
||||||
|
key: column.key,
|
||||||
|
disableSortBy: true,
|
||||||
|
textOverview: true,
|
||||||
|
align: isDateColumnHasColumns ? Align.Center : Align.Right,
|
||||||
|
};
|
||||||
|
return R.compose(
|
||||||
|
R.when(
|
||||||
|
R.always(isDateColumnHasColumns),
|
||||||
|
assocColumnsToTotalColumn(data, column),
|
||||||
|
),
|
||||||
|
R.when(
|
||||||
|
R.always(!isDateColumnHasColumns),
|
||||||
|
R.mergeLeft(dateRangeSoloColumnAttrs(data, column)),
|
||||||
|
),
|
||||||
|
)(columnAccessor);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines the given string starts with `date-range` string.
|
||||||
|
*/
|
||||||
|
const isMatchesDateRange = (r) => R.match(/^date-range/g, r).length > 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {} data
|
||||||
|
* @param {} column
|
||||||
|
*/
|
||||||
|
const dynamicColumnMapper = R.curry((data, column) => {
|
||||||
|
const indexTotalColumn = totalColumnCompose(data);
|
||||||
|
const indexAccountNameColumn = accountNameColumn(data);
|
||||||
|
const indexDatePeriodMapper = dateRangeColumn(data);
|
||||||
|
|
||||||
|
return R.compose(
|
||||||
|
R.when(R.pathSatisfies(isMatchesDateRange, ['key']), indexDatePeriodMapper),
|
||||||
|
R.when(R.pathEq(['key'], 'name'), indexAccountNameColumn),
|
||||||
|
R.when(R.pathEq(['key'], 'total'), indexTotalColumn),
|
||||||
|
)(column);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} columns
|
||||||
|
* @param {*} data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const dynamicColumns = (columns, data) => {
|
||||||
|
return R.map(dynamicColumnMapper(data), columns);
|
||||||
|
};
|
||||||
19
src/containers/FinancialStatements/ProfitLossSheet/hooks.js
Normal file
19
src/containers/FinancialStatements/ProfitLossSheet/hooks.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { dynamicColumns } from './dynamicColumns';
|
||||||
|
import { useProfitLossSheetContext } from './ProfitLossProvider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the profit/loss table columns.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const useProfitLossSheetColumns = () => {
|
||||||
|
const {
|
||||||
|
profitLossSheet: { table },
|
||||||
|
} = useProfitLossSheetContext();
|
||||||
|
|
||||||
|
return React.useMemo(
|
||||||
|
() => dynamicColumns(table.columns || [], table.rows || []),
|
||||||
|
[table],
|
||||||
|
);
|
||||||
|
};
|
||||||
156
src/containers/FinancialStatements/ProfitLossSheet/utils.js
Normal file
156
src/containers/FinancialStatements/ProfitLossSheet/utils.js
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import moment from 'moment';
|
||||||
|
import intl from 'react-intl-universal';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
|
import { useAppQueryString } from 'hooks';
|
||||||
|
import { transformToForm } from 'utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the default profit/loss sheet query.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getDefaultProfitLossQuery = () => ({
|
||||||
|
basis: 'cash',
|
||||||
|
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||||
|
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||||
|
displayColumnsType: 'total',
|
||||||
|
filterByOption: 'with-transactions',
|
||||||
|
|
||||||
|
previousYear: false,
|
||||||
|
previousYearAmountChange: false,
|
||||||
|
previousYearPercentageChange: false,
|
||||||
|
|
||||||
|
previousPeriod: false,
|
||||||
|
previousPeriodAmountChange: false,
|
||||||
|
previousPeriodPercentageChange: false,
|
||||||
|
|
||||||
|
// Percentage columns.
|
||||||
|
percentageColumn: false,
|
||||||
|
percentageRow: false,
|
||||||
|
percentageIncome: false,
|
||||||
|
percentageExpense: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the balance sheet query API.
|
||||||
|
*/
|
||||||
|
export const useProfitLossSheetQuery = () => {
|
||||||
|
// Retrieves location query.
|
||||||
|
const [locationQuery, setLocationQuery] = useAppQueryString();
|
||||||
|
|
||||||
|
// Merges the default query with location query.
|
||||||
|
const query = React.useMemo(() => {
|
||||||
|
const defaultQuery = getDefaultProfitLossQuery();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...defaultQuery,
|
||||||
|
...transformToForm(locationQuery, defaultQuery),
|
||||||
|
};
|
||||||
|
}, [locationQuery]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
query,
|
||||||
|
locationQuery,
|
||||||
|
setLocationQuery,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the profit/loss header validation schema.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const useProfitLossHeaderValidationSchema = () => {
|
||||||
|
return Yup.object().shape({
|
||||||
|
fromDate: Yup.date().required().label(intl.get('from_date')),
|
||||||
|
toDate: Yup.date()
|
||||||
|
.min(Yup.ref('fromDate'))
|
||||||
|
.required()
|
||||||
|
.label(intl.get('to_date')),
|
||||||
|
filterByOption: Yup.string(),
|
||||||
|
displayColumnsType: Yup.string(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the previous year checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousYearCheckBoxChange = R.curry((form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
|
||||||
|
form.setFieldValue('previousYear', isChecked);
|
||||||
|
|
||||||
|
if (!isChecked) {
|
||||||
|
form.setFieldValue('previousYearAmountChange', isChecked);
|
||||||
|
form.setFieldValue('previousYearPercentageChange', isChecked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the preivous period checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousPeriodCheckBoxChange = R.curry((form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
|
||||||
|
form.setFieldValue('previousPeriod', isChecked);
|
||||||
|
|
||||||
|
if (!isChecked) {
|
||||||
|
form.setFieldValue('previousPeriodAmountChange', isChecked);
|
||||||
|
form.setFieldValue('previousPeriodPercentageChange', isChecked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles previous year change amount checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousYearChangeCheckboxChange = R.curry((form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
form.setFieldValue('previousYear', isChecked);
|
||||||
|
}
|
||||||
|
form.setFieldValue('previousYearAmountChange', isChecked);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle previous year percentage checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousYearPercentageCheckboxChange = R.curry(
|
||||||
|
(form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
form.setFieldValue('previousYear', isChecked);
|
||||||
|
}
|
||||||
|
form.setFieldValue('previousYearPercentageChange', isChecked);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles previous period change amout checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousPeriodChangeCheckboxChange = R.curry(
|
||||||
|
(form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
form.setFieldValue('previousPeriod', isChecked);
|
||||||
|
}
|
||||||
|
form.setFieldValue('previousPeriodAmountChange', isChecked);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles previous period percentage checkbox change.
|
||||||
|
*/
|
||||||
|
export const handlePreviousPeriodPercentageCheckboxChange = R.curry(
|
||||||
|
(form, event) => {
|
||||||
|
const isChecked = event.currentTarget.checked;
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
form.setFieldValue('previousPeriod', isChecked);
|
||||||
|
}
|
||||||
|
form.setFieldValue('previousPeriodPercentageChange', isChecked);
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import { DataTable } from 'components';
|
|
||||||
|
|
||||||
import { usePurchaseByItemsContext } from './PurchasesByItemsProvider';
|
import { usePurchaseByItemsContext } from './PurchasesByItemsProvider';
|
||||||
|
|
||||||
import { usePurchasesByItemsTableColumns } from './components';
|
import { usePurchasesByItemsTableColumns } from './components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import { DataTable } from 'components';
|
|
||||||
import { useSalesByItemsContext } from './SalesByItemProvider';
|
import { useSalesByItemsContext } from './SalesByItemProvider';
|
||||||
import { useSalesByItemsTableColumns } from './components';
|
import { useSalesByItemsTableColumns } from './components';
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import DataTable from 'components/DataTable';
|
|
||||||
|
|
||||||
import { useTrialBalanceSheetContext } from './TrialBalanceProvider';
|
import { useTrialBalanceSheetContext } from './TrialBalanceProvider';
|
||||||
|
|
||||||
|
|
||||||
import { useTrialBalanceTableColumns } from './components';
|
import { useTrialBalanceTableColumns } from './components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage as T } from 'components';
|
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { DataTable } from 'components';
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
|
|
||||||
import { useVendorsBalanceColumns } from './components';
|
import { useVendorsBalanceColumns } from './components';
|
||||||
import { useVendorsBalanceSummaryContext } from './VendorsBalanceSummaryProvider';
|
import { useVendorsBalanceSummaryContext } from './VendorsBalanceSummaryProvider';
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import FinancialSheet from 'components/FinancialSheet';
|
import { DataTable, FinancialSheet } from 'components';
|
||||||
import DataTable from 'components/DataTable';
|
|
||||||
import { useVendorsTransactionsColumns } from './components';
|
import { useVendorsTransactionsColumns } from './components';
|
||||||
import { useVendorsTransactionsContext } from './VendorsTransactionsProvider';
|
import { useVendorsTransactionsContext } from './VendorsTransactionsProvider';
|
||||||
|
|
||||||
|
|||||||
@@ -2,30 +2,6 @@ import React from 'react';
|
|||||||
import { chain } from 'lodash';
|
import { chain } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { FormattedMessage as T } from 'components';
|
import { FormattedMessage as T } from 'components';
|
||||||
import intl from 'react-intl-universal';
|
|
||||||
|
|
||||||
export const balanceSheetRowsReducer = (accounts) => {
|
|
||||||
return accounts.map((account) => {
|
|
||||||
return {
|
|
||||||
...account,
|
|
||||||
children: balanceSheetRowsReducer([
|
|
||||||
...(account.children ? account.children : []),
|
|
||||||
...(account.total && account.children && account.children.length > 0
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
name: intl.get('total_name', { name: account.name }),
|
|
||||||
row_types: ['total-row', account.section_type],
|
|
||||||
total: { ...account.total },
|
|
||||||
...(account.total_periods && {
|
|
||||||
total_periods: account.total_periods,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
]),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const trialBalanceSheetReducer = (sheet) => {
|
export const trialBalanceSheetReducer = (sheet) => {
|
||||||
const results = [];
|
const results = [];
|
||||||
@@ -44,124 +20,7 @@ export const trialBalanceSheetReducer = (sheet) => {
|
|||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const profitLossSheetReducer = (profitLoss) => {
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
if (profitLoss.income) {
|
|
||||||
results.push({
|
|
||||||
name: <T id={'income'} />,
|
|
||||||
total: profitLoss.income.total,
|
|
||||||
children: [
|
|
||||||
...profitLoss.income.accounts,
|
|
||||||
{
|
|
||||||
name: <T id={'total_income'} />,
|
|
||||||
total: profitLoss.income.total,
|
|
||||||
total_periods: profitLoss.income.total_periods,
|
|
||||||
rowTypes: ['income_total', 'section_total', 'total'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
total_periods: profitLoss.income.total_periods,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (profitLoss.cost_of_sales) {
|
|
||||||
results.push({
|
|
||||||
name: <T id={'cost_of_sales'} />,
|
|
||||||
total: profitLoss.cost_of_sales.total,
|
|
||||||
children: [
|
|
||||||
...profitLoss.cost_of_sales.accounts,
|
|
||||||
{
|
|
||||||
name: <T id={'total_cost_of_sales'} />,
|
|
||||||
total: profitLoss.cost_of_sales.total,
|
|
||||||
total_periods: profitLoss.cost_of_sales.total_periods,
|
|
||||||
rowTypes: ['cogs_total', 'section_total', 'total'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
total_periods: profitLoss.cost_of_sales.total_periods,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (profitLoss.gross_profit) {
|
|
||||||
results.push({
|
|
||||||
name: <T id={'gross_profit'} />,
|
|
||||||
total: profitLoss.gross_profit.total,
|
|
||||||
total_periods: profitLoss.gross_profit.total_periods,
|
|
||||||
rowTypes: ['gross_total', 'section_total', 'total'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (profitLoss.expenses) {
|
|
||||||
results.push({
|
|
||||||
name: <T id={'expenses'} />,
|
|
||||||
total: profitLoss.expenses.total,
|
|
||||||
children: [
|
|
||||||
...profitLoss.expenses.accounts,
|
|
||||||
{
|
|
||||||
name: <T id={'total_expenses'} />,
|
|
||||||
total: profitLoss.expenses.total,
|
|
||||||
total_periods: profitLoss.expenses.total_periods,
|
|
||||||
rowTypes: ['expenses_total', 'section_total', 'total'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
total_periods: profitLoss.expenses.total_periods,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (profitLoss.operating_profit) {
|
|
||||||
results.push({
|
|
||||||
name: <T id={'net_operating_income'} />,
|
|
||||||
total: profitLoss.operating_profit.total,
|
|
||||||
total_periods: profitLoss.income.total_periods,
|
|
||||||
rowTypes: ['net_operating_total', 'section_total', 'total'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (profitLoss.other_income) {
|
|
||||||
results.push({
|
|
||||||
|
|
||||||
name:<T id={'other_income'}/>,
|
|
||||||
total: profitLoss.other_income.total,
|
|
||||||
total_periods: profitLoss.other_income.total_periods,
|
|
||||||
children: [
|
|
||||||
...profitLoss.other_income.accounts,
|
|
||||||
{
|
|
||||||
name: <T id={'total_other_income'} />,
|
|
||||||
total: profitLoss.other_income.total,
|
|
||||||
total_periods: profitLoss.other_income.total_periods,
|
|
||||||
rowTypes: ['expenses_total', 'section_total', 'total'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (profitLoss.other_expenses) {
|
|
||||||
results.push({
|
|
||||||
name: <T id={'other_expenses'} />,
|
|
||||||
total: profitLoss.other_expenses.total,
|
|
||||||
total_periods: profitLoss.other_expenses.total_periods,
|
|
||||||
children: [
|
|
||||||
...profitLoss.other_expenses.accounts,
|
|
||||||
{
|
|
||||||
name: <T id={'total_other_expenses'} />,
|
|
||||||
total: profitLoss.other_expenses.total,
|
|
||||||
total_periods: profitLoss.other_expenses.total_periods,
|
|
||||||
rowTypes: ['expenses_total', 'section_total', 'total'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (profitLoss.net_other_income) {
|
|
||||||
results.push({
|
|
||||||
name: <T id={'net_other_income'} />,
|
|
||||||
total: profitLoss.net_other_income.total,
|
|
||||||
total_periods: profitLoss.net_other_income.total_periods,
|
|
||||||
rowTypes: ['net_other_income', 'section_total', 'total'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (profitLoss.net_income) {
|
|
||||||
results.push({
|
|
||||||
name: <T id={'net_income'} />,
|
|
||||||
total: profitLoss.net_income.total,
|
|
||||||
total_periods: profitLoss.net_income.total_periods,
|
|
||||||
rowTypes: ['net_income_total', 'section_total', 'total'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const journalTableRowsReducer = (journal) => {
|
export const journalTableRowsReducer = (journal) => {
|
||||||
const TYPES = {
|
const TYPES = {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { useRef, useEffect, useMemo } from 'react';
|
import { useRef, useEffect, useMemo } from 'react';
|
||||||
|
import { useLocation, useHistory } from 'react-router';
|
||||||
import useAutofocus from './useAutofocus';
|
import useAutofocus from './useAutofocus';
|
||||||
import { useLocalStorage } from './utils/useLocalStorage';
|
import { useLocalStorage } from './utils/useLocalStorage';
|
||||||
|
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
export * from './useQueryString';
|
||||||
|
|
||||||
export function useIsValuePassed(value, compatatorValue) {
|
export function useIsValuePassed(value, compatatorValue) {
|
||||||
const cache = useRef([value]);
|
const cache = useRef([value]);
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { useRequestQuery } from '../useQueryRequest';
|
import { useRequestQuery } from '../useQueryRequest';
|
||||||
import {
|
import {
|
||||||
trialBalanceSheetReducer,
|
trialBalanceSheetReducer,
|
||||||
balanceSheetRowsReducer,
|
|
||||||
profitLossSheetReducer,
|
|
||||||
generalLedgerTableRowsReducer,
|
generalLedgerTableRowsReducer,
|
||||||
journalTableRowsReducer,
|
journalTableRowsReducer,
|
||||||
ARAgingSummaryTableRowsMapper,
|
ARAgingSummaryTableRowsMapper,
|
||||||
@@ -23,18 +21,12 @@ export function useBalanceSheet(query, props) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/financial_statements/balance_sheet',
|
url: '/financial_statements/balance_sheet',
|
||||||
params: query,
|
params: query,
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json+table',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
select: (res) => ({
|
select: (res) => res.data,
|
||||||
tableRows: balanceSheetRowsReducer(res.data.data),
|
|
||||||
...res.data,
|
|
||||||
}),
|
|
||||||
defaultData: {
|
|
||||||
data: [],
|
|
||||||
columns: [],
|
|
||||||
query: {},
|
|
||||||
tableRows: [],
|
|
||||||
},
|
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -76,18 +68,12 @@ export function useProfitLossSheet(query, props) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/financial_statements/profit_loss_sheet',
|
url: '/financial_statements/profit_loss_sheet',
|
||||||
params: query,
|
params: query,
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json+table',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
select: (res) => ({
|
select: (res) => res.data,
|
||||||
tableRows: profitLossSheetReducer(res.data.data),
|
|
||||||
...res.data,
|
|
||||||
}),
|
|
||||||
defaultData: {
|
|
||||||
data: {},
|
|
||||||
tableRows: [],
|
|
||||||
columns: [],
|
|
||||||
query: {},
|
|
||||||
},
|
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
89
src/hooks/useQueryString.ts
Normal file
89
src/hooks/useQueryString.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
|
||||||
|
import {
|
||||||
|
ParseOptions,
|
||||||
|
ParsedQuery,
|
||||||
|
StringifyOptions,
|
||||||
|
parse,
|
||||||
|
stringify,
|
||||||
|
} from 'query-string';
|
||||||
|
import { useHistory } from 'react-router';
|
||||||
|
|
||||||
|
export interface QueryStringResult {
|
||||||
|
[0]: ParsedQuery;
|
||||||
|
[1]: Dispatch<SetStateAction<Record<string, any>>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type NavigateCallback = (
|
||||||
|
pathnameWithParams: string,
|
||||||
|
pathname: string,
|
||||||
|
stringifedParams: string,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query string.
|
||||||
|
* @param {Location} location
|
||||||
|
* @param {NavigateCallback} navigate
|
||||||
|
* @param {ParseOptions} parseOptions
|
||||||
|
* @param {StringifyOptions} stringifyOptions
|
||||||
|
* @returns {QueryStringResult}
|
||||||
|
*/
|
||||||
|
export function useQueryString(
|
||||||
|
location: Location,
|
||||||
|
navigate: NavigateCallback,
|
||||||
|
parseOptions?: ParseOptions,
|
||||||
|
stringifyOptions?: StringifyOptions,
|
||||||
|
): QueryStringResult {
|
||||||
|
const isFirst = useRef(true);
|
||||||
|
const [state, setState] = useState(parse(location.search, parseOptions));
|
||||||
|
|
||||||
|
useEffect((): void => {
|
||||||
|
if (isFirst.current) {
|
||||||
|
isFirst.current = false;
|
||||||
|
} else {
|
||||||
|
const pathname = location.pathname;
|
||||||
|
const stringifedParams = stringify(state, stringifyOptions);
|
||||||
|
const pathnameWithParams = pathname + '?' + stringifedParams;
|
||||||
|
|
||||||
|
navigate(pathnameWithParams, pathname, stringifedParams);
|
||||||
|
}
|
||||||
|
}, [state]);
|
||||||
|
|
||||||
|
const setQuery: typeof setState = (values): void => {
|
||||||
|
const nextState = typeof values === 'function' ? values(state) : values;
|
||||||
|
setState(
|
||||||
|
(state): ParsedQuery => ({
|
||||||
|
...state,
|
||||||
|
...nextState,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return [state, setQuery];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query string hook integrate with react router of the application.
|
||||||
|
* @param {NavigateCallback} navigate
|
||||||
|
* @param {ParseOptions} parseOptions
|
||||||
|
* @returns {QueryStringResult}
|
||||||
|
*/
|
||||||
|
export const useAppQueryString = (
|
||||||
|
navigate: NavigateCallback,
|
||||||
|
parseOptions: ParseOptions = {},
|
||||||
|
): QueryStringResult => {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
return useQueryString(
|
||||||
|
window.location,
|
||||||
|
(pathnameWithParams, pathname, stringifiedParams) => {
|
||||||
|
history.push({ pathname, search: stringifiedParams });
|
||||||
|
|
||||||
|
navigate && navigate(pathnameWithParams, pathname, stringifiedParams);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parseNumbers: true,
|
||||||
|
parseBooleans: true,
|
||||||
|
...parseOptions,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1754,21 +1754,33 @@
|
|||||||
"payment_made.drawer.title": "تفاصيل سند المورد {number}",
|
"payment_made.drawer.title": "تفاصيل سند المورد {number}",
|
||||||
"manual_journal.drawer.title": "تفاصيل قيد يدوي ({number})",
|
"manual_journal.drawer.title": "تفاصيل قيد يدوي ({number})",
|
||||||
"expense.drawer.title": " تفاصيل المصروف",
|
"expense.drawer.title": " تفاصيل المصروف",
|
||||||
|
|
||||||
"global_error.you_dont_have_permissions": "ليس لديك صلاحية الوصول إلى هذه الصفحة.",
|
"global_error.you_dont_have_permissions": "ليس لديك صلاحية الوصول إلى هذه الصفحة.",
|
||||||
"global_error.transactions_locked": "تم قفل المعاملات التي تمت قبل {lockedToDate}. ومن ثم لا يمكن القيام بأي عمل.",
|
"global_error.transactions_locked": "تم قفل المعاملات التي تمت قبل {lockedToDate}. ومن ثم لا يمكن القيام بأي عمل.",
|
||||||
"global_error.authorized_user_inactive": "المستخدم المصرح له تم تعطيلة.",
|
"global_error.authorized_user_inactive": "المستخدم المصرح له تم تعطيلة.",
|
||||||
|
|
||||||
"vendor.alert.activated_message": "تم تفعيل المورد بنجاح.",
|
"vendor.alert.activated_message": "تم تفعيل المورد بنجاح.",
|
||||||
"vendor.alert.are_you_sure_want_to_activate_this_vendor": "هل أنت متأكد أنك تريد تفعيل هذا المورد؟ ستتمكن من تعطيله لاحقًا",
|
"vendor.alert.are_you_sure_want_to_activate_this_vendor": "هل أنت متأكد أنك تريد تفعيل هذا المورد؟ ستتمكن من تعطيله لاحقًا",
|
||||||
"vendor.alert.inactivated_message": "تم إلغاء تنشيط المورد بنجاح.",
|
"vendor.alert.inactivated_message": "تم إلغاء تنشيط المورد بنجاح.",
|
||||||
"vendor.alert.are_you_sure_want_to_inactivate_this_vendor":"هل أنت متأكد أنك تريد إلغاء تنشيط هذا المورد؟ ستكون قادرًا على تنشيطه لاحقًا",
|
"vendor.alert.are_you_sure_want_to_inactivate_this_vendor": "هل أنت متأكد أنك تريد إلغاء تنشيط هذا المورد؟ ستكون قادرًا على تنشيطه لاحقًا",
|
||||||
|
"customer.alert.activated_message": "تم تفعيل الزبون بنجاح.",
|
||||||
"customer.alert.activated_message":"تم تفعيل الزبون بنجاح.",
|
|
||||||
"customer.alert.are_you_sure_want_to_activate_this_customer": "هل أنت متأكد أنك تريد تفعيل هذا الزبون؟ ستتمكن من تعطيله لاحقًا",
|
"customer.alert.are_you_sure_want_to_activate_this_customer": "هل أنت متأكد أنك تريد تفعيل هذا الزبون؟ ستتمكن من تعطيله لاحقًا",
|
||||||
"customer.alert.inactivated_message": "تم إلغاء تنشيط الزبون بنجاح.",
|
"customer.alert.inactivated_message": "تم إلغاء تنشيط الزبون بنجاح.",
|
||||||
"customer.alert.are_you_sure_want_to_inactivate_this_customer":"هل أنت متأكد أنك تريد إلغاء تنشيط هذا الزبون؟ ستكون قادرًا على تنشيطه لاحقًا",
|
"customer.alert.are_you_sure_want_to_inactivate_this_customer": "هل أنت متأكد أنك تريد إلغاء تنشيط هذا الزبون؟ ستكون قادرًا على تنشيطه لاحقًا",
|
||||||
|
"credit_note_preview.dialog.title": "معاينة إشعار الدائن PDF",
|
||||||
"credit_note_preview.dialog.title":"معاينة إشعار الدائن PDF",
|
"payment_receive_preview.dialog.title": "معاينة سند الزبون PDF",
|
||||||
"payment_receive_preview.dialog.title":"معاينة سند الزبون PDF"
|
"balance_sheet.comparisons": "مقارنات",
|
||||||
|
"balance_sheet.percentage_of_column": "% التغير العمودي",
|
||||||
|
"balance_sheet.percentage_of_row": "% التغير الأفقي",
|
||||||
|
"balance_sheet.previous_year": "السنة السابقة (س.س)",
|
||||||
|
"balance_sheet.total_change": "إجمالي التغيرات",
|
||||||
|
"balance_sheet.change": "% التغيرات",
|
||||||
|
"balance_sheet.previous_period": "الفترة السابقة (ف.س) ",
|
||||||
|
"profit_loss_sheet.comparisons": "مقارنات",
|
||||||
|
"profit_loss_sheet.previous_year": "السنة السابقة (س.س)",
|
||||||
|
"profit_loss_sheet.total_change": "إجمالي التغيرات",
|
||||||
|
"profit_loss_sheet.perentage_change": "% التغيرات",
|
||||||
|
"profit_loss_sheet.previous_period": "الفترة السابقة (ف.س) ",
|
||||||
|
"profit_loss_sheet.percentage_of_column": "% التغير العمودي",
|
||||||
|
"profit_loss_sheet.percentage_of_row": "% التغير الأفقي",
|
||||||
|
"profit_loss_sheet.percentage_of_expense": "% التغير في المصاريف",
|
||||||
|
"profit_loss_sheet.percentage_of_income": "% التغير الإيرادات"
|
||||||
}
|
}
|
||||||
@@ -1739,18 +1739,35 @@
|
|||||||
"global_error.transactions_locked": "Transactions before {lockedToDate} has been locked. Hence action cannot be performed.",
|
"global_error.transactions_locked": "Transactions before {lockedToDate} has been locked. Hence action cannot be performed.",
|
||||||
"global_error.authorized_user_inactive": "The authorized user is inactive.",
|
"global_error.authorized_user_inactive": "The authorized user is inactive.",
|
||||||
|
|
||||||
|
"balance_sheet.comparisons": "Comparisons",
|
||||||
|
"balance_sheet.percentage_of_column": "% of Columns",
|
||||||
|
"balance_sheet.percentage_of_row": "% of Rows",
|
||||||
|
"balance_sheet.previous_year": "Previous Year (PY)",
|
||||||
|
"balance_sheet.total_change": "Total Change",
|
||||||
|
"balance_sheet.change": "% Change",
|
||||||
|
"balance_sheet.previous_period": "Previous Period (PP)",
|
||||||
|
"profit_loss_sheet.comparisons": "Comparisons",
|
||||||
|
"profit_loss_sheet.previous_year": "Previous Year",
|
||||||
|
"profit_loss_sheet.total_change": "Total Change",
|
||||||
|
"profit_loss_sheet.perentage_change": "Perentage Change",
|
||||||
|
"profit_loss_sheet.previous_period": "Previous Period (PP)",
|
||||||
|
"profit_loss_sheet.percentage_of_column": "% of Column",
|
||||||
|
"profit_loss_sheet.percentage_of_row": "% of Row",
|
||||||
|
"profit_loss_sheet.percentage_of_expense": "% of Expense",
|
||||||
|
"profit_loss_sheet.percentage_of_income": "% of Income",
|
||||||
|
|
||||||
"the_vendor_has_been_inactivated_successfully": "The contact has been inactivated successfully.",
|
"the_vendor_has_been_inactivated_successfully": "The contact has been inactivated successfully.",
|
||||||
|
|
||||||
"vendor.alert.activated_message": "The vendor has been activated successfully.",
|
"vendor.alert.activated_message": "The vendor has been activated successfully.",
|
||||||
"vendor.alert.are_you_sure_want_to_inactivate_this_vendor":"Are you sure want to inactivate this vendor? You will to able to activate it later.",
|
"vendor.alert.are_you_sure_want_to_inactivate_this_vendor": "Are you sure want to inactivate this vendor? You will to able to activate it later.",
|
||||||
"vendor.alert.inactivated_message": "The vendor has been inactivated successfully.",
|
"vendor.alert.inactivated_message": "The vendor has been inactivated successfully.",
|
||||||
"vendor.alert.are_you_sure_want_to_activate_this_vendor": "Are you sure want to activate this vendor? You will to able to inactivate it later.",
|
"vendor.alert.are_you_sure_want_to_activate_this_vendor": "Are you sure want to activate this vendor? You will to able to inactivate it later.",
|
||||||
|
|
||||||
"customer.alert.activated_message":"The customer has been activated successfully.",
|
"customer.alert.activated_message": "The customer has been activated successfully.",
|
||||||
"customer.alert.are_you_sure_want_to_activate_this_customer": "Are you sure want to activate this customer? You will to able to inactivate it later.",
|
"customer.alert.are_you_sure_want_to_activate_this_customer": "Are you sure want to activate this customer? You will to able to inactivate it later.",
|
||||||
"customer.alert.inactivated_message": "The customer has been inactivated successfully.",
|
"customer.alert.inactivated_message": "The customer has been inactivated successfully.",
|
||||||
"customer.alert.are_you_sure_want_to_inactivate_this_customer":"Are you sure want to inactivate this customer? You will to able to activate it later.",
|
"customer.alert.are_you_sure_want_to_inactivate_this_customer": "Are you sure want to inactivate this customer? You will to able to activate it later.",
|
||||||
|
|
||||||
"credit_note_preview.dialog.title":"Credit Note PDF Preview",
|
"credit_note_preview.dialog.title": "Credit Note PDF Preview",
|
||||||
"payment_receive_preview.dialog.title":"Payment Receive PDF Preview"
|
"payment_receive_preview.dialog.title": "Payment Receive PDF Preview"
|
||||||
}
|
}
|
||||||
@@ -285,6 +285,9 @@ html[lang^="ar"] {
|
|||||||
.align-right {
|
.align-right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.align-center{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.font-bold {
|
.font-bold {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -9,6 +9,11 @@
|
|||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
|
.thead .thead-inner,
|
||||||
|
.tbody .tbody-inner{
|
||||||
|
min-width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
.thead {
|
.thead {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
@@ -147,6 +152,19 @@
|
|||||||
width: 65%;
|
width: 65%;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.align-right {
|
||||||
|
.skeleton {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.align-center {
|
||||||
|
.skeleton {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.th {
|
.th {
|
||||||
@@ -340,11 +358,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-sticky-last-left-td] {
|
[data-sticky-last-left-td] {
|
||||||
// box-shadow: 2px 0px 3px #ccc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-sticky-first-right-td] {
|
[data-sticky-first-right-td] {
|
||||||
// box-shadow: -2px 0px 3px #ccc;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,18 +381,24 @@
|
|||||||
.table-constrant,
|
.table-constrant,
|
||||||
.table--constrant {
|
.table--constrant {
|
||||||
.table {
|
.table {
|
||||||
|
.thead {
|
||||||
|
.tr:first-of-type .th {
|
||||||
|
border-top: 1px solid #000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.thead .th {
|
.thead .th {
|
||||||
background: transparent;
|
background: #fff;
|
||||||
color: #222222;
|
color: #222222;
|
||||||
border-bottom: 1px solid #000000;
|
border-bottom: 1px solid #000000;
|
||||||
border-top: 1px solid #000000;
|
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tbody .tr .td {
|
.tbody .tr .td {
|
||||||
background: transparent;
|
background: #fff;
|
||||||
padding: 0.5rem 0.5rem;
|
padding: 0.5rem 0.5rem;
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
|
color: #000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
// Financial sheet.
|
|
||||||
// -----------------------
|
|
||||||
.financial-sheet {
|
|
||||||
border: 2px solid #f0f0f0;
|
|
||||||
border-radius: 10px;
|
|
||||||
min-width: 640px;
|
|
||||||
width: auto;
|
|
||||||
padding: 30px 18px;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 35px auto;
|
|
||||||
min-height: 400px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background: #fff;
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #464646;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
&__sheet-type {
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #666;
|
|
||||||
margin-top: 6px;
|
|
||||||
}
|
|
||||||
&__date {
|
|
||||||
text-align: center;
|
|
||||||
color: #666;
|
|
||||||
margin-top: 6px;
|
|
||||||
}
|
|
||||||
&__table {
|
|
||||||
margin-top: 24px;
|
|
||||||
|
|
||||||
.table {
|
|
||||||
.tbody,
|
|
||||||
.thead {
|
|
||||||
.tr .td,
|
|
||||||
.tr .th {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tr.no-results {
|
|
||||||
.td {
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 20px;
|
|
||||||
color: #666;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__inner {
|
|
||||||
&.is-loading {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__footer {
|
|
||||||
color: #888;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: auto;
|
|
||||||
padding-top: 18px;
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
|
|
||||||
> span + span{
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dashboard__loading-indicator {
|
|
||||||
margin: auto;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
&--expended {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--minimal {
|
|
||||||
border: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin-top: 20px;
|
|
||||||
|
|
||||||
.financial-sheet {
|
|
||||||
&__title {
|
|
||||||
font-size: 18px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
&__title + .financial-sheet__date {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
&__table {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.is-full-width {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 25px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.financial-statement {
|
|
||||||
&__header {
|
|
||||||
}
|
|
||||||
|
|
||||||
&__body {
|
|
||||||
padding-left: 15px;
|
|
||||||
padding-right: 15px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
|
|
||||||
.financial-sheet{
|
|
||||||
|
|
||||||
&--balance-sheet{
|
|
||||||
.financial-sheet__table{
|
|
||||||
|
|
||||||
.thead,
|
|
||||||
.tbody{
|
|
||||||
.tr .td.account_name ~ .td,
|
|
||||||
.tr .th.account_name ~ .th{
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tbody{
|
|
||||||
.tr .td{
|
|
||||||
border-bottom: 0;
|
|
||||||
padding-top: 0.4rem;
|
|
||||||
padding-bottom: 0.4rem;
|
|
||||||
}
|
|
||||||
.tr.row_type--total-row .td{
|
|
||||||
border-top: 1px solid #BBB;
|
|
||||||
}
|
|
||||||
.tr.row_type--total-row.row_type--assets .td,
|
|
||||||
.tr.row_type--total-row.row_type--liabilities_equity .td{
|
|
||||||
border-bottom: 3px double #333;
|
|
||||||
}
|
|
||||||
.tr.row_type--total-row{
|
|
||||||
.total.td,
|
|
||||||
.account_name.td,
|
|
||||||
.total-period.td{
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tr.is-expanded{
|
|
||||||
.td.total,
|
|
||||||
.td.total-period{
|
|
||||||
.cell-text{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,35 +4,8 @@
|
|||||||
.dashboard__insider--financial-report{
|
.dashboard__insider--financial-report{
|
||||||
|
|
||||||
.alert-compute-running{
|
.alert-compute-running{
|
||||||
position: relative;
|
|
||||||
padding: 8px 20px;
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: #fdecda;
|
|
||||||
color: #342515;
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
button{
|
|
||||||
font-size: 12px;
|
|
||||||
min-height: 16px;
|
|
||||||
padding: 0 4px;
|
|
||||||
|
|
||||||
&,
|
|
||||||
&:hover{
|
|
||||||
color: #824400;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
svg{
|
|
||||||
margin-right: 6px;
|
|
||||||
position: relative;
|
|
||||||
top: -2px;
|
|
||||||
fill: #975f19;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.financial-progressbar{
|
|
||||||
.progress-materializecss{
|
|
||||||
top: -2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
.bigcapital-datatable{
|
|
||||||
|
|
||||||
&--financial-report{
|
|
||||||
.table {
|
|
||||||
.tbody{
|
|
||||||
|
|
||||||
.tr.no-results {
|
|
||||||
.td{
|
|
||||||
border-bottom: 1px solid #DDD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.thead{
|
|
||||||
.tr .th{
|
|
||||||
background-color: #fff;
|
|
||||||
border-top: 1px solid #666;
|
|
||||||
border-bottom: 1px solid #666;
|
|
||||||
|
|
||||||
padding: 8px 0.4rem;
|
|
||||||
color: #222;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
|
|
||||||
.financial-sheet{
|
|
||||||
&--profit-loss-sheet{
|
|
||||||
.financial-sheet__table{
|
|
||||||
.thead,
|
|
||||||
.tbody{
|
|
||||||
.tr .td:not(:first-child),
|
|
||||||
.tr .th:not(:first-child) {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tbody{
|
|
||||||
.tr .td{
|
|
||||||
border-bottom: 0;
|
|
||||||
padding-top: 0.4rem;
|
|
||||||
padding-bottom: 0.4rem;
|
|
||||||
}
|
|
||||||
.tr.row_type--total{
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
.tr.row_type--section_total .td{
|
|
||||||
border-top: 1px solid #BBB
|
|
||||||
}
|
|
||||||
.tr.row_type--section_total + .tr .td{
|
|
||||||
border-top: 1px solid #666;
|
|
||||||
}
|
|
||||||
.tr.row_type--net_income_total.row_type--total .td{
|
|
||||||
border-bottom: 3px double #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tr.is-expanded{
|
|
||||||
.td.total,
|
|
||||||
.td.total-period{
|
|
||||||
.cell-text{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ import moment from 'moment';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import Currencies from 'js-money/lib/currency';
|
import Currencies from 'js-money/lib/currency';
|
||||||
|
import clsx from 'classnames';
|
||||||
|
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import Currency from 'js-money/lib/currency';
|
import Currency from 'js-money/lib/currency';
|
||||||
@@ -439,9 +440,11 @@ export const getColumnWidth = (
|
|||||||
rows,
|
rows,
|
||||||
accessor,
|
accessor,
|
||||||
{ maxWidth, minWidth, magicSpacing = 14 },
|
{ maxWidth, minWidth, magicSpacing = 14 },
|
||||||
|
headerText = '',
|
||||||
) => {
|
) => {
|
||||||
const cellLength = Math.max(
|
const cellLength = Math.max(
|
||||||
...rows.map((row) => (`${_.get(row, accessor)}` || '').length),
|
...rows.map((row) => (`${_.get(row, accessor)}` || '').length),
|
||||||
|
headerText.length,
|
||||||
);
|
);
|
||||||
let result = cellLength * magicSpacing;
|
let result = cellLength * magicSpacing;
|
||||||
|
|
||||||
@@ -900,3 +903,13 @@ export function ignoreEventFromSelectors(event, selectors) {
|
|||||||
.map((selector) => event.target.closest(selector))
|
.map((selector) => event.target.closest(selector))
|
||||||
.some((element) => !!element);
|
.some((element) => !!element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const tableRowTypesToClassnames = ({ original }) => {
|
||||||
|
const rowTypes = _.castArray(original.row_types);
|
||||||
|
const rowId = original.id;
|
||||||
|
|
||||||
|
const rowTypesClsx = rowTypes.map((t) => `row_type--${t}`);
|
||||||
|
const rowIdClsx = `row-id--${original.id}`;
|
||||||
|
|
||||||
|
return clsx(rowTypesClsx, { [`${rowIdClsx}`]: rowId });
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user