mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
feat: add warehouse transfer & expenses & journal.
This commit is contained in:
@@ -1,41 +1,30 @@
|
||||
import React from 'react';
|
||||
import { FastField } from 'formik';
|
||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import classNames from 'classnames';
|
||||
import { inputIntent } from 'utils';
|
||||
import { Row, Dragzone, Col, Postbox } from 'components';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { Row, Col, Paper } from 'components';
|
||||
import { ExpenseFormFooterLeft } from './ExpenseFormFooterLeft';
|
||||
import { ExpenseFormFooterRight } from './ExpenseFormFooterRight';
|
||||
|
||||
export default function ExpenseFormFooter() {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Postbox title={<T id={'expense_details'} />} defaultOpen={false}>
|
||||
<ExpensesFooterPaper>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
className={'form-group--description'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<ExpenseFormFooterLeft />
|
||||
</Col>
|
||||
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={<T id={'attachments_maximum'} />}
|
||||
/>
|
||||
<ExpenseFormFooterRight />
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</ExpensesFooterPaper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const ExpensesFooterPaper = styled(Paper)`
|
||||
padding: 20px;
|
||||
`;
|
||||
|
||||
33
src/containers/Expenses/ExpenseForm/ExpenseFormFooterLeft.js
Normal file
33
src/containers/Expenses/ExpenseForm/ExpenseFormFooterLeft.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { FFormGroup, FEditableText, FormattedMessage as T } from 'components';
|
||||
|
||||
export function ExpenseFormFooterLeft() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{/* --------- Description --------- */}
|
||||
<DescriptionFormGroup
|
||||
label={<T id={'description'} />}
|
||||
name={'description'}
|
||||
>
|
||||
<FEditableText
|
||||
name={'description'}
|
||||
placeholder={
|
||||
'Enter the description of your business to be displayed in your transaction'
|
||||
}
|
||||
/>
|
||||
</DescriptionFormGroup>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
const DescriptionFormGroup = styled(FFormGroup)`
|
||||
&.bp3-form-group {
|
||||
.bp3-label {
|
||||
font-size: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.bp3-form-content {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
T,
|
||||
TotalLines,
|
||||
TotalLine,
|
||||
TotalLineBorderStyle,
|
||||
TotalLineTextStyle,
|
||||
} from 'components';
|
||||
import { useExpensesTotals } from './utils';
|
||||
|
||||
export function ExpenseFormFooterRight() {
|
||||
const { formattedSubtotal, formattedTotal } = useExpensesTotals();
|
||||
|
||||
return (
|
||||
<ExpensesTotalLines>
|
||||
<TotalLine
|
||||
title={<T id={'manual_journal.details.subtotal'} />}
|
||||
value={formattedSubtotal}
|
||||
borderStyle={TotalLineBorderStyle.None}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'manual_journal.details.total'} />}
|
||||
value={formattedTotal}
|
||||
// borderStyle={TotalLineBorderStyle.SingleDark}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
</ExpensesTotalLines>
|
||||
);
|
||||
}
|
||||
|
||||
const ExpensesTotalLines = styled(TotalLines)`
|
||||
width: 100%;
|
||||
color: #555555;
|
||||
`;
|
||||
@@ -23,10 +23,9 @@ import {
|
||||
CustomerSelectField,
|
||||
AccountsSelectList,
|
||||
FieldRequiredHint,
|
||||
ExchangeRateInputGroup,
|
||||
Hint,
|
||||
If,
|
||||
} from 'components';
|
||||
import { ExpensesExchangeRateInputField } from './components';
|
||||
import { ACCOUNT_PARENT_TYPE } from 'common/accountTypes';
|
||||
import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||
|
||||
@@ -34,15 +33,7 @@ import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||
* Expense form header.
|
||||
*/
|
||||
export default function ExpenseFormHeader() {
|
||||
const {
|
||||
currencies,
|
||||
accounts,
|
||||
customers,
|
||||
isForeignCustomer,
|
||||
baseCurrency,
|
||||
selectCustomer,
|
||||
setSelectCustomer,
|
||||
} = useExpenseFormContext();
|
||||
const { currencies, accounts, customers } = useExpenseFormContext();
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
@@ -118,7 +109,6 @@ export default function ExpenseFormHeader() {
|
||||
selectedCurrencyCode={value}
|
||||
onCurrencySelected={(currencyItem) => {
|
||||
form.setFieldValue('currency_code', currencyItem.currency_code);
|
||||
setSelectCustomer(currencyItem);
|
||||
}}
|
||||
defaultSelectText={value}
|
||||
/>
|
||||
@@ -126,14 +116,11 @@ export default function ExpenseFormHeader() {
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<If condition={isForeignCustomer}>
|
||||
<ExchangeRateInputGroup
|
||||
fromCurrency={baseCurrency}
|
||||
toCurrency={selectCustomer?.currency_code}
|
||||
name={'exchange_rate'}
|
||||
formGroupProps={{ label: ' ', inline: true }}
|
||||
/>
|
||||
</If>
|
||||
{/* ----------- Exchange rate ----------- */}
|
||||
<ExpensesExchangeRateInputField
|
||||
name={'exchange_rate'}
|
||||
formGroupProps={{ label: ' ', inline: true }}
|
||||
/>
|
||||
|
||||
<FastField name={'reference_no'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
|
||||
@@ -9,7 +9,10 @@ import {
|
||||
AccountsListFieldCell,
|
||||
CheckBoxFieldCell,
|
||||
} from 'components/DataTableCells';
|
||||
import { formattedAmount, safeSumBy } from 'utils';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { ExchangeRateInputGroup } from 'components';
|
||||
import { useCurrentOrganization } from 'hooks/state';
|
||||
import { useExpensesIsForeign } from './utils';
|
||||
|
||||
/**
|
||||
* Expense category header cell.
|
||||
@@ -128,3 +131,27 @@ export function useExpenseFormTableColumns({ landedCost }) {
|
||||
[],
|
||||
);
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
* Expense exchange rate input field.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export function ExpensesExchangeRateInputField({ ...props }) {
|
||||
const currentOrganization = useCurrentOrganization();
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const isForeignJouranl = useExpensesIsForeign();
|
||||
|
||||
// Can't continue if the customer is not foreign.
|
||||
if (!isForeignJouranl) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<ExchangeRateInputGroup
|
||||
fromCurrency={values.currency_code}
|
||||
toCurrency={currentOrganization.base_currency}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useFormikContext } from 'formik';
|
||||
import moment from 'moment';
|
||||
import intl from 'react-intl-universal';
|
||||
import * as R from 'ramda';
|
||||
import { first } from 'lodash';
|
||||
import { first, sumBy } from 'lodash';
|
||||
import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||
|
||||
import {
|
||||
@@ -14,7 +14,9 @@ import {
|
||||
repeatValue,
|
||||
ensureEntriesHasEmptyLine,
|
||||
orderingLinesIndexes,
|
||||
formattedAmount,
|
||||
} from 'utils';
|
||||
import { useCurrentOrganization } from 'hooks/state';
|
||||
|
||||
const ERROR = {
|
||||
EXPENSE_ALREADY_PUBLISHED: 'EXPENSE.ALREADY.PUBLISHED',
|
||||
@@ -150,3 +152,45 @@ export const useSetPrimaryBranchToForm = () => {
|
||||
}
|
||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retreives the Journal totals.
|
||||
*/
|
||||
export const useExpensesTotals = () => {
|
||||
const {
|
||||
values: { categories, currency_code: currencyCode },
|
||||
} = useFormikContext();
|
||||
|
||||
const total = sumBy(categories, 'amount');
|
||||
|
||||
// Retrieves the formatted total money.
|
||||
const formattedTotal = React.useMemo(
|
||||
() => formattedAmount(total, currencyCode),
|
||||
[total, currencyCode],
|
||||
);
|
||||
// Retrieves the formatted subtotal.
|
||||
const formattedSubtotal = React.useMemo(
|
||||
() => formattedAmount(total, currencyCode, { money: false }),
|
||||
[total, currencyCode],
|
||||
);
|
||||
|
||||
return {
|
||||
formattedTotal,
|
||||
formattedSubtotal,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines whether the expenses has foreign .
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const useExpensesIsForeign = () => {
|
||||
const { values } = useFormikContext();
|
||||
const currentOrganization = useCurrentOrganization();
|
||||
|
||||
const isForeignExpenses = React.useMemo(
|
||||
() => values.currency_code !== currentOrganization.base_currency,
|
||||
[values.currency_code, currentOrganization.base_currency],
|
||||
);
|
||||
return isForeignExpenses;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user