feat(Sales & Purchases ): add branch & warehouse.

This commit is contained in:
elforjani13
2022-02-20 13:54:31 +02:00
parent 3ed2393cf1
commit b46c3f4843
35 changed files with 1184 additions and 28 deletions

View File

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

View File

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

View File

@@ -5,6 +5,8 @@ import {
useVendors,
useItems,
useBill,
useWarehouses,
useBranches,
useSettings,
useCreateBill,
useEditBill,
@@ -14,8 +16,20 @@ const BillFormContext = createContext();
// Filter all purchasable items only.
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,
});
// 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.
const { isFetching: isSettingLoading } = useSettings();
@@ -57,11 +85,16 @@ function BillFormProvider({ billId, ...props }) {
const isNewMode = !billId;
// Determines whether the warehouse and branches are loading.
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
const provider = {
accounts,
vendors,
items,
bill,
warehouses,
branches,
submitPayload,
isNewMode,
@@ -70,6 +103,9 @@ function BillFormProvider({ billId, ...props }) {
isAccountsLoading,
isItemsLoading,
isVendorsLoading,
isFeatureLoading,
isBranchesSuccess,
isWarehousesSuccess,
createBillMutate,
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 intl from 'react-intl-universal';
import * as R from 'ramda';
import { first } from 'lodash';
import { Intent } from '@blueprintjs/core';
import { useFormikContext } from 'formik';
import { AppToaster } from 'components';
import {
defaultFastFieldShouldUpdate,
@@ -14,6 +17,7 @@ import {
ensureEntriesHaveEmptyLine,
} from 'containers/Entries/utils';
import { isLandedCostDisabled } from '../../../Entries/utils';
import { useBillFormContext } from './BillFormProvider';
export const MIN_LINES_NUMBER = 4;
@@ -38,6 +42,8 @@ export const defaultBill = {
reference_no: '',
note: '',
open: '',
branch_id: '',
warehouse_id: '',
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 VendorCreditNoteFloatingActions from './VendorCreditNoteFloatingActions';
import VendorCreditNoteFormDialogs from './VendorCreditNoteFormDialogs';
import VendorCreditNoteFormTopBar from './VendorCreditNoteFormTopBar';
import { useVendorCreditNoteFormContext } from './VendorCreditNoteFormProvider';
@@ -151,6 +152,7 @@ function VendorCreditNoteForm({
onSubmit={handleFormSubmit}
>
<Form>
<VendorCreditNoteFormTopBar />
<VendorCreditNoteFormHeader />
<VendorCreditNoteItemsEntriesEditor />
<VendorCreditNoteFormFooter />

View File

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

View File

@@ -7,6 +7,8 @@ import {
useCreateVendorCredit,
useEditVendorCredit,
useVendorCredit,
useWarehouses,
useBranches,
useItems,
useVendors,
useSettingsVendorCredits,
@@ -51,6 +53,20 @@ function VendorCreditNoteFormProvider({ vendorCreditId, ...props }) {
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.
const [submitPayload, setSubmitPayload] = React.useState();
@@ -61,6 +77,9 @@ function VendorCreditNoteFormProvider({ vendorCreditId, ...props }) {
// Determines whether the form in new mode.
const isNewMode = !vendorCreditId;
// Determines whether the warehouse and branches are loading.
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
const newVendorCredit = !isEmpty(bill)
? transformToEditForm({
...pick(bill, ['vendor_id', 'entries']),
@@ -72,12 +91,17 @@ function VendorCreditNoteFormProvider({ vendorCreditId, ...props }) {
items,
vendors,
vendorCredit,
warehouses,
branches,
submitPayload,
isNewMode,
newVendorCredit,
isVendorCreditLoading,
isFeatureLoading,
isBranchesSuccess,
isWarehousesSuccess,
createVendorCreditMutate,
editVendorCreditMutate,
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 * as R from 'ramda';
import moment from 'moment';
import { first } from 'lodash';
import {
defaultFastFieldShouldUpdate,
@@ -14,6 +15,7 @@ import {
ensureEntriesHaveEmptyLine,
} from 'containers/Entries/utils';
import { useFormikContext } from 'formik';
import { useVendorCreditNoteFormContext } from './VendorCreditNoteFormProvider';
export const MIN_LINES_NUMBER = 4;
@@ -37,6 +39,8 @@ export const defaultVendorsCreditNote = {
vendor_credit_date: moment(new Date()).format('YYYY-MM-DD'),
reference_no: '',
note: '',
branch_id: '',
warehouse_id: '',
entries: [...repeatValue(defaultCreditNoteEntry, MIN_LINES_NUMBER)],
};
@@ -129,3 +133,34 @@ export const useObserveVendorCreditNoSettings = (prefix, nextNumber) => {
setFieldValue('vendor_credit_number', creditNo);
}, [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 PaymentMadeFooter from './PaymentMadeFooter';
import PaymentMadeFormBody from './PaymentMadeFormBody';
import PaymentMadeFormTopBar from './PaymentMadeFormTopBar';
import { PaymentMadeInnerProvider } from './PaymentMadeInnerProvider';
import withSettings from 'containers/Settings/withSettings';
@@ -145,6 +146,7 @@ function PaymentMadeForm({
>
<Form>
<PaymentMadeInnerProvider>
<PaymentMadeFormTopBar />
<PaymentMadeHeader />
<PaymentMadeFormBody />
<PaymentMadeFooter />

View File

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

View File

@@ -3,6 +3,7 @@ import {
useAccounts,
useVendors,
useItems,
useBranches,
usePaymentMadeEditPage,
useSettings,
useCreatePaymentMade,
@@ -45,6 +46,13 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
enabled: !!paymentMadeId,
});
// Fetches the branches list.
const {
data: branches,
isLoading: isBranchesLoading,
isSuccess: isBranchesSuccess,
} = useBranches();
// Fetch payment made settings.
useSettings();
@@ -54,6 +62,8 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
const isNewMode = !paymentMadeId;
const isFeatureLoading = isBranchesLoading;
// Provider payload.
const provider = {
paymentMadeId,
@@ -62,6 +72,7 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
paymentMadeEditPage,
vendors,
items,
branches,
submitPayload,
paymentVendorId,
@@ -72,6 +83,8 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
isVendorsLoading,
isPaymentFetching,
isPaymentLoading,
isFeatureLoading,
isBranchesSuccess,
createPaymentMadeMutate,
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 { pick } from 'lodash';
import { pick, first } from 'lodash';
import { useFormikContext } from 'formik';
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
import {
defaultFastFieldShouldUpdate,
safeSumBy,
@@ -31,6 +34,7 @@ export const defaultPaymentMade = {
payment_number: '',
statement: '',
currency_code: '',
branch_id: '',
entries: [],
};
@@ -91,3 +95,18 @@ export const transformFormToRequest = (form) => {
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]);
};