Merge branch 'develop' into print-resources

This commit is contained in:
Ahmed Bouhuolia
2024-05-30 19:50:05 +02:00
154 changed files with 9748 additions and 6369 deletions

View File

@@ -32,6 +32,7 @@ import {
defaultManualJournal,
} from './utils';
import { JournalSyncIncrementSettingsToForm } from './components';
import { transformAttachmentsToRequest } from '@/containers/Attachments/utils';
/**
* Journal entries form.
@@ -61,7 +62,6 @@ function MakeJournalEntriesForm({
journalNumberPrefix,
journalNextNumber,
);
// Form initial values.
const initialValues = useMemo(
() => ({
@@ -112,6 +112,7 @@ function MakeJournalEntriesForm({
setSubmitting(false);
return;
}
const attachments = transformAttachmentsToRequest(values);
const form = {
...omit(values, ['journal_number_manually']),
...(values.journal_number_manually && {
@@ -119,6 +120,7 @@ function MakeJournalEntriesForm({
}),
entries: R.compose(orderingLinesIndexes)(entries),
publish: submitPayload.publish,
attachments,
};
// Handle the request error.
const handleError = ({

View File

@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Row, Col, Paper } from '@/components';
import { MakeJournalFormFooterLeft } from './MakeJournalFormFooterLeft';
import { MakeJournalFormFooterRight } from './MakeJournalFormFooterRight';
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
export default function MakeJournalFormFooter() {
return (
@@ -15,6 +16,7 @@ export default function MakeJournalFormFooter() {
<Row>
<Col md={8}>
<MakeJournalFormFooterLeft />
<UploadAttachmentButton />
</Col>
<Col md={4}>

View File

@@ -18,6 +18,7 @@ import { AppToaster } from '@/components';
import { useFormikContext } from 'formik';
import { useMakeJournalFormContext } from './MakeJournalProvider';
import { useCurrentOrganization } from '@/hooks/state';
import { transformAttachmentsToForm } from '@/containers/Attachments/utils';
const ERROR = {
JOURNAL_NUMBER_ALREADY_EXISTS: 'JOURNAL.NUMBER.ALREADY.EXISTS',
@@ -57,6 +58,7 @@ export const defaultManualJournal = {
branch_id: '',
exchange_rate: 1,
entries: [...repeatValue(defaultEntry, DEFAULT_LINES_NUMBER)],
attachments: [],
};
// Transform to edit form.
@@ -76,9 +78,12 @@ export function transformToEditForm(manualJournal) {
ensureEntriesHasEmptyLine(MIN_LINES_NUMBER, defaultEntry),
)(initialEntries);
const attachments = transformAttachmentsToForm(manualJournal);
return {
...transformToForm(manualJournal, defaultManualJournal),
entries,
attachments,
};
}

View File

@@ -0,0 +1,17 @@
.popover :global .bp4-popover-content{
min-width: 600px;
}
.attachmentButton:not([class*=bp4-intent-]) {
&,
&:hover{
background-color: #fff;
}
border: 1px solid rgb(206, 212, 218);
}
.attachmentField :global .bp4-label{
font-weight: 500;
font-size: 12px;
}

View File

@@ -0,0 +1,57 @@
// @ts-nocheck
import clsx from 'classnames';
import { Field, useFormikContext } from 'formik';
import {
Button,
Classes,
Popover,
PopoverInteractionKind,
} from '@blueprintjs/core';
import { FFormGroup } from '@/components';
import { UploadAttachmentsPopoverContent } from './UploadAttachmentsPopoverContent';
import { transformToCamelCase, transfromToSnakeCase } from '@/utils';
import styles from './UploadAttachmentButton.module.scss';
function UploadAttachmentButtonButtonContentField() {
return (
<Field name={'attachments'}>
{({ form: { setFieldValue }, field: { value } }) => (
<UploadAttachmentsPopoverContent
value={transformToCamelCase(value)}
onChange={(changedValue) => {
setFieldValue('attachments', transfromToSnakeCase(changedValue));
}}
/>
)}
</Field>
);
}
export function UploadAttachmentButton() {
const { values } = useFormikContext();
const uploadedFiles = values?.attachments?.length || 0;
return (
<FFormGroup
name={'attachments'}
label={'Attachments'}
className={styles.attachmentField}
fastField={true}
>
<Popover
interactionKind={PopoverInteractionKind.CLICK}
popoverClassName={clsx(styles.popover, Classes.POPOVER_CONTENT_SIZING)}
placement={'top-start'}
content={<UploadAttachmentButtonButtonContentField />}
>
<Button className={styles.attachmentButton}>
{uploadedFiles > 0 ? (
<>Upload attachments ({uploadedFiles})</>
) : (
<>Upload attachments</>
)}
</Button>
</Popover>
</FFormGroup>
);
}

View File

@@ -0,0 +1,63 @@
.content {
}
.hintText{
display: flex;
font-size: 11px;
margin-top: 6px;
color: #738091;
justify-content: space-between;
}
.attachments{
margin-top: 12px;
}
.attachmentItem{
border-top: 1px solid #D3D8DE;
border-left: 1px solid #D3D8DE;
border-right: 1px solid #D3D8DE;
padding: 10px 14px;
justify-content: space-between;
&:first-child {
border-radius: 3px 3px 0 0;
}
&:last-child{
border-radius: 0 0 3px 3px;
border-bottom: 1px solid #D3D8DE;
}
}
.attachmentFilenameText{
}
.attachmentSizeText,
.attachmentLoadingText{
font-size: 13px;
color: #738091;
}
.attachmentContent{
}
.attachmentIcon{
color: #626b7c;
}
.label{
font-size: 12px;
margin-bottom: 4px;
}
.dropzoneRoot{
min-height: 140px;
padding: 10px;
}
.attachmentIconWrap{
width: 20PX;
text-align: right;
}

View File

@@ -0,0 +1,224 @@
// @ts-nocheck
import { useState } from 'react';
import { isEmpty } from 'lodash';
import { Button, Intent, Text, Spinner } from '@blueprintjs/core';
import { Box, Group, Icon, Stack } from '@/components';
import {
ImportDropzoneField,
ImportDropzoneFieldProps,
} from '@/containers/Import/ImportDropzoneFile';
import { useUncontrolled } from '@/hooks/useUncontrolled';
import {
useGetPresignedUrlAttachment,
useUploadAttachments,
} from '@/hooks/query/attachments';
import styles from './UploadAttachmentPopoverContent.module.scss';
import { MIME_TYPES } from '@/components/Dropzone/mine-types';
import { formatBytes } from '@/utils/format-bytes';
interface AttachmentFileCommon {
originName: string;
key: string;
size: number;
mimeType: string;
}
interface AttachmentFileLoaded extends AttachmentFileCommon {}
interface AttachmentFileLoading extends AttachmentFileCommon {
loading: boolean;
}
type AttachmentFile = AttachmentFileLoaded | AttachmentFileLoading;
interface UploadAttachmentsPopoverContentProps {
initialValue?: AttachmentFile[];
value?: AttachmentFile[];
onChange?: (value: AttachmentFile[]) => void;
onUploadedChange?: (value: AttachmentFile[]) => void;
dropzoneFieldProps?: ImportDropzoneFieldProps;
}
/**
* Uploads and list the attachments with ability to delete particular attachment.
* @param {UploadAttachmentsPopoverContentProps}
*/
export function UploadAttachmentsPopoverContent({
initialValue,
value,
onChange,
onUploadedChange,
dropzoneFieldProps,
}: UploadAttachmentsPopoverContentProps) {
// Controlled/uncontrolled value state.
const [localFiles, handleFilesChange] = useUncontrolled<AttachmentFile[]>({
finalValue: [],
initialValue,
value,
onChange: onChange,
});
// Stops loading of the given attachment key and updates it to new key,
// that came from the server-side after uploading is done.
const stopLoadingAttachment = (
localFiles: AttachmentFile[],
internalKey: string,
newKey: string,
) => {
return localFiles.map((localFile) => {
if (localFile.key === internalKey) {
return {
...localFile,
key: newKey,
loading: false,
};
}
return localFile;
});
};
// Uploads the attachments.
const { mutateAsync: uploadAttachments } = useUploadAttachments({
onSuccess: (data) => {
const newLocalFiles = stopLoadingAttachment(
localFiles,
data.config.data.get('internalKey'),
data.data.data.key,
);
handleFilesChange(newLocalFiles);
onUploadedChange && onUploadedChange(newLocalFiles);
},
});
// Deletes the attachment of the given file key.
const handleClick = (key: string) => () => {
const updatedFiles = localFiles.filter((file, i) => file.key !== key);
handleFilesChange(updatedFiles);
onUploadedChange && onUploadedChange(updatedFiles);
};
// Handle change dropzone.
const handleChangeDropzone = (file: File) => {
const formData = new FormData();
const key = Date.now().toString();
formData.append('file', file);
formData.append('internalKey', key);
handleFilesChange([
{
originName: file.name,
size: file.size,
key,
loading: true,
},
...localFiles,
]);
uploadAttachments(formData);
};
return (
<div className={styles.content}>
<div>
<Text className={styles.label}>Attach documents</Text>
<Stack spacing={0}>
<ImportDropzoneField
uploadIcon={null}
value={null}
title={''}
classNames={{ root: styles.dropzoneRoot }}
onChange={handleChangeDropzone}
dropzoneProps={{
accept: [
MIME_TYPES.doc,
MIME_TYPES.docx,
MIME_TYPES.pdf,
MIME_TYPES.png,
MIME_TYPES.jpeg,
],
}}
{...dropzoneFieldProps}
/>
<Group className={styles.hintText}>
<Box>Maximum: 25MB</Box>
</Group>
</Stack>
{!isEmpty(localFiles) && (
<Stack spacing={0} className={styles.attachments}>
{localFiles.map((localFile: AttachmentFile, index: number) => (
<Group
position={'space-between'}
className={styles.attachmentItem}
key={index}
>
<Group spacing={14} className={styles.attachmentContent}>
<div className={styles.attachmentIconWrap}>
{localFile.loading ? (
<Spinner size={20} />
) : (
<Icon
icon={'media'}
iconSize={16}
className={styles.attachmentIcon}
/>
)}
</div>
<Stack spacing={2}>
<Text className={styles.attachmentFilenameText}>
{localFile.originName}
</Text>
{localFile.loading ? (
<Text className={styles.attachmentLoadingText}>
Loading...
</Text>
) : (
<Text className={styles.attachmentSizeText}>
{formatBytes(localFile.size)}
</Text>
)}
</Stack>
</Group>
{!localFile.loading && (
<Group spacing={2}>
<ViewButton fileKey={localFile.key} />
<Button
small
minimal
intent={Intent.DANGER}
onClick={handleClick(localFile.key)}
>
<Icon icon={'trash-16'} iconSize={16} />
</Button>
</Group>
)}
</Group>
))}
</Stack>
)}
</div>
</div>
);
}
const ViewButton = ({ fileKey }: { fileKey: string }) => {
const [isLoading, setLoading] = useState<boolean>(false);
const { mutateAsync: getAttachmentPresignedUrl } =
useGetPresignedUrlAttachment();
const handleViewBtnClick = (key: string) => () => {
setLoading(true);
getAttachmentPresignedUrl(key).then((data) => {
window.open(data.presigned_url);
setLoading(false);
});
};
return (
<Button
small
minimal
onClick={handleViewBtnClick(fileKey)}
disabled={isLoading}
intent={Intent.PRIMARY}
>
View
</Button>
);
};

View File

@@ -0,0 +1,19 @@
// @ts-nocheck
import { transformToForm } from '@/utils';
const attachmentReqSchema = {
key: '',
size: '',
origin_name: '',
mime_type: '',
};
export const transformAttachmentsToForm = (values) => {
return values.attachments?.map((attachment) =>
transformToForm(attachment, attachmentReqSchema),
);
};
export const transformAttachmentsToRequest = (values) => {
return values.attachments?.map((attachment) => ({ key: attachment.key }));
};

View File

@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Row, Col, Paper } from '@/components';
import { ExpenseFormFooterLeft } from './ExpenseFormFooterLeft';
import { ExpenseFormFooterRight } from './ExpenseFormFooterRight';
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
export default function ExpenseFormFooter() {
return (
@@ -15,6 +16,7 @@ export default function ExpenseFormFooter() {
<Row>
<Col md={8}>
<ExpenseFormFooterLeft />
<UploadAttachmentButton />
</Col>
<Col md={4}>

View File

@@ -18,6 +18,10 @@ import {
formattedAmount,
} from '@/utils';
import { useCurrentOrganization } from '@/hooks/state';
import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
const ERROR = {
EXPENSE_ALREADY_PUBLISHED: 'EXPENSE.ALREADY.PUBLISHED',
@@ -46,6 +50,7 @@ export const defaultExpense = {
branch_id: '',
exchange_rate: 1,
categories: [...repeatValue(defaultExpenseEntry, MIN_LINES_NUMBER)],
attachments: [],
};
/**
@@ -93,9 +98,12 @@ export const transformToEditForm = (
ensureEntriesHasEmptyLine(MIN_LINES_NUMBER, expenseEntry),
)(initialEntries);
const attachments = transformAttachmentsToForm(expense);
return {
...transformToForm(expense, defaultExpense),
categories,
attachments,
};
};
@@ -133,10 +141,12 @@ export const filterNonZeroEntries = (categories) => {
*/
export const transformFormValuesToRequest = (values) => {
const categories = filterNonZeroEntries(values.categories);
const attachments = transformAttachmentsToRequest(values);
return {
...values,
categories: R.compose(orderingLinesIndexes)(categories),
attachments,
};
};

View File

@@ -1,17 +1,22 @@
// @ts-nocheck
import { useRef } from 'react';
import { Button, Intent } from '@blueprintjs/core';
import clsx from 'classnames';
import { Box, Icon, Stack } from '@/components';
import { Dropzone, DropzoneProps } from '@/components/Dropzone';
import { MIME_TYPES } from '@/components/Dropzone/mine-types';
import styles from './ImportDropzone.module.css';
import { useUncontrolled } from '@/hooks/useUncontrolled';
import styles from './ImportDropzone.module.css';
interface ImportDropzoneFieldProps {
export interface ImportDropzoneFieldProps {
initialValue?: File;
value?: File;
onChange?: (file: File) => void;
dropzoneProps?: DropzoneProps;
uploadIcon?: JSX.Element;
title?: string;
subtitle?: string;
classNames?: Record<string, string>;
}
export function ImportDropzoneField({
@@ -19,6 +24,10 @@ export function ImportDropzoneField({
value,
onChange,
dropzoneProps,
uploadIcon = <Icon icon="download" iconSize={26} />,
title = 'Drag images here or click to select files',
subtitle = 'Drag and Drop file here or Choose file',
classNames,
}: ImportDropzoneFieldProps) {
const [localValue, handleChange] = useUncontrolled({
value,
@@ -38,15 +47,18 @@ export function ImportDropzoneField({
onReject={(files) => console.log('rejected files', files)}
maxSize={5 * 1024 ** 2}
accept={[MIME_TYPES.csv, MIME_TYPES.xls, MIME_TYPES.xlsx]}
classNames={{ content: styles.dropzoneContent }}
classNames={{ root: classNames?.root, content: styles.dropzoneContent }}
activateOnClick={false}
openRef={openRef}
{...dropzoneProps}
>
<Stack spacing={12} align="center" className={styles.content}>
<Box className={styles.iconWrap}>
<Icon icon="download" iconSize={26} />
</Box>
<Stack
spacing={12}
align="center"
className={clsx(styles.content, classNames?.content)}
>
{uploadIcon && <Box className={styles.iconWrap}>{uploadIcon}</Box>}
{localValue ? (
<Stack spacing={6} justify="center" align="center">
<h4 className={styles.title}>{localValue.name}</h4>
@@ -56,15 +68,10 @@ export function ImportDropzoneField({
</Stack>
) : (
<Stack spacing={4} align="center">
<h4 className={styles.title}>
Drag images here or click to select files
</h4>
<span className={styles.subtitle}>
Drag and Drop file here or Choose file
</span>
{title && <h4 className={styles.title}>{title}</h4>}
{subtitle && <span className={styles.subtitle}>{subtitle}</span>}
</Stack>
)}
<Button
intent="none"
onClick={() => openRef.current?.()}

View File

@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Paper, Row, Col } from '@/components';
import { BillFormFooterLeft } from './BillFormFooterLeft';
import { BillFormFooterRight } from './BillFormFooterRight';
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
// Bill form floating actions.
export default function BillFormFooter() {
@@ -16,6 +17,7 @@ export default function BillFormFooter() {
<Row>
<Col md={8}>
<BillFormFooterLeft />
<UploadAttachmentButton />
</Col>
<Col md={4}>

View File

@@ -27,6 +27,10 @@ import {
} from '@/containers/Entries/utils';
import { useBillFormContext } from './BillFormProvider';
import { TaxType } from '@/interfaces/TaxRates';
import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
export const MIN_LINES_NUMBER = 1;
@@ -60,6 +64,7 @@ export const defaultBill = {
exchange_rate: 1,
currency_code: '',
entries: [...repeatValue(defaultBillEntry, MIN_LINES_NUMBER)],
attachments: [],
};
export const ERRORS = {
@@ -88,12 +93,15 @@ export const transformToEditForm = (bill) => {
updateItemsEntriesTotal,
)(initialEntries);
const attachments = transformAttachmentsToForm(bill);
return {
...transformToForm(bill, defaultBill),
inclusive_exclusive_tax: bill.is_inclusive_tax
? TaxType.Inclusive
: TaxType.Exclusive,
entries,
attachments,
};
};
@@ -120,11 +128,13 @@ export const filterNonZeroEntries = (entries) => {
*/
export const transformFormValuesToRequest = (values) => {
const entries = filterNonZeroEntries(values.entries);
const attachments = transformAttachmentsToRequest(values);
return {
...values,
entries: transformEntriesToSubmit(entries),
open: false,
attachments,
};
};

View File

@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Row, Col, Paper } from '@/components';
import { VendorCreditNoteFormFooterLeft } from './VendorCreditNoteFormFooterLeft';
import { VendorCreditNoteFormFooterRight } from './VendorCreditNoteFormFooterRight';
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
/**
* Vendor Credit note form footer.
@@ -18,6 +19,7 @@ export default function VendorCreditNoteFormFooter() {
<Row>
<Col md={8}>
<VendorCreditNoteFormFooterLeft />
<UploadAttachmentButton />
</Col>
<Col md={4}>

View File

@@ -20,6 +20,10 @@ import { useFormikContext } from 'formik';
import { useVendorCreditNoteFormContext } from './VendorCreditNoteFormProvider';
import { useCurrentOrganization } from '@/hooks/state';
import { getEntriesTotal } from '@/containers/Entries/utils';
import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
export const MIN_LINES_NUMBER = 1;
@@ -48,6 +52,7 @@ export const defaultVendorsCreditNote = {
exchange_rate: 1,
currency_code: '',
entries: [...repeatValue(defaultCreditNoteEntry, MIN_LINES_NUMBER)],
attachments: [],
};
/**
@@ -68,9 +73,12 @@ export const transformToEditForm = (creditNote) => {
updateItemsEntriesTotal,
)(initialEntries);
const attachments = transformAttachmentsToForm(creditNote);
return {
...transformToForm(creditNote, defaultVendorsCreditNote),
entries,
attachments,
};
};
@@ -100,11 +108,13 @@ export const filterNonZeroEntries = (entries) => {
*/
export const transformFormValuesToRequest = (values) => {
const entries = filterNonZeroEntries(values.entries);
const attachments = transformAttachmentsToRequest(values);
return {
...values,
entries: transformEntriesToSubmit(entries),
open: false,
attachments,
};
};

View File

@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Row, Col, Paper } from '@/components';
import { PaymentMadeFormFooterLeft } from './PaymentMadeFormFooterLeft';
import { PaymentMadeFormFooterRight } from './PaymentMadeFormFooterRight';
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
/**
* Payment made form footer.
@@ -18,6 +19,7 @@ export default function PaymentMadeFooter() {
<Row>
<Col md={8}>
<PaymentMadeFormFooterLeft />
<UploadAttachmentButton />
</Col>
<Col md={4}>

View File

@@ -16,6 +16,10 @@ import {
} from '@/utils';
import { useCurrentOrganization } from '@/hooks/state';
import { PAYMENT_MADE_ERRORS } from '../constants';
import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
export const ERRORS = {
PAYMENT_NUMBER_NOT_UNIQUE: 'PAYMENT.NUMBER.NOT.UNIQUE',
@@ -44,9 +48,12 @@ export const defaultPaymentMade = {
branch_id: '',
exchange_rate: 1,
entries: [],
attachments: [],
};
export const transformToEditForm = (paymentMade, paymentMadeEntries) => {
const attachments = transformAttachmentsToForm(paymentMade);
return {
...transformToForm(paymentMade, defaultPaymentMade),
full_amount: safeSumBy(paymentMadeEntries, 'payment_amount'),
@@ -56,6 +63,7 @@ export const transformToEditForm = (paymentMade, paymentMadeEntries) => {
payment_amount: paymentMadeEntry.payment_amount || '',
})),
],
attachments,
};
};
@@ -101,7 +109,9 @@ export const transformFormToRequest = (form) => {
...pick(entry, ['payment_amount', 'bill_id']),
}));
return { ...form, entries: orderingLinesIndexes(entries) };
const attachments = transformAttachmentsToRequest(form);
return { ...form, entries: orderingLinesIndexes(entries), attachments };
};
export const useSetPrimaryBranchToForm = () => {

View File

@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Row, Col, Paper } from '@/components';
import { CreditNoteFormFooterLeft } from './CreditNoteFormFooterLeft';
import { CreditNoteFormFooterRight } from './CreditNoteFormFooterRight';
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
/**
* Credit note form footer.
@@ -18,6 +19,7 @@ export default function CreditNoteFormFooter() {
<Row>
<Col md={8}>
<CreditNoteFormFooterLeft />
<UploadAttachmentButton />
</Col>
<Col md={4}>

View File

@@ -8,7 +8,6 @@ import {
defaultFastFieldShouldUpdate,
transformToForm,
repeatValue,
transactionNumber,
formattedAmount,
orderingLinesIndexes,
} from '@/utils';
@@ -21,6 +20,10 @@ import {
} from '@/containers/Entries/utils';
import { useCurrentOrganization } from '@/hooks/state';
import { getEntriesTotal } from '@/containers/Entries/utils';
import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
export const MIN_LINES_NUMBER = 1;
@@ -51,6 +54,7 @@ export const defaultCreditNote = {
exchange_rate: 1,
currency_code: '',
entries: [...repeatValue(defaultCreditNoteEntry, MIN_LINES_NUMBER)],
attachments: []
};
/**
@@ -71,9 +75,12 @@ export function transformToEditForm(creditNote) {
updateItemsEntriesTotal,
)(initialEntries);
const attachment = transformAttachmentsToForm(creditNote);
return {
...transformToForm(creditNote, defaultCreditNote),
entries,
attachment,
};
}
@@ -103,11 +110,13 @@ export const filterNonZeroEntries = (entries) => {
*/
export const transformFormValuesToRequest = (values) => {
const entries = filterNonZeroEntries(values.entries);
const attachments = transformAttachmentsToRequest(values);
return {
...values,
entries: transformEntriesToSubmit(entries),
open: false,
attachments,
};
};

View File

@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Row, Col, Paper } from '@/components';
import { EstimateFormFooterLeft } from './EstimateFormFooterLeft';
import { EstimateFormFooterRight } from './EstimateFormFooterRight';
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
/**
* Estimate form footer.
@@ -18,6 +19,7 @@ export default function EstiamteFormFooter() {
<Row>
<Col md={8}>
<EstimateFormFooterLeft />
<UploadAttachmentButton />
</Col>
<Col md={4}>

View File

@@ -18,6 +18,10 @@ import {
} from '@/containers/Entries/utils';
import { useCurrentOrganization } from '@/hooks/state';
import { getEntriesTotal } from '@/containers/Entries/utils';
import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
export const MIN_LINES_NUMBER = 1;
@@ -56,6 +60,7 @@ export const defaultEstimate = {
exchange_rate: 1,
currency_code: '',
entries: [...repeatValue(defaultEstimateEntry, MIN_LINES_NUMBER)],
attachments: []
};
const ERRORS = {
@@ -78,9 +83,12 @@ export const transformToEditForm = (estimate) => {
updateItemsEntriesTotal,
)(initialEntries);
const attachments = transformAttachmentsToForm(estimate);
return {
...transformToForm(estimate, defaultEstimate),
entries,
attachments,
};
};
@@ -150,6 +158,8 @@ export const transfromsFormValuesToRequest = (values) => {
const entries = values.entries.filter(
(item) => item.item_id && item.quantity,
);
const attachments = transformAttachmentsToRequest(values);
return {
...omit(values, ['estimate_number_manually', 'estimate_number']),
// The `estimate_number_manually` will be presented just if the auto-increment
@@ -160,6 +170,7 @@ export const transfromsFormValuesToRequest = (values) => {
entries: entries.map((entry) => ({
...transformToForm(entry, defaultEstimateEntryReq),
})),
attachments,
};
};

View File

@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Paper, Row, Col } from '@/components';
import { InvoiceFormFooterLeft } from './InvoiceFormFooterLeft';
import { InvoiceFormFooterRight } from './InvoiceFormFooterRight';
import { UploadAttachmentButton } from '../../../Attachments/UploadAttachmentButton';
export default function InvoiceFormFooter() {
return (
@@ -15,6 +16,7 @@ export default function InvoiceFormFooter() {
<Row>
<Col md={8}>
<InvoiceFormFooterLeft />
<UploadAttachmentButton />
</Col>
<Col md={4}>

View File

@@ -5,7 +5,7 @@ import intl from 'react-intl-universal';
import moment from 'moment';
import * as R from 'ramda';
import { Intent } from '@blueprintjs/core';
import { omit, first, sumBy, round } from 'lodash';
import { omit, first, sumBy } from 'lodash';
import {
compose,
transformToForm,
@@ -27,6 +27,10 @@ import {
ensureEntriesHaveEmptyLine,
} from '@/containers/Entries/utils';
import { TaxType } from '@/interfaces/TaxRates';
import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
export const MIN_LINES_NUMBER = 1;
@@ -63,6 +67,7 @@ export const defaultInvoice = {
warehouse_id: '',
project_id: '',
entries: [...repeatValue(defaultInvoiceEntry, MIN_LINES_NUMBER)],
attachments: [],
};
/**
@@ -89,6 +94,7 @@ export function transformToEditForm(invoice) {
? TaxType.Inclusive
: TaxType.Exclusive,
entries,
attachments: transformAttachmentsToForm(invoice),
};
}
@@ -192,6 +198,7 @@ export function transformValueToRequest(values) {
...omit(entry, ['amount', 'tax_amount', 'tax_rate']),
})),
delivered: false,
attachments: transformAttachmentsToRequest(values),
};
}

View File

@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Row, Col, Paper } from '@/components';
import { PaymentReceiveFormFootetLeft } from './PaymentReceiveFormFootetLeft';
import { PaymentReceiveFormFootetRight } from './PaymentReceiveFormFootetRight';
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
/**
* Payment receive form footer.
@@ -18,6 +19,7 @@ export default function PaymentReceiveFormFooter() {
<Row>
<Col md={8}>
<PaymentReceiveFormFootetLeft />
<UploadAttachmentButton />
</Col>
<Col md={4}>

View File

@@ -9,13 +9,16 @@ import { AppToaster } from '@/components';
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
import {
defaultFastFieldShouldUpdate,
transactionNumber,
transformToForm,
safeSumBy,
orderingLinesIndexes,
formattedAmount,
} from '@/utils';
import { useCurrentOrganization } from '@/hooks/state';
import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
// Default payment receive entry.
export const defaultPaymentReceiveEntry = {
@@ -39,11 +42,12 @@ export const defaultPaymentReceive = {
// Holds the payment number that entered manually only.
payment_receive_no_manually: '',
statement: '',
full_amount: '',
full_amount: '',
currency_code: '',
branch_id: '',
exchange_rate: 1,
entries: [],
attachments: []
};
export const defaultRequestPaymentEntry = {
@@ -74,6 +78,7 @@ export const transformToEditForm = (paymentReceive, paymentReceiveEntries) => ({
payment_amount: paymentReceiveEntry.payment_amount || '',
})),
],
attachments: transformAttachmentsToForm(paymentReceive),
});
/**
@@ -155,6 +160,8 @@ export const transformFormToRequest = (form) => {
...pick(entry, Object.keys(defaultRequestPaymentEntry)),
}));
const attachments = transformAttachmentsToRequest(form);
return {
...omit(form, ['payment_receive_no_manually', 'payment_receive_no']),
// The `payment_receive_no_manually` will be presented just if the auto-increment
@@ -163,6 +170,7 @@ export const transformFormToRequest = (form) => {
payment_receive_no: form.payment_receive_no,
}),
entries: orderingLinesIndexes(entries),
attachments,
};
};

View File

@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Paper, Row, Col } from '@/components';
import { ReceiptFormFooterLeft } from './ReceiptFormFooterLeft';
import { ReceiptFormFooterRight } from './ReceiptFormFooterRight';
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
export default function ReceiptFormFooter({}) {
return (
@@ -15,6 +16,7 @@ export default function ReceiptFormFooter({}) {
<Row>
<Col md={8}>
<ReceiptFormFooterLeft />
<UploadAttachmentButton />
</Col>
<Col md={4}>

View File

@@ -18,6 +18,10 @@ import {
} from '@/containers/Entries/utils';
import { useCurrentOrganization } from '@/hooks/state';
import { getEntriesTotal } from '@/containers/Entries/utils';
import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
export const MIN_LINES_NUMBER = 1;
@@ -56,6 +60,7 @@ export const defaultReceipt = {
exchange_rate: 1,
currency_code: '',
entries: [...repeatValue(defaultReceiptEntry, MIN_LINES_NUMBER)],
attachments: [],
};
const ERRORS = {
@@ -81,9 +86,12 @@ export const transformToEditForm = (receipt) => {
updateItemsEntriesTotal,
)(initialEntries);
const attachments = transformAttachmentsToForm(receipt);
return {
...transformToForm(receipt, defaultReceipt),
entries,
attachments,
};
};
@@ -142,6 +150,7 @@ export const transformFormValuesToRequest = (values) => {
const entries = values.entries.filter(
(item) => item.item_id && item.quantity,
);
const attachments = transformAttachmentsToRequest(values);
return {
...omit(values, ['receipt_number_manually', 'receipt_number']),
@@ -152,6 +161,7 @@ export const transformFormValuesToRequest = (values) => {
...transformToForm(entry, defaultReceiptEntryReq),
})),
closed: false,
attachments,
};
};