Merge branch 'feature/multi-dimensions' of https://github.com/bigcapitalhq/client into feature/multi-dimensions

This commit is contained in:
a.bouhuolia
2022-02-20 17:08:36 +02:00
35 changed files with 1184 additions and 28 deletions

View File

@@ -12,6 +12,7 @@ import BillFormHeader from './BillFormHeader';
import BillFloatingActions from './BillFloatingActions'; import BillFloatingActions from './BillFloatingActions';
import BillFormFooter from './BillFormFooter'; import BillFormFooter from './BillFormFooter';
import BillItemsEntriesEditor from './BillItemsEntriesEditor'; import BillItemsEntriesEditor from './BillItemsEntriesEditor';
import BillFormTopBar from './BillFormTopBar';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
@@ -125,6 +126,7 @@ function BillForm({
onSubmit={handleFormSubmit} onSubmit={handleFormSubmit}
> >
<Form> <Form>
<BillFormTopBar />
<BillFormHeader /> <BillFormHeader />
<BillItemsEntriesEditor /> <BillItemsEntriesEditor />
<BillFormFooter /> <BillFormFooter />

View File

@@ -23,6 +23,8 @@ const BillFormSchema = Yup.object().shape({
.max(DATATYPES_LENGTH.TEXT) .max(DATATYPES_LENGTH.TEXT)
.label(intl.get('note')), .label(intl.get('note')),
open: Yup.boolean(), open: Yup.boolean(),
branch_id: Yup.string(),
warehouse_id: Yup.string(),
entries: Yup.array().of( entries: Yup.array().of(
Yup.object().shape({ Yup.object().shape({
quantity: Yup.number() quantity: Yup.number()

View File

@@ -5,6 +5,8 @@ import {
useVendors, useVendors,
useItems, useItems,
useBill, useBill,
useWarehouses,
useBranches,
useSettings, useSettings,
useCreateBill, useCreateBill,
useEditBill, useEditBill,
@@ -14,8 +16,20 @@ const BillFormContext = createContext();
// Filter all purchasable items only. // Filter all purchasable items only.
const stringifiedFilterRoles = JSON.stringify([ const stringifiedFilterRoles = JSON.stringify([
{ index: 1, fieldKey: 'purchasable', value: true, condition: '&&', comparator: 'equals' }, {
{ index: 2, fieldKey: 'active', value: true, condition: '&&', comparator: 'equals' }, index: 1,
fieldKey: 'purchasable',
value: true,
condition: '&&',
comparator: 'equals',
},
{
index: 2,
fieldKey: 'active',
value: true,
condition: '&&',
comparator: 'equals',
},
]); ]);
/** /**
@@ -45,6 +59,20 @@ function BillFormProvider({ billId, ...props }) {
enabled: !!billId, enabled: !!billId,
}); });
// Fetch warehouses list.
const {
data: warehouses,
isLoading: isWarehouesLoading,
isSuccess: isWarehousesSuccess,
} = useWarehouses();
// Fetches the branches list.
const {
data: branches,
isLoading: isBranchesLoading,
isSuccess: isBranchesSuccess,
} = useBranches();
// Handle fetching bill settings. // Handle fetching bill settings.
const { isFetching: isSettingLoading } = useSettings(); const { isFetching: isSettingLoading } = useSettings();
@@ -57,11 +85,16 @@ function BillFormProvider({ billId, ...props }) {
const isNewMode = !billId; const isNewMode = !billId;
// Determines whether the warehouse and branches are loading.
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
const provider = { const provider = {
accounts, accounts,
vendors, vendors,
items, items,
bill, bill,
warehouses,
branches,
submitPayload, submitPayload,
isNewMode, isNewMode,
@@ -70,6 +103,9 @@ function BillFormProvider({ billId, ...props }) {
isAccountsLoading, isAccountsLoading,
isItemsLoading, isItemsLoading,
isVendorsLoading, isVendorsLoading,
isFeatureLoading,
isBranchesSuccess,
isWarehousesSuccess,
createBillMutate, createBillMutate,
editBillMutate, editBillMutate,

View File

@@ -0,0 +1,118 @@
import React from 'react';
import intl from 'react-intl-universal';
import {
Alignment,
Navbar,
NavbarGroup,
NavbarDivider,
Button,
Classes,
} from '@blueprintjs/core';
import styled from 'styled-components';
import {
useSetPrimaryBranchToForm,
useSetPrimaryWarehouseToForm,
} from './utils';
import { useFeatureCan } from 'hooks/state';
import { Icon, BranchSelect, FeatureCan, WarehouseSelect } from 'components';
import { useBillFormContext } from './BillFormProvider';
import { Features } from 'common';
/**
* Bill form topbar .
* @returns {JSX.Element}
*/
export default function BillFormTopBar() {
// Features guard.
const { featureCan } = useFeatureCan();
// Sets the primary warehouse to form.
useSetPrimaryWarehouseToForm();
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
// Can't display the navigation bar if warehouses or branches feature is not enabled.
if (!featureCan(Features.Warehouses) || !featureCan(Features.Branches)) {
return null;
}
return (
<Navbar className={'navbar--dashboard-topbar'}>
<NavbarGroup align={Alignment.LEFT}>
<FeatureCan feature={Features.Branches}>
<BillFormSelectBranch />
</FeatureCan>
{featureCan(Features.Warehouses) && featureCan(Features.Branches) && (
<NavbarDivider />
)}
<FeatureCan feature={Features.Warehouses}>
<BillFormSelectWarehouse />
</FeatureCan>
</NavbarGroup>
</Navbar>
);
}
function BillFormSelectBranch() {
// Bill form context.
const { branches, isBranchesLoading } = useBillFormContext();
return isBranchesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<BranchSelect
name={'branch_id'}
branches={branches}
input={BillBranchSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function BillFormSelectWarehouse() {
// Bill form context.
const { warehouses, isWarehouesLoading } = useBillFormContext();
return isWarehouesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<WarehouseSelect
name={'warehouse_id'}
warehouses={warehouses}
input={BillWarehouseSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function BillWarehouseSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.warehouse_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'warehouse-16'} iconSize={16} />}
/>
);
}
function BillBranchSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.branch_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'branch-16'} iconSize={16} />}
/>
);
}
const DetailsBarSkeletonBase = styled.div`
letter-spacing: 10px;
margin-right: 10px;
margin-left: 10px;
font-size: 8px;
width: 140px;
height: 10px;
`;

View File

@@ -1,7 +1,10 @@
import React from 'react';
import moment from 'moment'; import moment from 'moment';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import * as R from 'ramda'; import * as R from 'ramda';
import { first } from 'lodash';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { useFormikContext } from 'formik';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
import { import {
defaultFastFieldShouldUpdate, defaultFastFieldShouldUpdate,
@@ -14,6 +17,7 @@ import {
ensureEntriesHaveEmptyLine, ensureEntriesHaveEmptyLine,
} from 'containers/Entries/utils'; } from 'containers/Entries/utils';
import { isLandedCostDisabled } from '../../../Entries/utils'; import { isLandedCostDisabled } from '../../../Entries/utils';
import { useBillFormContext } from './BillFormProvider';
export const MIN_LINES_NUMBER = 4; export const MIN_LINES_NUMBER = 4;
@@ -38,6 +42,8 @@ export const defaultBill = {
reference_no: '', reference_no: '',
note: '', note: '',
open: '', open: '',
branch_id: '',
warehouse_id: '',
entries: [...repeatValue(defaultBillEntry, MIN_LINES_NUMBER)], entries: [...repeatValue(defaultBillEntry, MIN_LINES_NUMBER)],
}; };
@@ -177,3 +183,34 @@ export const handleErrors = (errors, { setErrors }) => {
); );
} }
}; };
export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = useBillFormContext();
React.useEffect(() => {
if (isBranchesSuccess) {
const primaryBranch = branches.find((b) => b.primary) || first(branches);
if (primaryBranch) {
setFieldValue('branch_id', primaryBranch.id);
}
}
}, [isBranchesSuccess, setFieldValue, branches]);
};
export const useSetPrimaryWarehouseToForm = () => {
const { setFieldValue } = useFormikContext();
const { warehouses, isWarehousesSuccess } = useBillFormContext();
React.useEffect(() => {
if (isWarehousesSuccess) {
const primaryWarehouse =
warehouses.find((b) => b.primary) || first(warehouses);
if (primaryWarehouse) {
setFieldValue('warehouse_id', primaryWarehouse.id);
}
}
}, [isWarehousesSuccess, setFieldValue, warehouses]);
};

View File

@@ -16,6 +16,7 @@ import VendorCreditNoteItemsEntriesEditor from './VendorCreditNoteItemsEntriesEd
import VendorCreditNoteFormFooter from './VendorCreditNoteFormFooter'; import VendorCreditNoteFormFooter from './VendorCreditNoteFormFooter';
import VendorCreditNoteFloatingActions from './VendorCreditNoteFloatingActions'; import VendorCreditNoteFloatingActions from './VendorCreditNoteFloatingActions';
import VendorCreditNoteFormDialogs from './VendorCreditNoteFormDialogs'; import VendorCreditNoteFormDialogs from './VendorCreditNoteFormDialogs';
import VendorCreditNoteFormTopBar from './VendorCreditNoteFormTopBar';
import { useVendorCreditNoteFormContext } from './VendorCreditNoteFormProvider'; import { useVendorCreditNoteFormContext } from './VendorCreditNoteFormProvider';
@@ -151,6 +152,7 @@ function VendorCreditNoteForm({
onSubmit={handleFormSubmit} onSubmit={handleFormSubmit}
> >
<Form> <Form>
<VendorCreditNoteFormTopBar />
<VendorCreditNoteFormHeader /> <VendorCreditNoteFormHeader />
<VendorCreditNoteItemsEntriesEditor /> <VendorCreditNoteItemsEntriesEditor />
<VendorCreditNoteFormFooter /> <VendorCreditNoteFormFooter />

View File

@@ -16,6 +16,8 @@ const getSchema = Yup.object().shape({
.max(DATATYPES_LENGTH.TEXT) .max(DATATYPES_LENGTH.TEXT)
.label(intl.get('note')), .label(intl.get('note')),
open: Yup.boolean(), open: Yup.boolean(),
branch_id: Yup.string(),
warehouse_id: Yup.string(),
entries: Yup.array().of( entries: Yup.array().of(
Yup.object().shape({ Yup.object().shape({
quantity: Yup.number() quantity: Yup.number()

View File

@@ -7,6 +7,8 @@ import {
useCreateVendorCredit, useCreateVendorCredit,
useEditVendorCredit, useEditVendorCredit,
useVendorCredit, useVendorCredit,
useWarehouses,
useBranches,
useItems, useItems,
useVendors, useVendors,
useSettingsVendorCredits, useSettingsVendorCredits,
@@ -51,6 +53,20 @@ function VendorCreditNoteFormProvider({ vendorCreditId, ...props }) {
enabled: !!billId, enabled: !!billId,
}); });
// Fetch warehouses list.
const {
data: warehouses,
isLoading: isWarehouesLoading,
isSuccess: isWarehousesSuccess,
} = useWarehouses();
// Fetches the branches list.
const {
data: branches,
isLoading: isBranchesLoading,
isSuccess: isBranchesSuccess,
} = useBranches();
// Form submit payload. // Form submit payload.
const [submitPayload, setSubmitPayload] = React.useState(); const [submitPayload, setSubmitPayload] = React.useState();
@@ -61,6 +77,9 @@ function VendorCreditNoteFormProvider({ vendorCreditId, ...props }) {
// Determines whether the form in new mode. // Determines whether the form in new mode.
const isNewMode = !vendorCreditId; const isNewMode = !vendorCreditId;
// Determines whether the warehouse and branches are loading.
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
const newVendorCredit = !isEmpty(bill) const newVendorCredit = !isEmpty(bill)
? transformToEditForm({ ? transformToEditForm({
...pick(bill, ['vendor_id', 'entries']), ...pick(bill, ['vendor_id', 'entries']),
@@ -72,12 +91,17 @@ function VendorCreditNoteFormProvider({ vendorCreditId, ...props }) {
items, items,
vendors, vendors,
vendorCredit, vendorCredit,
warehouses,
branches,
submitPayload, submitPayload,
isNewMode, isNewMode,
newVendorCredit, newVendorCredit,
isVendorCreditLoading, isVendorCreditLoading,
isFeatureLoading,
isBranchesSuccess,
isWarehousesSuccess,
createVendorCreditMutate, createVendorCreditMutate,
editVendorCreditMutate, editVendorCreditMutate,
setSubmitPayload, setSubmitPayload,

View File

@@ -0,0 +1,118 @@
import React from 'react';
import intl from 'react-intl-universal';
import styled from 'styled-components';
import {
Alignment,
Navbar,
NavbarGroup,
NavbarDivider,
Button,
Classes,
} from '@blueprintjs/core';
import {
useSetPrimaryBranchToForm,
useSetPrimaryWarehouseToForm,
} from './utils';
import { useFeatureCan } from 'hooks/state';
import { Icon, BranchSelect, FeatureCan, WarehouseSelect } from 'components';
import { useVendorCreditNoteFormContext } from './VendorCreditNoteFormProvider';
import { Features } from 'common';
/**
* Vendor Credit note form topbar .
* @returns {JSX.Element}
*/
export default function VendorCreditNoteFormTopBar() {
// Features guard.
const { featureCan } = useFeatureCan();
// Sets the primary warehouse to form.
useSetPrimaryWarehouseToForm();
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
// Can't display the navigation bar if warehouses or branches feature is not enabled.
if (!featureCan(Features.Warehouses) || !featureCan(Features.Branches)) {
return null;
}
return (
<Navbar className={'navbar--dashboard-topbar'}>
<NavbarGroup align={Alignment.LEFT}>
<FeatureCan feature={Features.Branches}>
<VendorCreditNoteFormSelectBranch />
</FeatureCan>
{featureCan(Features.Warehouses) && featureCan(Features.Branches) && (
<NavbarDivider />
)}
<FeatureCan feature={Features.Warehouses}>
<VendorCreditFormSelectWarehouse />
</FeatureCan>
</NavbarGroup>
</Navbar>
);
}
function VendorCreditNoteFormSelectBranch() {
// Vendor credit note form context.
const { branches, isBranchesLoading } = useVendorCreditNoteFormContext();
return isBranchesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<BranchSelect
name={'branch_id'}
branches={branches}
input={VendorCreditNoteBranchSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function VendorCreditFormSelectWarehouse() {
// vendor credit note form context.
const { warehouses, isWarehouesLoading } = useVendorCreditNoteFormContext();
return isWarehouesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<WarehouseSelect
name={'warehouse_id'}
warehouses={warehouses}
input={VendorCreditNoteWarehouseSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function VendorCreditNoteWarehouseSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.warehouse_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'warehouse-16'} iconSize={16} />}
/>
);
}
function VendorCreditNoteBranchSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.branch_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'branch-16'} iconSize={16} />}
/>
);
}
const DetailsBarSkeletonBase = styled.div`
letter-spacing: 10px;
margin-right: 10px;
margin-left: 10px;
font-size: 8px;
width: 140px;
height: 10px;
`;

View File

@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import * as R from 'ramda'; import * as R from 'ramda';
import moment from 'moment'; import moment from 'moment';
import { first } from 'lodash';
import { import {
defaultFastFieldShouldUpdate, defaultFastFieldShouldUpdate,
@@ -14,6 +15,7 @@ import {
ensureEntriesHaveEmptyLine, ensureEntriesHaveEmptyLine,
} from 'containers/Entries/utils'; } from 'containers/Entries/utils';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { useVendorCreditNoteFormContext } from './VendorCreditNoteFormProvider';
export const MIN_LINES_NUMBER = 4; export const MIN_LINES_NUMBER = 4;
@@ -37,6 +39,8 @@ export const defaultVendorsCreditNote = {
vendor_credit_date: moment(new Date()).format('YYYY-MM-DD'), vendor_credit_date: moment(new Date()).format('YYYY-MM-DD'),
reference_no: '', reference_no: '',
note: '', note: '',
branch_id: '',
warehouse_id: '',
entries: [...repeatValue(defaultCreditNoteEntry, MIN_LINES_NUMBER)], entries: [...repeatValue(defaultCreditNoteEntry, MIN_LINES_NUMBER)],
}; };
@@ -129,3 +133,34 @@ export const useObserveVendorCreditNoSettings = (prefix, nextNumber) => {
setFieldValue('vendor_credit_number', creditNo); setFieldValue('vendor_credit_number', creditNo);
}, [setFieldValue, prefix, nextNumber]); }, [setFieldValue, prefix, nextNumber]);
}; };
export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = useVendorCreditNoteFormContext();
React.useEffect(() => {
if (isBranchesSuccess) {
const primaryBranch = branches.find((b) => b.primary) || first(branches);
if (primaryBranch) {
setFieldValue('branch_id', primaryBranch.id);
}
}
}, [isBranchesSuccess, setFieldValue, branches]);
};
export const useSetPrimaryWarehouseToForm = () => {
const { setFieldValue } = useFormikContext();
const { warehouses, isWarehousesSuccess } = useVendorCreditNoteFormContext();
React.useEffect(() => {
if (isWarehousesSuccess) {
const primaryWarehouse =
warehouses.find((b) => b.primary) || first(warehouses);
if (primaryWarehouse) {
setFieldValue('warehouse_id', primaryWarehouse.id);
}
}
}, [isWarehousesSuccess, setFieldValue, warehouses]);
};

View File

@@ -12,6 +12,7 @@ import PaymentMadeHeader from './PaymentMadeFormHeader';
import PaymentMadeFloatingActions from './PaymentMadeFloatingActions'; import PaymentMadeFloatingActions from './PaymentMadeFloatingActions';
import PaymentMadeFooter from './PaymentMadeFooter'; import PaymentMadeFooter from './PaymentMadeFooter';
import PaymentMadeFormBody from './PaymentMadeFormBody'; import PaymentMadeFormBody from './PaymentMadeFormBody';
import PaymentMadeFormTopBar from './PaymentMadeFormTopBar';
import { PaymentMadeInnerProvider } from './PaymentMadeInnerProvider'; import { PaymentMadeInnerProvider } from './PaymentMadeInnerProvider';
import withSettings from 'containers/Settings/withSettings'; import withSettings from 'containers/Settings/withSettings';
@@ -145,6 +146,7 @@ function PaymentMadeForm({
> >
<Form> <Form>
<PaymentMadeInnerProvider> <PaymentMadeInnerProvider>
<PaymentMadeFormTopBar />
<PaymentMadeHeader /> <PaymentMadeHeader />
<PaymentMadeFormBody /> <PaymentMadeFormBody />
<PaymentMadeFooter /> <PaymentMadeFooter />

View File

@@ -19,6 +19,7 @@ const Schema = Yup.object().shape({
.label(intl.get('payment_no_')), .label(intl.get('payment_no_')),
reference: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(), reference: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(),
description: Yup.string().max(DATATYPES_LENGTH.TEXT), description: Yup.string().max(DATATYPES_LENGTH.TEXT),
branch_id: Yup.string(),
entries: Yup.array().of( entries: Yup.array().of(
Yup.object().shape({ Yup.object().shape({
id: Yup.number().nullable(), id: Yup.number().nullable(),

View File

@@ -3,6 +3,7 @@ import {
useAccounts, useAccounts,
useVendors, useVendors,
useItems, useItems,
useBranches,
usePaymentMadeEditPage, usePaymentMadeEditPage,
useSettings, useSettings,
useCreatePaymentMade, useCreatePaymentMade,
@@ -45,6 +46,13 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
enabled: !!paymentMadeId, enabled: !!paymentMadeId,
}); });
// Fetches the branches list.
const {
data: branches,
isLoading: isBranchesLoading,
isSuccess: isBranchesSuccess,
} = useBranches();
// Fetch payment made settings. // Fetch payment made settings.
useSettings(); useSettings();
@@ -54,6 +62,8 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
const isNewMode = !paymentMadeId; const isNewMode = !paymentMadeId;
const isFeatureLoading = isBranchesLoading;
// Provider payload. // Provider payload.
const provider = { const provider = {
paymentMadeId, paymentMadeId,
@@ -62,6 +72,7 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
paymentMadeEditPage, paymentMadeEditPage,
vendors, vendors,
items, items,
branches,
submitPayload, submitPayload,
paymentVendorId, paymentVendorId,
@@ -72,6 +83,8 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
isVendorsLoading, isVendorsLoading,
isPaymentFetching, isPaymentFetching,
isPaymentLoading, isPaymentLoading,
isFeatureLoading,
isBranchesSuccess,
createPaymentMadeMutate, createPaymentMadeMutate,
editPaymentMadeMutate, editPaymentMadeMutate,

View File

@@ -0,0 +1,77 @@
import React from 'react';
import intl from 'react-intl-universal';
import {
Alignment,
Navbar,
NavbarGroup,
Button,
Classes,
} from '@blueprintjs/core';
import styled from 'styled-components';
import { useSetPrimaryBranchToForm } from './utils';
import { useFeatureCan } from 'hooks/state';
import { Icon, BranchSelect, FeatureCan } from 'components';
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
import { Features } from 'common';
/**
* Payment made from top bar.
* @returns
*/
export default function PaymentMadeFormTopBar() {
// Features guard.
const { featureCan } = useFeatureCan();
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
// Can't display the navigation bar if branches feature is not enabled.
if (!featureCan(Features.Branches)) {
return null;
}
return (
<Navbar className={'navbar--dashboard-topbar'}>
<NavbarGroup align={Alignment.LEFT}>
<FeatureCan feature={Features.Branches}>
<PaymentMadeFormSelectBranch />
</FeatureCan>
</NavbarGroup>
</Navbar>
);
}
function PaymentMadeFormSelectBranch() {
// payment made form context.
const { branches, isBranchesLoading } = usePaymentMadeFormContext();
return isBranchesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<BranchSelect
name={'branch_id'}
branches={branches}
input={PaymentMadeBranchSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function PaymentMadeBranchSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.branch_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'branch-16'} iconSize={16} />}
/>
);
}
const DetailsBarSkeletonBase = styled.div`
letter-spacing: 10px;
margin-right: 10px;
margin-left: 10px;
font-size: 8px;
width: 140px;
height: 10px;
`;

View File

@@ -1,5 +1,8 @@
import React from 'react';
import moment from 'moment'; import moment from 'moment';
import { pick } from 'lodash'; import { pick, first } from 'lodash';
import { useFormikContext } from 'formik';
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
import { import {
defaultFastFieldShouldUpdate, defaultFastFieldShouldUpdate,
safeSumBy, safeSumBy,
@@ -31,6 +34,7 @@ export const defaultPaymentMade = {
payment_number: '', payment_number: '',
statement: '', statement: '',
currency_code: '', currency_code: '',
branch_id: '',
entries: [], entries: [],
}; };
@@ -91,3 +95,18 @@ export const transformFormToRequest = (form) => {
return { ...form, entries: orderingLinesIndexes(entries) }; return { ...form, entries: orderingLinesIndexes(entries) };
}; };
export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = usePaymentMadeFormContext();
React.useEffect(() => {
if (isBranchesSuccess) {
const primaryBranch = branches.find((b) => b.primary) || first(branches);
if (primaryBranch) {
setFieldValue('branch_id', primaryBranch.id);
}
}
}, [isBranchesSuccess, setFieldValue, branches]);
};

View File

@@ -16,6 +16,7 @@ import CreditNoteItemsEntriesEditorField from './CreditNoteItemsEntriesEditorFie
import CreditNoteFormFooter from './CreditNoteFormFooter'; import CreditNoteFormFooter from './CreditNoteFormFooter';
import CreditNoteFloatingActions from './CreditNoteFloatingActions'; import CreditNoteFloatingActions from './CreditNoteFloatingActions';
import CreditNoteFormDialogs from './CreditNoteFormDialogs'; import CreditNoteFormDialogs from './CreditNoteFormDialogs';
import CreditNoteFormTopBar from './CreditNoteFormTopBar';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
@@ -153,6 +154,7 @@ function CreditNoteForm({
onSubmit={handleFormSubmit} onSubmit={handleFormSubmit}
> >
<Form> <Form>
<CreditNoteFormTopBar />
<CreditNoteFormHeader /> <CreditNoteFormHeader />
<CreditNoteItemsEntriesEditorField /> <CreditNoteItemsEntriesEditorField />
<CreditNoteFormFooter /> <CreditNoteFormFooter />

View File

@@ -22,6 +22,8 @@ const getSchema = () =>
.min(1) .min(1)
.max(DATATYPES_LENGTH.TEXT) .max(DATATYPES_LENGTH.TEXT)
.label(intl.get('note')), .label(intl.get('note')),
branch_id: Yup.string(),
warehouse_id: Yup.string(),
entries: Yup.array().of( entries: Yup.array().of(
Yup.object().shape({ Yup.object().shape({
quantity: Yup.number() quantity: Yup.number()

View File

@@ -10,6 +10,8 @@ import {
useEditCreditNote, useEditCreditNote,
useItems, useItems,
useCustomers, useCustomers,
useWarehouses,
useBranches,
useSettingsCreditNotes, useSettingsCreditNotes,
useInvoice, useInvoice,
} from 'hooks/query'; } from 'hooks/query';
@@ -49,6 +51,20 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) {
enabled: !!invoiceId, enabled: !!invoiceId,
}); });
// Fetch warehouses list.
const {
data: warehouses,
isLoading: isWarehouesLoading,
isSuccess: isWarehousesSuccess,
} = useWarehouses();
// Fetches the branches list.
const {
data: branches,
isLoading: isBranchesLoading,
isSuccess: isBranchesSuccess,
} = useBranches();
// Handle fetching settings. // Handle fetching settings.
useSettingsCreditNotes(); useSettingsCreditNotes();
@@ -62,6 +78,9 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) {
// Determines whether the form in new mode. // Determines whether the form in new mode.
const isNewMode = !creditNoteId; const isNewMode = !creditNoteId;
// Determines whether the warehouse and branches are loading.
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
const newCreditNote = !isEmpty(invoice) const newCreditNote = !isEmpty(invoice)
? transformToEditForm({ ? transformToEditForm({
...pick(invoice, ['customer_id', 'entries']), ...pick(invoice, ['customer_id', 'entries']),
@@ -73,13 +92,18 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) {
items, items,
customers, customers,
creditNote, creditNote,
branches,
warehouses,
submitPayload, submitPayload,
isNewMode, isNewMode,
newCreditNote, newCreditNote,
isItemsLoading, isItemsLoading,
isCustomersLoading, isCustomersLoading,
isFeatureLoading,
isBranchesSuccess,
isWarehousesSuccess,
createCreditNoteMutate, createCreditNoteMutate,
editCreditNoteMutate, editCreditNoteMutate,
setSubmitPayload, setSubmitPayload,

View File

@@ -0,0 +1,118 @@
import React from 'react';
import intl from 'react-intl-universal';
import {
Alignment,
Navbar,
NavbarGroup,
NavbarDivider,
Button,
Classes,
} from '@blueprintjs/core';
import styled from 'styled-components';
import {
useSetPrimaryBranchToForm,
useSetPrimaryWarehouseToForm,
} from './utils';
import { useFeatureCan } from 'hooks/state';
import { Icon, BranchSelect, FeatureCan, WarehouseSelect } from 'components';
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
import { Features } from 'common';
/**
* Credit note form topbar .
* @returns {JSX.Element}
*/
export default function CreditNoteFormTopbar() {
// Features guard.
const { featureCan } = useFeatureCan();
// Sets the primary warehouse to form.
useSetPrimaryWarehouseToForm();
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
// Can't display the navigation bar if warehouses or branches feature is not enabled.
if (!featureCan(Features.Warehouses) || !featureCan(Features.Branches)) {
return null;
}
return (
<Navbar className={'navbar--dashboard-topbar'}>
<NavbarGroup align={Alignment.LEFT}>
<FeatureCan feature={Features.Branches}>
<CreditNoteFormSelectBranch />
</FeatureCan>
{featureCan(Features.Warehouses) && featureCan(Features.Branches) && (
<NavbarDivider />
)}
<FeatureCan feature={Features.Warehouses}>
<CreditFormSelectWarehouse />
</FeatureCan>
</NavbarGroup>
</Navbar>
);
}
function CreditNoteFormSelectBranch() {
// Credit note form context.
const { branches, isBranchesLoading } = useCreditNoteFormContext();
return isBranchesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<BranchSelect
name={'branch_id'}
branches={branches}
input={CreditNoteBranchSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function CreditFormSelectWarehouse() {
// Credit note form context.
const { warehouses, isWarehouesLoading } = useCreditNoteFormContext();
return isWarehouesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<WarehouseSelect
name={'warehouse_id'}
warehouses={warehouses}
input={CreditNoteWarehouseSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function CreditNoteWarehouseSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.warehouse_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'warehouse-16'} iconSize={16} />}
/>
);
}
function CreditNoteBranchSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.branch_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'branch-16'} iconSize={16} />}
/>
);
}
const DetailsBarSkeletonBase = styled.div`
letter-spacing: 10px;
margin-right: 10px;
margin-left: 10px;
font-size: 8px;
width: 140px;
height: 10px;
`;

View File

@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import * as R from 'ramda'; import * as R from 'ramda';
import moment from 'moment'; import moment from 'moment';
import { first } from 'lodash';
import { import {
defaultFastFieldShouldUpdate, defaultFastFieldShouldUpdate,
@@ -10,6 +11,7 @@ import {
orderingLinesIndexes, orderingLinesIndexes,
} from 'utils'; } from 'utils';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
import { import {
updateItemsEntriesTotal, updateItemsEntriesTotal,
@@ -39,6 +41,8 @@ export const defaultCreditNote = {
reference_no: '', reference_no: '',
note: '', note: '',
terms_conditions: '', terms_conditions: '',
branch_id: '',
warehouse_id: '',
entries: [...repeatValue(defaultCreditNoteEntry, MIN_LINES_NUMBER)], entries: [...repeatValue(defaultCreditNoteEntry, MIN_LINES_NUMBER)],
}; };
@@ -131,3 +135,34 @@ export const useObserveCreditNoSettings = (prefix, nextNumber) => {
setFieldValue('credit_note_number', creditNo); setFieldValue('credit_note_number', creditNo);
}, [setFieldValue, prefix, nextNumber]); }, [setFieldValue, prefix, nextNumber]);
}; };
export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = useCreditNoteFormContext();
React.useEffect(() => {
if (isBranchesSuccess) {
const primaryBranch = branches.find((b) => b.primary) || first(branches);
if (primaryBranch) {
setFieldValue('branch_id', primaryBranch.id);
}
}
}, [isBranchesSuccess, setFieldValue, branches]);
};
export const useSetPrimaryWarehouseToForm = () => {
const { setFieldValue } = useFormikContext();
const { warehouses, isWarehousesSuccess } = useCreditNoteFormContext();
React.useEffect(() => {
if (isWarehousesSuccess) {
const primaryWarehouse =
warehouses.find((b) => b.primary) || first(warehouses);
if (primaryWarehouse) {
setFieldValue('warehouse_id', primaryWarehouse.id);
}
}
}, [isWarehousesSuccess, setFieldValue, warehouses]);
};

View File

@@ -17,6 +17,7 @@ import EstimateItemsEntriesField from './EstimateItemsEntriesField';
import EstimateFloatingActions from './EstimateFloatingActions'; import EstimateFloatingActions from './EstimateFloatingActions';
import EstimateFormFooter from './EstimateFormFooter'; import EstimateFormFooter from './EstimateFormFooter';
import EstimateFormDialogs from './EstimateFormDialogs'; import EstimateFormDialogs from './EstimateFormDialogs';
import EstimtaeFormTopBar from './EstimtaeFormTopBar';
import withSettings from 'containers/Settings/withSettings'; import withSettings from 'containers/Settings/withSettings';
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization'; import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
@@ -28,7 +29,7 @@ import {
transformToEditForm, transformToEditForm,
defaultEstimate, defaultEstimate,
transfromsFormValuesToRequest, transfromsFormValuesToRequest,
handleErrors handleErrors,
} from './utils'; } from './utils';
/** /**
@@ -153,6 +154,7 @@ function EstimateForm({
onSubmit={handleFormSubmit} onSubmit={handleFormSubmit}
> >
<Form> <Form>
<EstimtaeFormTopBar />
<EstimateFormHeader /> <EstimateFormHeader />
<EstimateItemsEntriesField /> <EstimateItemsEntriesField />
<EstimateFormFooter /> <EstimateFormFooter />

View File

@@ -28,6 +28,8 @@ const Schema = Yup.object().shape({
.max(DATATYPES_LENGTH.TEXT) .max(DATATYPES_LENGTH.TEXT)
.label(intl.get('note')), .label(intl.get('note')),
delivered: Yup.boolean(), delivered: Yup.boolean(),
branch_id: Yup.string(),
warehouse_id: Yup.string(),
entries: Yup.array().of( entries: Yup.array().of(
Yup.object().shape({ Yup.object().shape({
quantity: Yup.number() quantity: Yup.number()

View File

@@ -3,6 +3,8 @@ import DashboardInsider from 'components/Dashboard/DashboardInsider';
import { import {
useEstimate, useEstimate,
useCustomers, useCustomers,
useWarehouses,
useBranches,
useItems, useItems,
useSettingsEstimates, useSettingsEstimates,
useCreateEstimate, useCreateEstimate,
@@ -39,6 +41,20 @@ function EstimateFormProvider({ estimateId, ...props }) {
isLoading: isCustomersLoading, isLoading: isCustomersLoading,
} = useCustomers({ page_size: 10000 }); } = useCustomers({ page_size: 10000 });
// Fetch warehouses list.
const {
data: warehouses,
isLoading: isWarehouesLoading,
isSuccess: isWarehousesSuccess,
} = useWarehouses();
// Fetches the branches list.
const {
data: branches,
isLoading: isBranchesLoading,
isSuccess: isBranchesSuccess,
} = useBranches();
// Handle fetch settings. // Handle fetch settings.
useSettingsEstimates(); useSettingsEstimates();
@@ -51,12 +67,17 @@ function EstimateFormProvider({ estimateId, ...props }) {
const isNewMode = !estimateId; const isNewMode = !estimateId;
// Determines whether the warehouse and branches are loading.
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
// Provider payload. // Provider payload.
const provider = { const provider = {
estimateId, estimateId,
estimate, estimate,
items, items,
customers, customers,
branches,
warehouses,
isNewMode, isNewMode,
isItemsFetching, isItemsFetching,
@@ -65,7 +86,9 @@ function EstimateFormProvider({ estimateId, ...props }) {
isCustomersLoading, isCustomersLoading,
isItemsLoading, isItemsLoading,
isEstimateLoading, isEstimateLoading,
isFeatureLoading,
isBranchesSuccess,
isWarehousesSuccess,
submitPayload, submitPayload,
setSubmitPayload, setSubmitPayload,

View File

@@ -0,0 +1,118 @@
import React from 'react';
import intl from 'react-intl-universal';
import {
Alignment,
Navbar,
NavbarGroup,
NavbarDivider,
Button,
Classes,
} from '@blueprintjs/core';
import styled from 'styled-components';
import {
useSetPrimaryBranchToForm,
useSetPrimaryWarehouseToForm,
} from './utils';
import { useFeatureCan } from 'hooks/state';
import { Icon, BranchSelect, FeatureCan, WarehouseSelect } from 'components';
import { useEstimateFormContext } from './EstimateFormProvider';
import { Features } from 'common';
/**
* Estimate form topbar .
* @returns {JSX.Element}
*/
export default function EstimtaeFormTopBar() {
// Features guard.
const { featureCan } = useFeatureCan();
// Sets the primary warehouse to form.
useSetPrimaryWarehouseToForm();
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
// Can't display the navigation bar if warehouses or branches feature is not enabled.
if (!featureCan(Features.Warehouses) || !featureCan(Features.Branches)) {
return null;
}
return (
<Navbar className={'navbar--dashboard-topbar'}>
<NavbarGroup align={Alignment.LEFT}>
<FeatureCan feature={Features.Branches}>
<EstimateFormSelectBranch />
</FeatureCan>
{featureCan(Features.Warehouses) && featureCan(Features.Branches) && (
<NavbarDivider />
)}
<FeatureCan feature={Features.Warehouses}>
<EstimateFormSelectWarehouse />
</FeatureCan>
</NavbarGroup>
</Navbar>
);
}
function EstimateFormSelectBranch() {
// Estimate form context.
const { branches, isBranchesLoading } = useEstimateFormContext();
return isBranchesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<BranchSelect
name={'branch_id'}
branches={branches}
input={EstimateBranchSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function EstimateFormSelectWarehouse() {
// Estimate form context.
const { warehouses, isWarehouesLoading } = useEstimateFormContext();
return isWarehouesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<WarehouseSelect
name={'warehouse_id'}
warehouses={warehouses}
input={EstimateWarehouseSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function EstimateWarehouseSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.warehouse_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'warehouse-16'} iconSize={16} />}
/>
);
}
function EstimateBranchSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.branch_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'branch-16'} iconSize={16} />}
/>
);
}
const DetailsBarSkeletonBase = styled.div`
letter-spacing: 10px;
margin-right: 10px;
margin-left: 10px;
font-size: 8px;
width: 140px;
height: 10px;
`;

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import moment from 'moment'; import moment from 'moment';
import * as R from 'ramda'; import * as R from 'ramda';
import { omit } from 'lodash'; import { omit, first } from 'lodash';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { import {
defaultFastFieldShouldUpdate, defaultFastFieldShouldUpdate,
@@ -10,6 +10,7 @@ import {
repeatValue, repeatValue,
transformToForm, transformToForm,
} from 'utils'; } from 'utils';
import { useEstimateFormContext } from './EstimateFormProvider';
import { import {
updateItemsEntriesTotal, updateItemsEntriesTotal,
ensureEntriesHaveEmptyLine, ensureEntriesHaveEmptyLine,
@@ -36,6 +37,8 @@ export const defaultEstimate = {
reference: '', reference: '',
note: '', note: '',
terms_conditions: '', terms_conditions: '',
branch_id: '',
warehouse_id: '',
entries: [...repeatValue(defaultEstimateEntry, MIN_LINES_NUMBER)], entries: [...repeatValue(defaultEstimateEntry, MIN_LINES_NUMBER)],
}; };
@@ -116,8 +119,8 @@ export const ITEMS_FILTER_ROLES = JSON.stringify([
/** /**
* Transform response errors to fields. * Transform response errors to fields.
* @param {*} errors * @param {*} errors
* @param {*} param1 * @param {*} param1
*/ */
export const handleErrors = (errors, { setErrors }) => { export const handleErrors = (errors, { setErrors }) => {
if (errors.some((e) => e.type === ERRORS.ESTIMATE_NUMBER_IS_NOT_UNQIUE)) { if (errors.some((e) => e.type === ERRORS.ESTIMATE_NUMBER_IS_NOT_UNQIUE)) {
@@ -151,3 +154,34 @@ export const transfromsFormValuesToRequest = (values) => {
entries: entries.map((entry) => ({ ...omit(entry, ['amount']) })), entries: entries.map((entry) => ({ ...omit(entry, ['amount']) })),
}; };
}; };
export const useSetPrimaryWarehouseToForm = () => {
const { setFieldValue } = useFormikContext();
const { warehouses, isWarehousesSuccess } = useEstimateFormContext();
React.useEffect(() => {
if (isWarehousesSuccess) {
const primaryWarehouse =
warehouses.find((b) => b.primary) || first(warehouses);
if (primaryWarehouse) {
setFieldValue('warehouse_id', primaryWarehouse.id);
}
}
}, [isWarehousesSuccess, setFieldValue, warehouses]);
};
export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = useEstimateFormContext();
React.useEffect(() => {
if (isBranchesSuccess) {
const primaryBranch = branches.find((b) => b.primary) || first(branches);
if (primaryBranch) {
setFieldValue('branch_id', primaryBranch.id);
}
}
}, [isBranchesSuccess, setFieldValue, branches]);
};

View File

@@ -15,6 +15,7 @@ import PaymentReceiveFloatingActions from './PaymentReceiveFloatingActions';
import PaymentReceiveFormFooter from './PaymentReceiveFormFooter'; import PaymentReceiveFormFooter from './PaymentReceiveFormFooter';
import PaymentReceiveFormAlerts from './PaymentReceiveFormAlerts'; import PaymentReceiveFormAlerts from './PaymentReceiveFormAlerts';
import PaymentReceiveFormDialogs from './PaymentReceiveFormDialogs'; import PaymentReceiveFormDialogs from './PaymentReceiveFormDialogs';
import PaymentReceiveFormTopBar from './PaymentReceiveFormTopBar';
import { PaymentReceiveInnerProvider } from './PaymentReceiveInnerProvider'; import { PaymentReceiveInnerProvider } from './PaymentReceiveInnerProvider';
import withSettings from 'containers/Settings/withSettings'; import withSettings from 'containers/Settings/withSettings';
@@ -178,6 +179,7 @@ function PaymentReceiveForm({
> >
<Form> <Form>
<PaymentReceiveInnerProvider> <PaymentReceiveInnerProvider>
<PaymentReceiveFormTopBar />
<PaymentReceiveHeader /> <PaymentReceiveHeader />
<PaymentReceiveFormBody /> <PaymentReceiveFormBody />
<PaymentReceiveFormFooter /> <PaymentReceiveFormFooter />

View File

@@ -19,6 +19,7 @@ const Schema = Yup.object().shape({
.label(intl.get('payment_receive_no_')), .label(intl.get('payment_receive_no_')),
reference_no: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(), reference_no: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(),
// statement: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT), // statement: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
branch_id: Yup.string(),
entries: Yup.array().of( entries: Yup.array().of(
Yup.object().shape({ Yup.object().shape({
id: Yup.number().nullable(), id: Yup.number().nullable(),

View File

@@ -5,6 +5,7 @@ import {
usePaymentReceiveEditPage, usePaymentReceiveEditPage,
useAccounts, useAccounts,
useCustomers, useCustomers,
useBranches,
useCreatePaymentReceive, useCreatePaymentReceive,
useEditPaymentReceive, useEditPaymentReceive,
} from 'hooks/query'; } from 'hooks/query';
@@ -42,9 +43,18 @@ function PaymentReceiveFormProvider({ paymentReceiveId, ...props }) {
isLoading: isCustomersLoading, isLoading: isCustomersLoading,
} = useCustomers({ page_size: 10000 }); } = useCustomers({ page_size: 10000 });
// Fetches the branches list.
const {
data: branches,
isLoading: isBranchesLoading,
isSuccess: isBranchesSuccess,
} = useBranches();
// Detarmines whether the new mode. // Detarmines whether the new mode.
const isNewMode = !paymentReceiveId; const isNewMode = !paymentReceiveId;
const isFeatureLoading = isBranchesLoading;
// Create and edit payment receive mutations. // Create and edit payment receive mutations.
const { mutateAsync: editPaymentReceiveMutate } = useEditPaymentReceive(); const { mutateAsync: editPaymentReceiveMutate } = useEditPaymentReceive();
const { mutateAsync: createPaymentReceiveMutate } = useCreatePaymentReceive(); const { mutateAsync: createPaymentReceiveMutate } = useCreatePaymentReceive();
@@ -56,11 +66,14 @@ function PaymentReceiveFormProvider({ paymentReceiveId, ...props }) {
paymentEntriesEditPage, paymentEntriesEditPage,
accounts, accounts,
customers, customers,
branches,
isPaymentLoading, isPaymentLoading,
isAccountsLoading, isAccountsLoading,
isPaymentFetching, isPaymentFetching,
isCustomersLoading, isCustomersLoading,
isFeatureLoading,
isBranchesSuccess,
isNewMode, isNewMode,
submitPayload, submitPayload,

View File

@@ -0,0 +1,78 @@
import React from 'react';
import intl from 'react-intl-universal';
import {
Alignment,
Navbar,
NavbarGroup,
Button,
Classes,
} from '@blueprintjs/core';
import styled from 'styled-components';
import { useSetPrimaryBranchToForm } from './utils';
import { useFeatureCan } from 'hooks/state';
import { Icon, BranchSelect, FeatureCan } from 'components';
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
import { Features } from 'common';
/**
* Payment receive from top bar.
* @returns
*/
export default function PaymentReceiveFormTopBar() {
// Features guard.
const { featureCan } = useFeatureCan();
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
// Can't display the navigation bar if branches feature is not enabled.
if (!featureCan(Features.Branches)) {
return null;
}
return (
<Navbar className={'navbar--dashboard-topbar'}>
<NavbarGroup align={Alignment.LEFT}>
<FeatureCan feature={Features.Branches}>
<PaymentReceiveFormSelectBranch />
</FeatureCan>
</NavbarGroup>
</Navbar>
);
}
function PaymentReceiveFormSelectBranch() {
// payment receive form context.
const { branches, isBranchesLoading } = usePaymentReceiveFormContext();
return isBranchesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<BranchSelect
name={'branch_id'}
branches={branches}
input={PaymentReceiveBranchSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function PaymentReceiveBranchSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.branch_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'branch-16'} iconSize={16} />}
/>
);
}
const DetailsBarSkeletonBase = styled.div`
letter-spacing: 10px;
margin-right: 10px;
margin-left: 10px;
font-size: 8px;
width: 140px;
height: 10px;
`;

View File

@@ -1,13 +1,14 @@
import React from 'react'; import React from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import moment from 'moment'; import moment from 'moment';
import { omit, pick } from 'lodash'; import { omit, pick, first } from 'lodash';
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
import { import {
defaultFastFieldShouldUpdate, defaultFastFieldShouldUpdate,
transactionNumber, transactionNumber,
transformToForm, transformToForm,
safeSumBy, safeSumBy,
orderingLinesIndexes orderingLinesIndexes,
} from 'utils'; } from 'utils';
// Default payment receive entry. // Default payment receive entry.
@@ -32,6 +33,7 @@ export const defaultPaymentReceive = {
statement: '', statement: '',
full_amount: '', full_amount: '',
currency_code: '', currency_code: '',
branch_id: '',
entries: [], entries: [],
}; };
@@ -46,8 +48,9 @@ export const defaultRequestPayment = {
deposit_account_id: '', deposit_account_id: '',
payment_date: '', payment_date: '',
payment_receive_no: '', payment_receive_no: '',
branch_id: '',
statement: '', statement: '',
entries: [] entries: [],
}; };
/** /**
@@ -78,6 +81,7 @@ export const transformInvoicesNewPageEntries = (invoices) => [
currency_code: invoice.currency_code, currency_code: invoice.currency_code,
payment_amount: '', payment_amount: '',
invoice_no: invoice.invoice_no, invoice_no: invoice.invoice_no,
branch_id: invoice.branch_id,
total_payment_amount: invoice.payment_amount, total_payment_amount: invoice.payment_amount,
})), })),
]; ];
@@ -162,3 +166,18 @@ export const transformFormToRequest = (form) => {
entries: orderingLinesIndexes(entries), entries: orderingLinesIndexes(entries),
}; };
}; };
export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = usePaymentReceiveFormContext();
React.useEffect(() => {
if (isBranchesSuccess) {
const primaryBranch = branches.find((b) => b.primary) || first(branches);
if (primaryBranch) {
setFieldValue('branch_id', primaryBranch.id);
}
}
}, [isBranchesSuccess, setFieldValue, branches]);
};

View File

@@ -19,6 +19,7 @@ import ReceiptItemsEntriesEditor from './ReceiptItemsEntriesEditor';
import ReceiptFormFloatingActions from './ReceiptFormFloatingActions'; import ReceiptFormFloatingActions from './ReceiptFormFloatingActions';
import ReceiptFormFooter from './ReceiptFormFooter'; import ReceiptFormFooter from './ReceiptFormFooter';
import ReceiptFormDialogs from './ReceiptFormDialogs'; import ReceiptFormDialogs from './ReceiptFormDialogs';
import ReceiptFormTopbar from './ReceiptFormTopbar';
import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withSettings from 'containers/Settings/withSettings'; import withSettings from 'containers/Settings/withSettings';
@@ -157,6 +158,7 @@ function ReceiptForm({
onSubmit={handleFormSubmit} onSubmit={handleFormSubmit}
> >
<Form> <Form>
<ReceiptFormTopbar />
<ReceiptFromHeader /> <ReceiptFromHeader />
<ReceiptItemsEntriesEditor /> <ReceiptItemsEntriesEditor />
<ReceiptFormFooter /> <ReceiptFormFooter />

View File

@@ -4,12 +4,8 @@ import { DATATYPES_LENGTH } from 'common/dataTypes';
import { isBlank } from 'utils'; import { isBlank } from 'utils';
const Schema = Yup.object().shape({ const Schema = Yup.object().shape({
customer_id: Yup.string() customer_id: Yup.string().label(intl.get('customer_name_')).required(),
.label(intl.get('customer_name_')) receipt_date: Yup.date().required().label(intl.get('receipt_date_')),
.required(),
receipt_date: Yup.date()
.required()
.label(intl.get('receipt_date_')),
receipt_number: Yup.string() receipt_number: Yup.string()
.nullable() .nullable()
.max(DATATYPES_LENGTH.STRING) .max(DATATYPES_LENGTH.STRING)
@@ -29,6 +25,8 @@ const Schema = Yup.object().shape({
.max(DATATYPES_LENGTH.TEXT) .max(DATATYPES_LENGTH.TEXT)
.label(intl.get('note')), .label(intl.get('note')),
closed: Yup.boolean(), closed: Yup.boolean(),
branch_id: Yup.string(),
warehouse_id: Yup.string(),
entries: Yup.array().of( entries: Yup.array().of(
Yup.object().shape({ Yup.object().shape({
quantity: Yup.number() quantity: Yup.number()

View File

@@ -5,6 +5,8 @@ import {
useAccounts, useAccounts,
useSettingsReceipts, useSettingsReceipts,
useCustomers, useCustomers,
useWarehouses,
useBranches,
useItems, useItems,
useCreateReceipt, useCreateReceipt,
useEditReceipt, useEditReceipt,
@@ -17,12 +19,9 @@ const ReceiptFormContext = createContext();
*/ */
function ReceiptFormProvider({ receiptId, ...props }) { function ReceiptFormProvider({ receiptId, ...props }) {
// Fetch sale receipt details. // Fetch sale receipt details.
const { data: receipt, isLoading: isReceiptLoading } = useReceipt( const { data: receipt, isLoading: isReceiptLoading } = useReceipt(receiptId, {
receiptId, enabled: !!receiptId,
{ });
enabled: !!receiptId,
},
);
// Fetch accounts list. // Fetch accounts list.
const { data: accounts, isLoading: isAccountsLoading } = useAccounts(); const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
@@ -32,12 +31,38 @@ function ReceiptFormProvider({ receiptId, ...props }) {
isLoading: isCustomersLoading, isLoading: isCustomersLoading,
} = useCustomers({ page_size: 10000 }); } = useCustomers({ page_size: 10000 });
// Fetch warehouses list.
const {
data: warehouses,
isLoading: isWarehouesLoading,
isSuccess: isWarehousesSuccess,
} = useWarehouses();
// Fetches the branches list.
const {
data: branches,
isLoading: isBranchesLoading,
isSuccess: isBranchesSuccess,
} = useBranches();
// Filter all sellable items only. // Filter all sellable items only.
const stringifiedFilterRoles = React.useMemo( const stringifiedFilterRoles = React.useMemo(
() => () =>
JSON.stringify([ JSON.stringify([
{ index: 1, fieldKey: 'sellable', value: true, condition: '&&', comparator: 'equals', }, {
{ index: 2, fieldKey: 'active', value: true, condition: '&&', comparator: 'equals' }, index: 1,
fieldKey: 'sellable',
value: true,
condition: '&&',
comparator: 'equals',
},
{
index: 2,
fieldKey: 'active',
value: true,
condition: '&&',
comparator: 'equals',
},
]), ]),
[], [],
); );
@@ -61,12 +86,16 @@ function ReceiptFormProvider({ receiptId, ...props }) {
const isNewMode = !receiptId; const isNewMode = !receiptId;
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
const provider = { const provider = {
receiptId, receiptId,
receipt, receipt,
accounts, accounts,
customers, customers,
items, items,
branches,
warehouses,
submitPayload, submitPayload,
isNewMode, isNewMode,
@@ -74,7 +103,12 @@ function ReceiptFormProvider({ receiptId, ...props }) {
isAccountsLoading, isAccountsLoading,
isCustomersLoading, isCustomersLoading,
isItemsLoading, isItemsLoading,
isWarehouesLoading,
isBranchesLoading,
isFeatureLoading,
isSettingLoading, isSettingLoading,
isBranchesSuccess,
isWarehousesSuccess,
createReceiptMutate, createReceiptMutate,
editReceiptMutate, editReceiptMutate,

View File

@@ -0,0 +1,127 @@
import React from 'react';
import intl from 'react-intl-universal';
import styled from 'styled-components';
import {
Alignment,
Navbar,
NavbarGroup,
NavbarDivider,
Button,
Classes,
} from '@blueprintjs/core';
import {
useSetPrimaryWarehouseToForm,
useSetPrimaryBranchToForm,
} from './utils';
import { useFeatureCan } from 'hooks/state';
import { Icon, BranchSelect, FeatureCan, WarehouseSelect } from 'components';
import { useReceiptFormContext } from './ReceiptFormProvider';
import { Features } from 'common';
/**
* Receipt form topbar .
* @returns {JSX.Element}
*/
export default function ReceiptFormTopbar() {
// Features guard.
const { featureCan } = useFeatureCan();
// Sets the primary warehouse to form.
useSetPrimaryWarehouseToForm();
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
// Can't display the navigation bar if warehouses or branches feature is not enabled.
if (!featureCan(Features.Warehouses) || !featureCan(Features.Branches)) {
return null;
}
return (
<Navbar className={'navbar--dashboard-topbar'}>
<NavbarGroup align={Alignment.LEFT}>
<FeatureCan feature={Features.Branches}>
<ReceiptFormSelectBranch />
</FeatureCan>
{featureCan(Features.Warehouses) && featureCan(Features.Branches) && (
<NavbarDivider />
)}
<FeatureCan feature={Features.Warehouses}>
<ReceiptFormSelectWarehouse />
</FeatureCan>
</NavbarGroup>
</Navbar>
);
}
/**
* Receipt select branch.
* @returns
*/
function ReceiptFormSelectBranch() {
// Receipt form context.
const { branches, isBranchesLoading } = useReceiptFormContext();
return isBranchesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<BranchSelect
name={'branch_id'}
branches={branches}
input={ReceiptBranchSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
/**
* Receipt select warehouse.
* @returns
*/
function ReceiptFormSelectWarehouse() {
// Receipt form context.
const { warehouses, isWarehouesLoading } = useReceiptFormContext();
return isWarehouesLoading ? (
<DetailsBarSkeletonBase className={Classes.SKELETON} />
) : (
<WarehouseSelect
name={'warehouse_id'}
warehouses={warehouses}
input={ReceiptWarehouseSelectButton}
popoverProps={{ minimal: true }}
/>
);
}
function ReceiptBranchSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.branch_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'branch-16'} iconSize={16} />}
/>
);
}
function ReceiptWarehouseSelectButton({ label }) {
return (
<Button
text={intl.get('invoice.warehouse_button.label', { label })}
minimal={true}
small={true}
icon={<Icon icon={'warehouse-16'} iconSize={16} />}
/>
);
}
const DetailsBarSkeletonBase = styled.div`
letter-spacing: 10px;
margin-right: 10px;
margin-left: 10px;
font-size: 8px;
width: 140px;
height: 10px;
`;

View File

@@ -3,13 +3,14 @@ import { useFormikContext } from 'formik';
import moment from 'moment'; import moment from 'moment';
import * as R from 'ramda'; import * as R from 'ramda';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { omit } from 'lodash'; import { omit, first } from 'lodash';
import { import {
defaultFastFieldShouldUpdate, defaultFastFieldShouldUpdate,
transactionNumber, transactionNumber,
repeatValue, repeatValue,
transformToForm, transformToForm,
} from 'utils'; } from 'utils';
import { useReceiptFormContext } from './ReceiptFormProvider';
import { import {
updateItemsEntriesTotal, updateItemsEntriesTotal,
ensureEntriesHaveEmptyLine, ensureEntriesHaveEmptyLine,
@@ -36,6 +37,8 @@ export const defaultReceipt = {
receipt_message: '', receipt_message: '',
statement: '', statement: '',
closed: '', closed: '',
branch_id: '',
warehouse_id: '',
entries: [...repeatValue(defaultReceiptEntry, MIN_LINES_NUMBER)], entries: [...repeatValue(defaultReceiptEntry, MIN_LINES_NUMBER)],
}; };
@@ -142,3 +145,34 @@ export const transformFormValuesToRequest = (values) => {
closed: false, closed: false,
}; };
}; };
export const useSetPrimaryWarehouseToForm = () => {
const { setFieldValue } = useFormikContext();
const { warehouses, isWarehousesSuccess } = useReceiptFormContext();
React.useEffect(() => {
if (isWarehousesSuccess) {
const primaryWarehouse =
warehouses.find((b) => b.primary) || first(warehouses);
if (primaryWarehouse) {
setFieldValue('warehouse_id', primaryWarehouse.id);
}
}
}, [isWarehousesSuccess, setFieldValue, warehouses]);
};
export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = useReceiptFormContext();
React.useEffect(() => {
if (isBranchesSuccess) {
const primaryBranch = branches.find((b) => b.primary) || first(branches);
if (primaryBranch) {
setFieldValue('branch_id', primaryBranch.id);
}
}
}, [isBranchesSuccess, setFieldValue, branches]);
};