feat: wip UI upload attachments

This commit is contained in:
Ahmed Bouhuolia
2024-05-28 23:34:51 +02:00
parent fcd61c6159
commit cfdbcea9c0
49 changed files with 286 additions and 67 deletions

View File

@@ -158,6 +158,8 @@ export default class SalesReceiptsController extends BaseController {
.toInt(), .toInt(),
check('receipt_message').optional().trim().escape(), check('receipt_message').optional().trim().escape(),
check('statement').optional().trim().escape(), check('statement').optional().trim().escape(),
check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(),
]; ];
} }

View File

@@ -236,7 +236,7 @@ export default class SaleEstimate extends mixin(TenantModel, [
to: 'documents.id', to: 'documents.id',
}, },
filter(query) { filter(query) {
query.where('model_ref', 'Expense'); query.where('model_ref', 'SaleEstimate');
}, },
}, },
}; };

View File

@@ -540,7 +540,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
to: 'documents.id', to: 'documents.id',
}, },
filter(query) { filter(query) {
query.where('model_ref', 'Expense'); query.where('model_ref', 'SaleInvoice');
}, },
}, },
}; };

View File

@@ -99,8 +99,6 @@ export class AttachmentsOnManualJournals {
manualJournalDTO, manualJournalDTO,
manualJournal, manualJournal,
}: IManualJournalEventEditedPayload) { }: IManualJournalEventEditedPayload) {
// if (isEmpty(saleInvoiceDTO.attachments)) return;
const keys = manualJournalDTO.attachments?.map( const keys = manualJournalDTO.attachments?.map(
(attachment) => attachment.key (attachment) => attachment.key
); );

View File

@@ -2,6 +2,7 @@ import { Transformer } from '@/lib/Transformer/Transformer';
import { formatNumber } from 'utils'; import { formatNumber } from 'utils';
import { ItemEntryTransformer } from '../Sales/Invoices/ItemEntryTransformer'; import { ItemEntryTransformer } from '../Sales/Invoices/ItemEntryTransformer';
import { ICreditNote } from '@/interfaces'; import { ICreditNote } from '@/interfaces';
import { AttachmentTransformer } from '../Attachments/AttachmentTransformer';
export class CreditNoteTransformer extends Transformer { export class CreditNoteTransformer extends Transformer {
/** /**
@@ -16,6 +17,7 @@ export class CreditNoteTransformer extends Transformer {
'formattedCreditsUsed', 'formattedCreditsUsed',
'formattedSubtotal', 'formattedSubtotal',
'entries', 'entries',
'attachments',
]; ];
}; };
@@ -80,4 +82,13 @@ export class CreditNoteTransformer extends Transformer {
currencyCode: credit.currencyCode, currencyCode: credit.currencyCode,
}); });
}; };
/**
* Retrieves the credit note attachments.
* @param {ISaleInvoice} invoice
* @returns
*/
protected attachments = (creditNote) => {
return this.item(creditNote.attachments, new AttachmentTransformer());
};
} }

View File

@@ -78,7 +78,7 @@ export class EditManualJournal {
return { return {
id: oldManualJournal.id, id: oldManualJournal.id,
...omit(manualJournalDTO, ['publish']), ...omit(manualJournalDTO, ['publish', 'attachments']),
...(manualJournalDTO.publish && !oldManualJournal.publishedAt ...(manualJournalDTO.publish && !oldManualJournal.publishedAt
? { publishedAt: moment().toMySqlDateTime() } ? { publishedAt: moment().toMySqlDateTime() }
: {}), : {}),
@@ -143,6 +143,7 @@ export class EditManualJournal {
tenantId, tenantId,
manualJournal, manualJournal,
oldManualJournal, oldManualJournal,
manualJournalDTO,
trx, trx,
} as IManualJournalEventEditedPayload); } as IManualJournalEventEditedPayload);

View File

@@ -28,7 +28,7 @@ export class GetManualJournal {
.withGraphFetched('entries.contact') .withGraphFetched('entries.contact')
.withGraphFetched('entries.branch') .withGraphFetched('entries.branch')
.withGraphFetched('transactions') .withGraphFetched('transactions')
.withGraphFetched('media') .withGraphFetched('attachments')
.throwIfNotFound(); .throwIfNotFound();
return this.transformer.transform( return this.transformer.transform(

View File

@@ -1,6 +1,7 @@
import { IManualJournal } from '@/interfaces'; import { IManualJournal } from '@/interfaces';
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { formatNumber } from 'utils'; import { formatNumber } from 'utils';
import { AttachmentTransformer } from '../Attachments/AttachmentTransformer';
export class ManualJournalTransfromer extends Transformer { export class ManualJournalTransfromer extends Transformer {
/** /**
@@ -8,7 +9,12 @@ export class ManualJournalTransfromer extends Transformer {
* @returns {Array} * @returns {Array}
*/ */
public includeAttributes = (): string[] => { public includeAttributes = (): string[] => {
return ['formattedAmount', 'formattedDate', 'formattedPublishedAt']; return [
'formattedAmount',
'formattedDate',
'formattedPublishedAt',
'attachments',
];
}; };
/** /**
@@ -39,4 +45,13 @@ export class ManualJournalTransfromer extends Transformer {
protected formattedPublishedAt = (manualJorunal: IManualJournal): string => { protected formattedPublishedAt = (manualJorunal: IManualJournal): string => {
return this.formatDate(manualJorunal.publishedAt); return this.formatDate(manualJorunal.publishedAt);
}; };
/**
* Retrieves the manual journal attachments.
* @param {ISaleInvoice} invoice
* @returns
*/
protected attachments = (manualJorunal: IManualJournal) => {
return this.item(manualJorunal.attachments, new AttachmentTransformer());
};
} }

View File

@@ -2,6 +2,7 @@ import { IBillPayment } from '@/interfaces';
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { formatNumber } from 'utils'; import { formatNumber } from 'utils';
import { BillPaymentEntryTransformer } from './BillPaymentEntryTransformer'; import { BillPaymentEntryTransformer } from './BillPaymentEntryTransformer';
import { AttachmentTransformer } from '@/services/Attachments/AttachmentTransformer';
export class BillPaymentTransformer extends Transformer { export class BillPaymentTransformer extends Transformer {
/** /**
@@ -9,7 +10,12 @@ export class BillPaymentTransformer extends Transformer {
* @returns {Array} * @returns {Array}
*/ */
public includeAttributes = (): string[] => { public includeAttributes = (): string[] => {
return ['formattedPaymentDate', 'formattedAmount', 'entries']; return [
'formattedPaymentDate',
'formattedAmount',
'entries',
'attachments',
];
}; };
/** /**
@@ -38,4 +44,13 @@ export class BillPaymentTransformer extends Transformer {
protected entries = (billPayment) => { protected entries = (billPayment) => {
return this.item(billPayment.entries, new BillPaymentEntryTransformer()); return this.item(billPayment.entries, new BillPaymentEntryTransformer());
}; };
/**
* Retrieves the bill attachments.
* @param {ISaleInvoice} invoice
* @returns
*/
protected attachments = (billPayment) => {
return this.item(billPayment.attachments, new AttachmentTransformer());
};
} }

View File

@@ -1,6 +1,6 @@
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
import * as R from 'ramda'; import * as R from 'ramda';
import { sumBy } from 'lodash'; import { omit, sumBy } from 'lodash';
import { IBillPayment, IBillPaymentDTO, IVendor } from '@/interfaces'; import { IBillPayment, IBillPaymentDTO, IVendor } from '@/interfaces';
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform'; import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
import { formatDateFields } from '@/utils'; import { formatDateFields } from '@/utils';
@@ -24,7 +24,9 @@ export class CommandBillPaymentDTOTransformer {
oldBillPayment?: IBillPayment oldBillPayment?: IBillPayment
): Promise<IBillPayment> { ): Promise<IBillPayment> {
const initialDTO = { const initialDTO = {
...formatDateFields(billPaymentDTO, ['paymentDate']), ...formatDateFields(omit(billPaymentDTO, ['attachments']), [
'paymentDate',
]),
amount: sumBy(billPaymentDTO.entries, 'paymentAmount'), amount: sumBy(billPaymentDTO.entries, 'paymentAmount'),
currencyCode: vendor.currencyCode, currencyCode: vendor.currencyCode,
exchangeRate: billPaymentDTO.exchangeRate || 1, exchangeRate: billPaymentDTO.exchangeRate || 1,

View File

@@ -13,7 +13,7 @@ export class GetBillPayment {
private transformer: TransformerInjectable; private transformer: TransformerInjectable;
/** /**
* Retrieve bill payment. * Retrieves bill payment.
* @param {number} tenantId * @param {number} tenantId
* @param {number} billPyamentId * @param {number} billPyamentId
* @return {Promise<IBillPayment>} * @return {Promise<IBillPayment>}
@@ -30,6 +30,7 @@ export class GetBillPayment {
.withGraphFetched('paymentAccount') .withGraphFetched('paymentAccount')
.withGraphFetched('transactions') .withGraphFetched('transactions')
.withGraphFetched('branch') .withGraphFetched('branch')
.withGraphFetched('attachments')
.findById(billPyamentId) .findById(billPyamentId)
.throwIfNotFound(); .throwIfNotFound();

View File

@@ -29,7 +29,8 @@ export class GetBill {
.withGraphFetched('vendor') .withGraphFetched('vendor')
.withGraphFetched('entries.item') .withGraphFetched('entries.item')
.withGraphFetched('branch') .withGraphFetched('branch')
.withGraphFetched('taxes.taxRate'); .withGraphFetched('taxes.taxRate')
.withGraphFetched('attachments');
// Validates the bill existance. // Validates the bill existance.
this.validators.validateBillExistance(bill); this.validators.validateBillExistance(bill);

View File

@@ -1,5 +1,6 @@
import { IBill } from '@/interfaces'; import { IBill } from '@/interfaces';
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { AttachmentTransformer } from '@/services/Attachments/AttachmentTransformer';
import { ItemEntryTransformer } from '@/services/Sales/Invoices/ItemEntryTransformer'; import { ItemEntryTransformer } from '@/services/Sales/Invoices/ItemEntryTransformer';
import { SaleInvoiceTaxEntryTransformer } from '@/services/Sales/Invoices/SaleInvoiceTaxEntryTransformer'; import { SaleInvoiceTaxEntryTransformer } from '@/services/Sales/Invoices/SaleInvoiceTaxEntryTransformer';
import { formatNumber } from 'utils'; import { formatNumber } from 'utils';
@@ -26,6 +27,7 @@ export class PurchaseInvoiceTransformer extends Transformer {
'totalLocalFormatted', 'totalLocalFormatted',
'taxes', 'taxes',
'entries', 'entries',
'attachments',
]; ];
}; };
@@ -192,4 +194,13 @@ export class PurchaseInvoiceTransformer extends Transformer {
currencyCode: bill.currencyCode, currencyCode: bill.currencyCode,
}); });
}; };
/**
* Retrieves the bill attachments.
* @param {ISaleInvoice} invoice
* @returns
*/
protected attachments = (bill) => {
return this.item(bill.attachments, new AttachmentTransformer());
};
} }

View File

@@ -64,7 +64,7 @@ export default class BaseVendorCredit {
autoNextNumber; autoNextNumber;
const initialDTO = { const initialDTO = {
...omit(vendorCreditDTO, ['open']), ...omit(vendorCreditDTO, ['open', 'attachments']),
amount, amount,
currencyCode: vendorCurrencyCode, currencyCode: vendorCurrencyCode,
exchangeRate: vendorCreditDTO.exchangeRate || 1, exchangeRate: vendorCreditDTO.exchangeRate || 1,

View File

@@ -26,7 +26,8 @@ export default class GetVendorCredit {
.findById(vendorCreditId) .findById(vendorCreditId)
.withGraphFetched('entries.item') .withGraphFetched('entries.item')
.withGraphFetched('vendor') .withGraphFetched('vendor')
.withGraphFetched('branch'); .withGraphFetched('branch')
.withGraphFetched('attachments');
if (!vendorCredit) { if (!vendorCredit) {
throw new ServiceError(ERRORS.VENDOR_CREDIT_NOT_FOUND); throw new ServiceError(ERRORS.VENDOR_CREDIT_NOT_FOUND);

View File

@@ -1,5 +1,6 @@
import { IVendorCredit } from '@/interfaces'; import { IVendorCredit } from '@/interfaces';
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { AttachmentTransformer } from '@/services/Attachments/AttachmentTransformer';
import { ItemEntryTransformer } from '@/services/Sales/Invoices/ItemEntryTransformer'; import { ItemEntryTransformer } from '@/services/Sales/Invoices/ItemEntryTransformer';
import { formatNumber } from 'utils'; import { formatNumber } from 'utils';
@@ -16,6 +17,7 @@ export class VendorCreditTransformer extends Transformer {
'formattedCreditsRemaining', 'formattedCreditsRemaining',
'formattedInvoicedAmount', 'formattedInvoicedAmount',
'entries', 'entries',
'attachments',
]; ];
}; };
@@ -80,4 +82,13 @@ export class VendorCreditTransformer extends Transformer {
currencyCode: vendorCredit.currencyCode, currencyCode: vendorCredit.currencyCode,
}); });
}; };
/**
* Retrieves the vendor credit attachments.
* @param {IVendorCredit} invoice
* @returns
*/
protected attachments = (vendorCredit) => {
return this.item(vendorCredit.attachments, new AttachmentTransformer());
};
} }

View File

@@ -28,7 +28,8 @@ export class GetSaleEstimate {
.findById(estimateId) .findById(estimateId)
.withGraphFetched('entries.item') .withGraphFetched('entries.item')
.withGraphFetched('customer') .withGraphFetched('customer')
.withGraphFetched('branch'); .withGraphFetched('branch')
.withGraphFetched('attachments');
// Validates the estimate existance. // Validates the estimate existance.
this.validators.validateEstimateExistance(estimate); this.validators.validateEstimateExistance(estimate);

View File

@@ -2,6 +2,7 @@ import { ISaleEstimate } from '@/interfaces';
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { formatNumber } from 'utils'; import { formatNumber } from 'utils';
import { ItemEntryTransformer } from '../Invoices/ItemEntryTransformer'; import { ItemEntryTransformer } from '../Invoices/ItemEntryTransformer';
import { AttachmentTransformer } from '@/services/Attachments/AttachmentTransformer';
export class SaleEstimateTransfromer extends Transformer { export class SaleEstimateTransfromer extends Transformer {
/** /**
@@ -18,6 +19,7 @@ export class SaleEstimateTransfromer extends Transformer {
'formattedApprovedAtDate', 'formattedApprovedAtDate',
'formattedRejectedAtDate', 'formattedRejectedAtDate',
'entries', 'entries',
'attachments',
]; ];
}; };
@@ -91,9 +93,18 @@ export class SaleEstimateTransfromer extends Transformer {
* @param {ISaleEstimate} estimate * @param {ISaleEstimate} estimate
* @returns {} * @returns {}
*/ */
protected entries = (estimate) => { protected entries = (estimate: ISaleEstimate) => {
return this.item(estimate.entries, new ItemEntryTransformer(), { return this.item(estimate.entries, new ItemEntryTransformer(), {
currencyCode: estimate.currencyCode, currencyCode: estimate.currencyCode,
}); });
}; };
/**
* Retrieves the sale estimate attachments.
* @param {ISaleInvoice} invoice
* @returns
*/
protected attachments = (estimate: ISaleEstimate) => {
return this.item(estimate.attachments, new AttachmentTransformer());
};
} }

View File

@@ -101,6 +101,7 @@ export class CreateSaleReceipt {
tenantId, tenantId,
saleReceipt, saleReceipt,
saleReceiptId: saleReceipt.id, saleReceiptId: saleReceipt.id,
saleReceiptDTO,
trx, trx,
} as ISaleReceiptCreatedPayload); } as ISaleReceiptCreatedPayload);

View File

@@ -110,6 +110,7 @@ export class EditSaleReceipt {
oldSaleReceipt, oldSaleReceipt,
saleReceipt, saleReceipt,
saleReceiptId, saleReceiptId,
saleReceiptDTO,
trx, trx,
} as ISaleReceiptEditedPayload); } as ISaleReceiptEditedPayload);

View File

@@ -28,7 +28,8 @@ export class GetSaleReceipt {
.withGraphFetched('entries.item') .withGraphFetched('entries.item')
.withGraphFetched('customer') .withGraphFetched('customer')
.withGraphFetched('depositAccount') .withGraphFetched('depositAccount')
.withGraphFetched('branch'); .withGraphFetched('branch')
.withGraphFetched('attachments');
// Valdiates the sale receipt existance. // Valdiates the sale receipt existance.
this.validators.validateReceiptExistance(saleReceipt); this.validators.validateReceiptExistance(saleReceipt);

View File

@@ -68,9 +68,10 @@ export class SaleReceiptDTOTransformer {
const initialDTO = { const initialDTO = {
amount, amount,
...formatDateFields(omit(saleReceiptDTO, ['closed', 'entries']), [ ...formatDateFields(
'receiptDate', omit(saleReceiptDTO, ['closed', 'entries', 'attachments']),
]), ['receiptDate']
),
currencyCode: paymentCustomer.currencyCode, currencyCode: paymentCustomer.currencyCode,
exchangeRate: saleReceiptDTO.exchangeRate || 1, exchangeRate: saleReceiptDTO.exchangeRate || 1,
receiptNumber, receiptNumber,

View File

@@ -3,6 +3,7 @@ import { ISaleReceipt } from '@/interfaces';
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { formatNumber } from 'utils'; import { formatNumber } from 'utils';
import { ItemEntryTransformer } from '../Invoices/ItemEntryTransformer'; import { ItemEntryTransformer } from '../Invoices/ItemEntryTransformer';
import { AttachmentTransformer } from '@/services/Attachments/AttachmentTransformer';
@Service() @Service()
export class SaleReceiptTransformer extends Transformer { export class SaleReceiptTransformer extends Transformer {
@@ -17,6 +18,7 @@ export class SaleReceiptTransformer extends Transformer {
'formattedReceiptDate', 'formattedReceiptDate',
'formattedClosedAtDate', 'formattedClosedAtDate',
'entries', 'entries',
'attachments',
]; ];
}; };
@@ -68,4 +70,13 @@ export class SaleReceiptTransformer extends Transformer {
currencyCode: receipt.currencyCode, currencyCode: receipt.currencyCode,
}); });
}; };
/**
* Retrieves the sale receipt attachments.
* @param {ISaleReceipt} invoice
* @returns
*/
protected attachments = (receipt) => {
return this.item(receipt.attachments, new AttachmentTransformer());
};
} }

View File

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

View File

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

View File

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

View File

@@ -9,6 +9,7 @@ import {
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { FFormGroup } from '@/components'; import { FFormGroup } from '@/components';
import { UploadAttachmentsPopoverContent } from './UploadAttachmentsPopoverContent'; import { UploadAttachmentsPopoverContent } from './UploadAttachmentsPopoverContent';
import { transformToCamelCase, transfromToSnakeCase } from '@/utils';
import styles from './UploadAttachmentButton.module.scss'; import styles from './UploadAttachmentButton.module.scss';
function UploadAttachmentButtonButtonContentField() { function UploadAttachmentButtonButtonContentField() {
@@ -16,9 +17,9 @@ function UploadAttachmentButtonButtonContentField() {
<Field name={'attachments'}> <Field name={'attachments'}>
{({ form: { setFieldValue }, field: { value } }) => ( {({ form: { setFieldValue }, field: { value } }) => (
<UploadAttachmentsPopoverContent <UploadAttachmentsPopoverContent
value={value} value={transformToCamelCase(value)}
onChange={(value) => { onChange={(changedValue) => {
setFieldValue('attachments', value); setFieldValue('attachments', transfromToSnakeCase(changedValue));
}} }}
/> />
)} )}
@@ -35,6 +36,7 @@ export function UploadAttachmentButton() {
name={'attachments'} name={'attachments'}
label={'Attachments'} label={'Attachments'}
className={styles.attachmentField} className={styles.attachmentField}
fastField={true}
> >
<Popover <Popover
interactionKind={PopoverInteractionKind.CLICK} interactionKind={PopoverInteractionKind.CLICK}

View File

@@ -1,16 +1,16 @@
// @ts-nocheck // @ts-nocheck
import { useState } from 'react';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { Button, Intent, Text, Spinner } from '@blueprintjs/core'; import { Button, Intent, Text, Spinner } from '@blueprintjs/core';
import { Box, Group, Icon, Stack } from '@/components'; import { Box, Group, Icon, Stack } from '@/components';
import { ImportDropzoneField } from '@/containers/Import/ImportDropzoneFile';
import { useUncontrolled } from '@/hooks/useUncontrolled';
import { import {
useDeleteAttachment, ImportDropzoneField,
useUploadAttachments, ImportDropzoneFieldProps,
} from '@/hooks/query/attachments'; } from '@/containers/Import/ImportDropzoneFile';
import { formatBytes } from './utils'; import { useUncontrolled } from '@/hooks/useUncontrolled';
import { useUploadAttachments } from '@/hooks/query/attachments';
import { formatBytes } from '../Sales/Invoices/InvoiceForm/utils';
import styles from './UploadAttachmentPopoverContent.module.scss'; import styles from './UploadAttachmentPopoverContent.module.scss';
import { MIME_TYPES } from '@/components/Dropzone/mine-types';
interface AttachmentFileCommon { interface AttachmentFileCommon {
originName: string; originName: string;
@@ -28,6 +28,8 @@ interface UploadAttachmentsPopoverContentProps {
initialValue?: AttachmentFile[]; initialValue?: AttachmentFile[];
value?: AttachmentFile[]; value?: AttachmentFile[];
onChange?: (value: AttachmentFile[]) => void; onChange?: (value: AttachmentFile[]) => void;
onUploadedChange?: (value: AttachmentFile[]) => void;
dropzoneFieldProps?: ImportDropzoneFieldProps;
} }
/** /**
@@ -38,6 +40,8 @@ export function UploadAttachmentsPopoverContent({
initialValue, initialValue,
value, value,
onChange, onChange,
onUploadedChange,
dropzoneFieldProps,
}: UploadAttachmentsPopoverContentProps) { }: UploadAttachmentsPopoverContentProps) {
// Controlled/uncontrolled value state. // Controlled/uncontrolled value state.
const [localFiles, handleFilesChange] = useUncontrolled<AttachmentFile[]>({ const [localFiles, handleFilesChange] = useUncontrolled<AttachmentFile[]>({
@@ -66,45 +70,23 @@ export function UploadAttachmentsPopoverContent({
}; };
// Uploads the attachments. // Uploads the attachments.
const { mutateAsync: uploadAttachments } = useUploadAttachments({ const { mutateAsync: uploadAttachments } = useUploadAttachments({
onSuccess: (data, variables, context) => { onSuccess: (data) => {
const newLocalFiles = stopLoadingAttachment( const newLocalFiles = stopLoadingAttachment(
localFiles, localFiles,
data.config.data.get('internalKey'), data.config.data.get('internalKey'),
data.data.data.key, data.data.data.key,
); );
handleFilesChange(newLocalFiles); handleFilesChange(newLocalFiles);
onUploadedChange && onUploadedChange(newLocalFiles);
}, },
}); });
// Deletes the attachment.
const { mutateAsync: deleteAttachment } = useDeleteAttachment();
// Deletes the attachment of the given file key. // Deletes the attachment of the given file key.
const DeleteButton = ({ fileKey }: { fileKey: string }) => { const handleClick = (key: string) => () => {
const [loading, setLoading] = useState<boolean>(false); const updatedFiles = localFiles.filter((file, i) => file.key !== key);
handleFilesChange(updatedFiles);
const handleClick = () => { onUploadedChange && onUploadedChange(updatedFiles);
setLoading(true);
deleteAttachment(fileKey).then(() => {
const updatedFiles = localFiles.filter(
(file, i) => file.key !== fileKey,
);
handleFilesChange(updatedFiles);
setLoading(false);
});
};
return (
<Button
small
minimal
intent={Intent.DANGER}
loading={loading}
disabled={loading}
onClick={handleClick}
>
<Icon icon={'trash-16'} iconSize={16} />
</Button>
);
}; };
// Handle change dropzone. // Handle change dropzone.
const handleChangeDropzone = (file: File) => { const handleChangeDropzone = (file: File) => {
const formData = new FormData(); const formData = new FormData();
@@ -136,6 +118,16 @@ export function UploadAttachmentsPopoverContent({
title={''} title={''}
classNames={{ root: styles.dropzoneRoot }} classNames={{ root: styles.dropzoneRoot }}
onChange={handleChangeDropzone} onChange={handleChangeDropzone}
dropzoneProps={{
accept: [
MIME_TYPES.doc,
MIME_TYPES.docx,
MIME_TYPES.pdf,
MIME_TYPES.png,
MIME_TYPES.jpeg,
],
}}
{...dropzoneFieldProps}
/> />
<Group className={styles.hintText}> <Group className={styles.hintText}>
<Box>Formats: CSV, XLSX</Box> <Box>Formats: CSV, XLSX</Box>
@@ -180,7 +172,14 @@ export function UploadAttachmentsPopoverContent({
<Button small minimal> <Button small minimal>
View View
</Button> </Button>
<DeleteButton fileKey={localFile.key} /> <Button
small
minimal
intent={Intent.DANGER}
onClick={handleClick(localFile.key)}
>
<Icon icon={'trash-16'} iconSize={16} />
</Button>
</Group> </Group>
)} )}
</Group> </Group>

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

View File

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

View File

@@ -8,7 +8,7 @@ import { MIME_TYPES } from '@/components/Dropzone/mine-types';
import { useUncontrolled } from '@/hooks/useUncontrolled'; import { useUncontrolled } from '@/hooks/useUncontrolled';
import styles from './ImportDropzone.module.css'; import styles from './ImportDropzone.module.css';
interface ImportDropzoneFieldProps { export interface ImportDropzoneFieldProps {
initialValue?: File; initialValue?: File;
value?: File; value?: File;
onChange?: (file: File) => void; onChange?: (file: File) => void;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ import { CLASSES } from '@/constants/classes';
import { Paper, Row, Col } from '@/components'; import { Paper, Row, Col } from '@/components';
import { InvoiceFormFooterLeft } from './InvoiceFormFooterLeft'; import { InvoiceFormFooterLeft } from './InvoiceFormFooterLeft';
import { InvoiceFormFooterRight } from './InvoiceFormFooterRight'; import { InvoiceFormFooterRight } from './InvoiceFormFooterRight';
import { UploadAttachmentButton } from './UploadAttachmentButton'; import { UploadAttachmentButton } from '../../../Attachments/UploadAttachmentButton';
export default function InvoiceFormFooter() { export default function InvoiceFormFooter() {
return ( return (

View File

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

View File

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

View File

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

View File

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