Compare commits

...

24 Commits

Author SHA1 Message Date
a.bouhuolia
e1ea5c402c feat: add env variable to customize the proxy public ports 2023-05-31 20:29:37 +02:00
a.bouhuolia
34b2c2c8b4 fix: copy package-lock.json inside the container 2023-05-31 13:07:30 +02:00
a.bouhuolia
5d96fe6aa0 chore(webapp): remove Sentry from webapp 2023-05-31 09:59:35 +02:00
Ameir Abdeldayem
d2b5084b42 chore: fix typo in README file (#124) 2023-05-30 10:46:51 +02:00
a.bouhuolia
81fb0734d5 update CHANGELOG 2023-05-28 15:04:31 +02:00
a.bouhuolia
3639ce44e5 chore: bump CHANGELOG v0.9.1 2023-05-28 15:02:14 +02:00
Ahmed Bouhuolia
a7c00d60d5 Merge pull request #121 from bigcapitalhq/BIG-429-clean-up-the-auto-increment-of-transactions
fix: the auto-increment of transactions.
2023-05-28 14:50:43 +02:00
a.bouhuolia
932750b62d fix(webapp): fix credit note and receipt auto-increment 2023-05-28 14:45:34 +02:00
a.bouhuolia
c90ffed67f fix(webapp): payment receive auto-increment 2023-05-26 00:02:47 +02:00
a.bouhuolia
e92c4486aa fix(webapp): auto-increment estimate transactions 2023-05-25 22:04:21 +02:00
a.bouhuolia
aaceea5338 fix(webapp): warehouse and branch reset on invoice form 2023-05-24 23:52:05 +02:00
a.bouhuolia
4d54d180bc fix(webapp): invoice transactions increment 2023-05-24 23:28:09 +02:00
Ahmed Bouhuolia
8fdd98e34d Merge pull request #122 from bigcapitalhq/BIG-434-delete-invoice-transaction-issue
fix(server): delete invoice transaction
2023-05-23 15:12:43 +02:00
a.bouhuolia
d53c5ee5e6 fix(server): delete invoice transaction 2023-05-23 15:11:56 +02:00
a.bouhuolia
4082e4e2b8 fix: auto-increment transaction field 2023-05-23 14:39:57 +02:00
a.bouhuolia
0c689459cb fix: auto-increment cashflow transactions 2023-05-23 13:56:35 +02:00
a.bouhuolia
40ef02f215 fix: auto-increment settings 2023-05-22 21:57:43 +02:00
a.bouhuolia
d369f0bb17 fix: the auto-increment of transactions. 2023-05-19 00:29:35 +02:00
Ahmed Bouhuolia
425d0293cc Merge pull request #120 from bigcapitalhq/BIG-433-fix-base-currency-should-be-enabled-with-account-model
BIG-433-fix-base-currency-should-be-enabled-with-account-model
2023-05-12 15:58:48 +02:00
a.bouhuolia
b621650975 fix(server): base currency should be enabled with account model. 2023-05-12 15:58:01 +02:00
a.bouhuolia
40948160fe fix(webapp): localization 2023-05-12 12:46:59 +02:00
Ahmed Bouhuolia
aa6b9dd295 Merge pull request #118 from bigcapitalhq/BIG-428-clean-up-the-preferences-pages
fix(webapp): general, accoutant and items preferences
2023-05-12 12:36:19 +02:00
Ahmed Bouhuolia
8f6325d529 Merge pull request #119 from bigcapitalhq/fix-delete-journals-manual-journal
fix(server): deleting ledger entries of manual journal
2023-05-12 00:14:36 +02:00
a.bouhuolia
0aa681043d fix(server): deleting ledger entries of manual journal 2023-05-11 22:46:34 +02:00
106 changed files with 1511 additions and 1469 deletions

View File

@@ -29,6 +29,10 @@ JWT_SECRET=b0JDZW56RnV6aEthb0RGPXVEcUI
BASE_URL=https://bigcapital.ly
CONTACT_US_MAIL=support@bigcapital.ly
# App proxy
PUBLIC_PROXY_PORT=80
PUBLIC_PROXY_SSL_PORT=443
# Agendash
AGENDASH_AUTH_USER=agendash
AGENDASH_AUTH_PASSWORD=123123

View File

@@ -2,6 +2,24 @@
All notable changes to Bigcapital server-side will be in this file.
## [0.9.1] - 28-05-2023
`@bigcapital/server`
- fix: deleting ledger entries of manual journal.
- fix: base currency should be enabled.
- fix: delete invoice transaction issue.
`@bigcapital/webapp`
- fix: general, accoutant and items preferences.
- fix: auto-increment sale invoices, estiamtes, credit notes, payments and manual journals.
- refactor: the setup organization form to use binded Formik components.
## [0.9.0] - 06-05-2023
`@bigcapital/server`
- [Sign-up restrictions](https://docs.bigcapital.ly/docs/deployment/signup_restriction) for self-hosting instances to disable signup or control the allowed email addresses and domains that can sign-up.
## [0.8.3] - 06-04-2023
`@bigcaptial/monorepo`

View File

@@ -26,6 +26,6 @@ Bigcapital is a smart and open-source accounting and inventory software, Bigcapi
- [Bug Tracker](https://github.com/bigcapitalhq/bigcapital/issues) - Notify us new bugs.
- [Source Code](https://github.com/bigcapitalhq/bigcapital) - Github repo.
# Changlog
# Changelog
Please see [Releases](https://github.com/bigcapitalhq/bigcapital/releases) for more information what has changed recently.

View File

@@ -15,14 +15,14 @@ services:
- ./data/logs/nginx/:/var/log/nginx
- ./docker/certbot/certs/:/var/certs
ports:
- "80:80"
- "443:443"
- "${PUBLIC_PROXY_PORT:-80}:80"
- "${PUBLIC_PROXY_SSL_PORT:-443}:443"
tty: true
depends_on:
- server
- webapp
webapp:
webapp:
container_name: bigcapital-webapp
image: ghcr.io/bigcapitalhq/webapp:latest

View File

@@ -3,17 +3,17 @@ import AccountsData from '../data/accounts';
export default class SeedAccounts extends TenantSeeder {
/**
* Seeds initial accounts to the organization.
* Seeds initial accounts to the organization.
*/
up(knex) {
const data = AccountsData.map((account) => {
return {
...account,
name: this.i18n.__(account.name),
description: this.i18n.__(account.description),
currencyCode: this.tenant.metadata.baseCurrency,
};
});
const data = AccountsData.map((account) => ({
...account,
name: this.i18n.__(account.name),
description: this.i18n.__(account.description),
currencyCode: this.tenant.metadata.baseCurrency,
seededAt: new Date(),
})
);
return knex('accounts').then(async () => {
// Inserts seed entries.
return knex('accounts').insert(data);

View File

@@ -10,7 +10,7 @@ export class LedgerRevert {
private tenancy: HasTenancyService;
@Inject()
ledgerStorage: LedgerStorageService;
private ledgerStorage: LedgerStorageService;
/**
* Reverts the jouranl entries.

View File

@@ -5,18 +5,13 @@ import {
ICreditNoteDeletedPayload,
ICreditNoteEditedPayload,
ICreditNoteOpenedPayload,
IRefundCreditNoteOpenedPayload,
} from '@/interfaces';
import CreditNoteGLEntries from './CreditNoteGLEntries';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@Service()
export default class CreditNoteGLEntriesSubscriber {
@Inject()
creditNoteGLEntries: CreditNoteGLEntries;
@Inject()
tenancy: HasTenancyService;
private creditNoteGLEntries: CreditNoteGLEntries;
/**
* Attaches events with handlers.

View File

@@ -1,11 +1,10 @@
import { difference, sumBy, omit, map } from 'lodash';
import { difference } from 'lodash';
import { Service, Inject } from 'typedi';
import { ServiceError } from '@/exceptions';
import {
IManualJournalDTO,
IManualJournalEntry,
IManualJournal,
IManualJournalEntryDTO,
} from '@/interfaces';
import TenancyService from '@/services/Tenancy/TenancyService';
import { ERRORS } from './constants';
@@ -286,7 +285,7 @@ export class CommandManualJournalValidators {
public validateJournalCurrencyWithAccountsCurrency = async (
tenantId: number,
manualJournalDTO: IManualJournalDTO,
baseCurrency: string,
baseCurrency: string
) => {
const { Account } = this.tenancy.models(tenantId);

View File

@@ -3,25 +3,20 @@ import * as R from 'ramda';
import {
IManualJournal,
IManualJournalEntry,
IAccount,
ILedgerEntry,
} from '@/interfaces';
import { Knex } from 'knex';
import Ledger from '@/services/Accounting/Ledger';
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { LedgerRevert } from '@/services/Accounting/LedgerStorageRevert';
@Service()
export class ManualJournalGLEntries {
@Inject()
ledgerStorage: LedgerStorageService;
private ledgerStorage: LedgerStorageService;
@Inject()
ledgerRevert: LedgerRevert;
@Inject()
tenancy: HasTenancyService;
private tenancy: HasTenancyService;
/**
* Create manual journal GL entries.
@@ -77,7 +72,7 @@ export class ManualJournalGLEntries {
manualJournalId: number,
trx?: Knex.Transaction
): Promise<void> => {
return this.ledgerRevert.revertGLEntries(
return this.ledgerStorage.deleteByReference(
tenantId,
manualJournalId,
'Journal',
@@ -86,7 +81,7 @@ export class ManualJournalGLEntries {
};
/**
*
* Retrieves the ledger of the given manual journal.
* @param {IManualJournal} manualJournal
* @returns {Ledger}
*/
@@ -97,11 +92,13 @@ export class ManualJournalGLEntries {
};
/**
*
* Retrieves the common entry details of the manual journal
* @param {IManualJournal} manualJournal
* @returns {}
* @returns {Partial<ILedgerEntry>}
*/
private getManualJournalCommonEntry = (manualJournal: IManualJournal) => {
private getManualJournalCommonEntry = (
manualJournal: IManualJournal
): Partial<ILedgerEntry> => {
return {
transactionNumber: manualJournal.journalNumber,
referenceNumber: manualJournal.reference,
@@ -118,7 +115,8 @@ export class ManualJournalGLEntries {
};
/**
*
* Retrieves the ledger entry of the given manual journal and
* its associated entry.
* @param {IManualJournal} manualJournal -
* @param {IManualJournalEntry} entry -
* @returns {ILedgerEntry}
@@ -149,7 +147,7 @@ export class ManualJournalGLEntries {
);
/**
*
* Retrieves the ledger of the given manual journal.
* @param {IManualJournal} manualJournal
* @returns {ILedgerEntry[]}
*/

View File

@@ -23,8 +23,11 @@ export class ProjectBillableBillSubscriber {
events.saleInvoice.onCreated,
this.handleIncreaseBillableBill
);
bus.subscribe(events.saleInvoice.onEdited, this.handleDecreaseBillableBill);
bus.subscribe(events.saleInvoice.onDeleted, this.handleEditBillableBill);
bus.subscribe(events.saleInvoice.onEdited, this.handleEditBillableBill);
bus.subscribe(
events.saleInvoice.onDeleted,
this.handleDecreaseBillableBill
);
}
/**

View File

@@ -1,7 +1,11 @@
import { Knex } from 'knex';
import { Inject, Service } from 'typedi';
import async from 'async';
import { ISaleInvoice, ISaleInvoiceDTO, ProjectLinkRefType } from '@/interfaces';
import {
ISaleInvoice,
ISaleInvoiceDTO,
ProjectLinkRefType,
} from '@/interfaces';
import { ProjectBillableExpense } from './ProjectBillableExpense';
import { filterEntriesByRefType } from './_utils';

View File

@@ -21,13 +21,10 @@ export class ProjectBillableExpensesSubscriber {
events.saleInvoice.onCreated,
this.handleIncreaseBillableExpenses
);
bus.subscribe(
events.saleInvoice.onEdited,
this.handleDecreaseBillableExpenses
);
bus.subscribe(events.saleInvoice.onEdited, this.handleEditBillableExpenses);
bus.subscribe(
events.saleInvoice.onDeleted,
this.handleEditBillableExpenses
this.handleDecreaseBillableExpenses
);
}

View File

@@ -5,10 +5,10 @@ USER root
WORKDIR /app
# Install dependencies
COPY package.json ./
COPY package*.json ./
COPY lerna.json ./
COPY ./packages/webapp/package.json /app/packages/webapp/package.json
COPY ./packages/webapp/package*.json /app/packages/webapp/
RUN npm install
RUN npm run bootstrap

View File

@@ -16,8 +16,6 @@
"@casl/react": "^2.3.0",
"@craco/craco": "^5.9.0",
"@reduxjs/toolkit": "^1.2.5",
"@sentry/react": "^6.13.2",
"@sentry/tracing": "^6.13.2",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",

View File

@@ -20,24 +20,6 @@
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<% if (process.env.NODE_ENV === 'production') { %>
<!-- Hotjar Tracking Code for https://app.bigcapital.ly/ -->
<script>
(function (h, o, t, j, a, r) {
h.hj =
h.hj ||
function () {
(h.hj.q = h.hj.q || []).push(arguments);
};
h._hjSettings = { hjid: 2774528, hjsv: 6 };
a = o.getElementsByTagName('head')[0];
r = o.createElement('script');
r.async = 1;
r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
a.appendChild(r);
})(window, document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');
</script>
<% } %>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
@@ -69,7 +51,5 @@
href="https://cdnjs.cloudflare.com/ajax/libs/flexboxgrid/6.3.1/flexboxgrid.min.css"
type="text/css"
/>
<!-- <link href="https://cdn.syncfusion.com/ej2/material.css" rel="stylesheet"> -->
<!-- <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" /> -->
</body>
</html>

View File

@@ -17,8 +17,8 @@ import AllocateLandedCostDialog from '@/containers/Dialogs/AllocateLandedCostDia
import InvoicePdfPreviewDialog from '@/containers/Dialogs/InvoicePdfPreviewDialog';
import EstimatePdfPreviewDialog from '@/containers/Dialogs/EstimatePdfPreviewDialog';
import ReceiptPdfPreviewDialog from '@/containers/Dialogs/ReceiptPdfPreviewDialog';
import MoneyInDialog from '@/containers/Dialogs/MoneyInDialog';
import MoneyOutDialog from '@/containers/Dialogs/MoneyOutDialog';
import MoneyInDialog from '@/containers/CashFlow/MoneyInDialog';
import MoneyOutDialog from '@/containers/CashFlow/MoneyOutDialog';
import BadDebtDialog from '@/containers/Dialogs/BadDebtDialog';
import NotifyInvoiceViaSMSDialog from '@/containers/Dialogs/NotifyInvoiceViaSMSDialog';
import NotifyReceiptViaSMSDialog from '@/containers/Dialogs/NotifyReceiptViaSMSDialog';

View File

@@ -46,4 +46,5 @@ export enum DialogsName {
EstimateExpenseForm = 'estimate-expense-form',
ProjectInvoicingForm = 'project-invoicing-form',
ProjectBillableEntriesForm = 'project-billable-entries',
InvoiceNumberSettings = 'InvoiceNumberSettings'
}

View File

@@ -4,7 +4,7 @@ import { Formik, Form } from 'formik';
import { Intent } from '@blueprintjs/core';
import intl from 'react-intl-universal';
import * as R from 'ramda';
import { defaultTo, isEmpty, omit } from 'lodash';
import { isEmpty, omit } from 'lodash';
import classNames from 'classnames';
import { useHistory } from 'react-router-dom';
@@ -31,6 +31,7 @@ import {
transformToEditForm,
defaultManualJournal,
} from './utils';
import { JournalSyncIncrementSettingsToForm } from './components';
/**
* Journal entries form.
@@ -40,6 +41,7 @@ function MakeJournalEntriesForm({
journalNextNumber,
journalNumberPrefix,
journalAutoIncrement,
// #withCurrentOrganization
organization: { base_currency },
}) {
@@ -69,6 +71,8 @@ function MakeJournalEntriesForm({
}
: {
...defaultManualJournal,
// If the auto-increment mode is enabled, take the next journal
// number from the settings.
...(journalAutoIncrement && {
journal_number: journalNumber,
}),
@@ -116,7 +120,6 @@ function MakeJournalEntriesForm({
entries: R.compose(orderingLinesIndexes)(entries),
publish: submitPayload.publish,
};
// Handle the request error.
const handleError = ({
response: {
@@ -126,7 +129,6 @@ function MakeJournalEntriesForm({
transformErrors(errors, { setErrors });
setSubmitting(false);
};
// Handle the request success.
const handleSuccess = (errors) => {
AppToaster.show({
@@ -147,7 +149,6 @@ function MakeJournalEntriesForm({
resetForm();
}
};
if (isNewMode) {
createJournalMutate(form).then(handleSuccess).catch(handleError);
} else {
@@ -179,6 +180,9 @@ function MakeJournalEntriesForm({
{/* --------- Dialogs --------- */}
<MakeJournalFormDialogs />
{/* --------- Effects --------- */}
<JournalSyncIncrementSettingsToForm />
</Form>
</Formik>
</div>

View File

@@ -8,6 +8,19 @@ import { PageFormBigNumber, FormattedMessage as T } from '@/components';
import MakeJournalEntriesHeaderFields from './MakeJournalEntriesHeaderFields';
export default function MakeJournalEntriesHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<MakeJournalEntriesHeaderFields />
<MakeJournalHeaderBigNumber />
</div>
);
}
/**
* Big total number of make journal header.
* @returns {React.ReactNode}
*/
function MakeJournalHeaderBigNumber() {
const {
values: { entries, currency_code },
} = useFormikContext();
@@ -17,14 +30,10 @@ export default function MakeJournalEntriesHeader() {
const total = Math.max(totalCredit, totalDebit);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<MakeJournalEntriesHeaderFields />
<PageFormBigNumber
label={<T id={'amount'} />}
amount={total}
currencyCode={currency_code}
/>
</div>
<PageFormBigNumber
label={<T id={'amount'} />}
amount={total}
currencyCode={currency_code}
/>
);
}

View File

@@ -6,14 +6,14 @@ import {
Position,
ControlGroup,
} from '@blueprintjs/core';
import { FastField, ErrorMessage } from 'formik';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { DateInput } from '@blueprintjs/datetime';
import * as R from 'ramda';
import classNames from 'classnames';
import { CLASSES } from '@/constants/classes';
import {
momentFormatter,
compose,
inputIntent,
handleDateChange,
tansformDateValue,
@@ -25,54 +25,101 @@ import {
Icon,
InputPrependButton,
CurrencySelectList,
FormattedMessage as T
FormattedMessage as T,
FInputGroup,
FFormGroup,
} from '@/components';
import { useMakeJournalFormContext } from './MakeJournalProvider';
import { JournalExchangeRateInputField } from './components';
import { currenciesFieldShouldUpdate } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import {
currenciesFieldShouldUpdate,
useObserveJournalNoSettings,
} from './utils';
/**
* Journal number field of make journal form.
*/
const MakeJournalTransactionNoField = R.compose(
withDialogActions,
withSettings(({ manualJournalsSettings }) => ({
journalAutoIncrement: manualJournalsSettings?.autoIncrement,
})),
)(
({
// #withDialog
openDialog,
// #withSettings
journalAutoIncrement,
}) => {
const { setFieldValue, values } = useFormikContext();
const handleJournalNumberChange = () => {
openDialog('journal-number-form');
};
const handleJournalNoBlur = (event) => {
const newValue = event.target.value;
if (values.journal_number !== newValue && journalAutoIncrement) {
openDialog('journal-number-form', {
initialFormValues: {
onceManualNumber: newValue,
incrementMode: 'manual-transaction',
},
});
}
if (!journalAutoIncrement) {
setFieldValue('journal_number', newValue);
setFieldValue('journal_number_manually', newValue);
}
};
return (
<FFormGroup
name={'journal_number'}
label={<T id={'journal_no'} />}
labelInfo={
<>
<FieldRequiredHint />
<FieldHint />
</>
}
fill={true}
inline={true}
fastField={true}
>
<ControlGroup fill={true}>
<FInputGroup
name={'journal_number'}
fill={true}
asyncControl={true}
onBlur={handleJournalNoBlur}
fastField={true}
onChange={() => {}}
/>
<InputPrependButton
buttonProps={{
onClick: handleJournalNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: <T id={'setting_your_auto_generated_journal_number'} />,
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FFormGroup>
);
},
);
/**
* Make journal entries header.
*/
function MakeJournalEntriesHeader({
// #ownProps
onJournalNumberChanged,
// #withDialog
openDialog,
// #withSettings
journalAutoIncrement,
journalNextNumber,
journalNumberPrefix,
}) {
export default function MakeJournalEntriesHeader({}) {
const { currencies } = useMakeJournalFormContext();
// Handle journal number change.
const handleJournalNumberChange = () => {
openDialog('journal-number-form');
};
// Handle journal number blur.
const handleJournalNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && journalAutoIncrement) {
openDialog('journal-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
useObserveJournalNoSettings(journalNumberPrefix, journalNextNumber);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/*------------ Posting date -----------*/}
@@ -106,46 +153,7 @@ function MakeJournalEntriesHeader({
</FastField>
{/*------------ Journal number -----------*/}
<FastField name={'journal_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'journal_no'} />}
labelInfo={
<>
<FieldRequiredHint />
<FieldHint />
</>
}
className={'form-group--journal-number'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="journal_number" />}
fill={true}
inline={true}
>
<ControlGroup fill={true}>
<InputGroup
fill={true}
value={field.value}
asyncControl={true}
onBlur={handleJournalNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleJournalNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T id={'setting_your_auto_generated_journal_number'} />
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</FastField>
<MakeJournalTransactionNoField />
{/*------------ Reference -----------*/}
<FastField name={'reference'}>
@@ -219,12 +227,3 @@ function MakeJournalEntriesHeader({
</div>
);
}
export default compose(
withDialogActions,
withSettings(({ manualJournalsSettings }) => ({
journalAutoIncrement: manualJournalsSettings?.autoIncrement,
journalNextNumber: manualJournalsSettings?.nextNumber,
journalNumberPrefix: manualJournalsSettings?.numberPrefix,
})),
)(MakeJournalEntriesHeader);

View File

@@ -10,17 +10,21 @@ export default function MakeJournalFormDialogs() {
const { setFieldValue } = useFormikContext();
// Update the form once the journal number form submit confirm.
const handleConfirm = ({ manually, incrementNumber }) => {
setFieldValue('journal_number', incrementNumber || '');
setFieldValue('journal_number_manually', manually);
const handleConfirm = (settings) => {
// Set the invoice transaction no. that cames from dialog to the form.
// the `journal_number` will be empty except the increment mode is not auto.
setFieldValue('journal_number', settings.transactionNumber);
setFieldValue('journal_number_manually', '');
if (settings.incrementMode !== 'auto') {
setFieldValue('journal_number_manually', settings.transactionNumber);
}
};
return (
<>
<JournalNumberDialog
dialogName={'journal-number-form'}
onConfirm={handleConfirm}
/>
</>
<JournalNumberDialog
dialogName={'journal-number-form'}
onConfirm={handleConfirm}
/>
);
}

View File

@@ -1,9 +1,10 @@
// @ts-nocheck
import React from 'react';
import React, { useEffect } from 'react';
import intl from 'react-intl-universal';
import { Menu, MenuItem, Position, Button } from '@blueprintjs/core';
import { Popover2 } from '@blueprintjs/popover2';
import { useFormikContext } from 'formik';
import * as R from 'ramda';
import {
ExchangeRateInputGroup,
@@ -24,6 +25,9 @@ import { CellType, Features, Align } from '@/constants';
import { useCurrentOrganization, useFeatureCan } from '@/hooks/state';
import { useJournalIsForeign } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
import { useUpdateEffect } from '@/hooks';
/**
* Contact header cell.
@@ -199,3 +203,37 @@ export function JournalExchangeRateInputField({ ...props }) {
/>
);
}
/**
* Syncs journal auto-increment settings to form.
* @return {React.ReactNode}
*/
export const JournalSyncIncrementSettingsToForm = R.compose(
withSettings(({ manualJournalsSettings }) => ({
journalAutoIncrement: manualJournalsSettings?.autoIncrement,
journalNextNumber: manualJournalsSettings?.nextNumber,
journalNumberPrefix: manualJournalsSettings?.numberPrefix,
})),
)(({ journalAutoIncrement, journalNextNumber, journalNumberPrefix }) => {
const { setFieldValue } = useFormikContext();
useUpdateEffect(() => {
// Do not update if the journal auto-increment mode is disabled.
if (!journalAutoIncrement) return null;
setFieldValue(
'journal_number',
transactionNumber(journalNumberPrefix, journalNextNumber),
);
}, [
setFieldValue,
journalNumberPrefix,
journalNextNumber,
journalAutoIncrement,
]);
return null;
});
JournalSyncIncrementSettingsToForm.displayName =
'JournalSyncIncrementSettingsToForm';

View File

@@ -6,7 +6,6 @@ import intl from 'react-intl-universal';
import { Intent } from '@blueprintjs/core';
import { sumBy, setWith, toSafeInteger, get, first } from 'lodash';
import {
transactionNumber,
updateTableCell,
repeatValue,
transformToForm,
@@ -46,7 +45,7 @@ export const defaultEntry = {
export const defaultManualJournal = {
journal_number: '',
journal_number_manually: false,
journal_number_manually: '',
journal_type: 'Journal',
date: moment(new Date()).format('YYYY-MM-DD'),
description: '',
@@ -174,15 +173,6 @@ export const transformErrors = (resErrors, { setErrors, errors }) => {
}
};
export const useObserveJournalNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const journalNo = transactionNumber(prefix, nextNumber);
setFieldValue('journal_number', journalNo);
}, [setFieldValue, prefix, nextNumber]);
};
/**
* Detarmines entries fast field should update.
*/

View File

@@ -1,9 +1,15 @@
// @ts-nocheck
import React from 'react';
import OwnerContributionFormFields from './OwnerContribution/OwnerContributionFormFields';
import OtherIncomeFormFields from './OtherIncome/OtherIncomeFormFields';
import TransferFromAccountFormFields from './TransferFromAccount/TransferFromAccountFormFields';
/**
*
* @param param0
* @returns
*/
export default function MoneyInContentFields({ accountType }) {
const handleTransactionType = () => {
switch (accountType) {
@@ -19,6 +25,5 @@ export default function MoneyInContentFields({ accountType }) {
break;
}
};
return <React.Fragment>{handleTransactionType()}</React.Fragment>;
}

View File

@@ -5,6 +5,7 @@ import { Form } from 'formik';
import MoneyInFormFields from './MoneyInFormFields';
import MoneyInFormDialog from './MoneyInFormDialog';
import MoneyInFloatingActions from './MoneyInFloatingActions';
import { MoneyInOutSyncIncrementSettingsToForm } from '../_components';
/**
* Money In form content.
@@ -15,6 +16,7 @@ export default function MoneyInFormContent() {
<MoneyInFormFields />
<MoneyInFormDialog />
<MoneyInFloatingActions />
<MoneyInOutSyncIncrementSettingsToForm />
</Form>
);
}

View File

@@ -11,12 +11,9 @@ export default function MoneyInFormDialog() {
const { setFieldValue } = useFormikContext();
// Update the form once the transaction number form submit confirm.
const handleTransactionNumberFormConfirm = ({
incrementNumber,
manually,
}) => {
setFieldValue('transaction_number', incrementNumber || '');
setFieldValue('transaction_number_manually', manually);
const handleTransactionNumberFormConfirm = (settings) => {
setFieldValue('transaction_number', settings.transactionNumber);
setFieldValue('transaction_number_manually', settings.transactionNumber);
};
return (
<React.Fragment>

View File

@@ -16,14 +16,12 @@ import {
InputPrependText,
MoneyInputGroup,
FieldRequiredHint,
Icon,
Col,
Row,
If,
FeatureCan,
BranchSelect,
BranchSelectButton,
InputPrependButton,
ExchangeRateMutedField,
} from '@/components';
import { DateInput } from '@blueprintjs/datetime';
@@ -35,65 +33,26 @@ import {
momentFormatter,
tansformDateValue,
handleDateChange,
compose,
} from '@/utils';
import { useMoneyInDailogContext } from '../MoneyInDialogProvider';
import {
useObserveTransactionNoSettings,
useSetPrimaryBranchToForm,
useForeignAccount,
BranchRowDivider,
} from '../utils';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { MoneyInOutTransactionNoField } from '../../_components';
/**
* Other income form fields.
*/
function OtherIncomeFormFields({
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
export default function OtherIncomeFormFields() {
// Money in dialog context.
const { accounts, account, branches } = useMoneyInDailogContext();
const { values } = useFormikContext();
const amountFieldRef = useAutofocus();
const isForeigAccount = useForeignAccount();
// Handle tranaction number changing.
const handleTransactionNumberChange = () => {
openDialog('transaction-number-form');
};
// Handle transaction no. field blur.
const handleTransactionNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && transactionAutoIncrement) {
openDialog('transaction-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs transaction number settings with form.
useObserveTransactionNoSettings(
transactionNumberPrefix,
transactionNextNumber,
);
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
@@ -149,42 +108,7 @@ function OtherIncomeFormFields({
</Col>
<Col xs={5}>
{/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'transaction_number'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="transaction_number" />}
className={'form-group--transaction_number'}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleTransactionNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleTransactionNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T
id={
'cash_flow.setting_your_auto_generated_transaction_number'
}
/>
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</Field>
<MoneyInOutTransactionNoField />
</Col>
</Row>
{/*------------ amount -----------*/}
@@ -298,12 +222,3 @@ function OtherIncomeFormFields({
</React.Fragment>
);
}
export default compose(
withDialogActions,
withSettings(({ cashflowSetting }) => ({
transactionAutoIncrement: cashflowSetting?.autoIncrement,
transactionNextNumber: cashflowSetting?.nextNumber,
transactionNumberPrefix: cashflowSetting?.numberPrefix,
})),
)(OtherIncomeFormFields);

View File

@@ -17,11 +17,9 @@ import {
InputPrependText,
MoneyInputGroup,
FieldRequiredHint,
Icon,
Col,
Row,
If,
InputPrependButton,
ExchangeRateMutedField,
BranchSelect,
BranchSelectButton,
@@ -35,31 +33,20 @@ import {
momentFormatter,
tansformDateValue,
handleDateChange,
compose,
} from '@/utils';
import { useMoneyInDailogContext } from '../MoneyInDialogProvider';
import {
useObserveTransactionNoSettings,
useSetPrimaryBranchToForm,
useForeignAccount,
BranchRowDivider,
} from '../../MoneyInDialog/utils';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { MoneyInOutTransactionNoField } from '../../_components';
/**
/**
* Owner contribution form fields.
*/
function OwnerContributionFormFields({
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
export default function OwnerContributionFormFields() {
// Money in dialog context.
const { accounts, account, branches } = useMoneyInDailogContext();
@@ -69,31 +56,6 @@ function OwnerContributionFormFields({
const isForeigAccount = useForeignAccount();
// Handle tranaction number changing.
const handleTransactionNumberChange = () => {
openDialog('transaction-number-form');
};
// Handle transaction no. field blur.
const handleTransactionNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && transactionAutoIncrement) {
openDialog('transaction-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs transaction number settings with form.
useObserveTransactionNoSettings(
transactionNumberPrefix,
transactionNextNumber,
);
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
@@ -148,42 +110,7 @@ function OwnerContributionFormFields({
</Col>
<Col xs={5}>
{/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'transaction_number'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="transaction_number" />}
className={'form-group--transaction_number'}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleTransactionNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleTransactionNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T
id={
'cash_flow.setting_your_auto_generated_transaction_number'
}
/>
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</Field>
<MoneyInOutTransactionNoField />
</Col>
</Row>
{/*------------ amount -----------*/}
@@ -294,12 +221,3 @@ function OwnerContributionFormFields({
</React.Fragment>
);
}
export default compose(
withDialogActions,
withSettings(({ cashflowSetting }) => ({
transactionAutoIncrement: cashflowSetting?.autoIncrement,
transactionNextNumber: cashflowSetting?.nextNumber,
transactionNumberPrefix: cashflowSetting?.numberPrefix,
})),
)(OwnerContributionFormFields);

View File

@@ -17,11 +17,9 @@ import {
InputPrependText,
MoneyInputGroup,
FieldRequiredHint,
Icon,
Col,
Row,
If,
InputPrependButton,
ExchangeRateMutedField,
FeatureCan,
BranchSelect,
@@ -35,30 +33,20 @@ import {
momentFormatter,
tansformDateValue,
handleDateChange,
compose,
} from '@/utils';
import { useMoneyInDailogContext } from '../MoneyInDialogProvider';
import {
useObserveTransactionNoSettings,
useSetPrimaryBranchToForm,
useForeignAccount,
BranchRowDivider,
} from '../../MoneyInDialog/utils';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { MoneyInOutTransactionNoField } from '../../_components';
/**
* Transfer from account form fields.
*/
function TransferFromAccountFormFields({
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
export default function TransferFromAccountFormFields() {
// Money in dialog context.
const { accounts, account, branches } = useMoneyInDailogContext();
@@ -67,33 +55,9 @@ function TransferFromAccountFormFields({
const { values } = useFormikContext();
// Handle tranaction number changing.
const handleTransactionNumberChange = () => {
openDialog('transaction-number-form');
};
// Handle transaction no. field blur.
const handleTransactionNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && transactionAutoIncrement) {
openDialog('transaction-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
// Syncs transaction number settings with form.
useObserveTransactionNoSettings(
transactionNumberPrefix,
transactionNextNumber,
);
return (
<React.Fragment>
<FeatureCan feature={Features.Branches}>
@@ -145,42 +109,7 @@ function TransferFromAccountFormFields({
</Col>
<Col xs={5}>
{/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'transaction_number'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="transaction_number" />}
className={'form-group--transaction_number'}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleTransactionNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleTransactionNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T
id={
'cash_flow.setting_your_auto_generated_transaction_number'
}
/>
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</Field>
<MoneyInOutTransactionNoField />
</Col>
</Row>
{/*------------ amount -----------*/}
@@ -296,12 +225,3 @@ function TransferFromAccountFormFields({
</React.Fragment>
);
}
export default compose(
withDialogActions,
withSettings(({ cashflowSetting }) => ({
transactionAutoIncrement: cashflowSetting?.autoIncrement,
transactionNextNumber: cashflowSetting?.nextNumber,
transactionNumberPrefix: cashflowSetting?.numberPrefix,
})),
)(TransferFromAccountFormFields);

View File

@@ -20,7 +20,6 @@ function MoneyOutContentFields({ accountType }) {
break;
}
};
return <React.Fragment>{handleTransactionType()}</React.Fragment>;
}

View File

@@ -3,8 +3,10 @@ import React from 'react';
import { Form } from 'formik';
import MoneyOutFormFields from './MoneyOutFormFields';
import MoneyOutFormDialog from './MoneyOutFormDialog'
import MoneyOutFormDialog from './MoneyOutFormDialog';
import MoneyOutFloatingActions from './MoneyOutFloatingActions';
import { MoneyInOutSyncIncrementSettingsToForm } from '../_components';
/**
* Money out form content.
*/
@@ -12,8 +14,9 @@ export default function MoneyOutFormContent() {
return (
<Form>
<MoneyOutFormFields />
<MoneyOutFormDialog/>
<MoneyOutFormDialog />
<MoneyOutFloatingActions />
<MoneyInOutSyncIncrementSettingsToForm />
</Form>
);
}

View File

@@ -16,11 +16,9 @@ import {
InputPrependText,
MoneyInputGroup,
FieldRequiredHint,
Icon,
Col,
Row,
If,
InputPrependButton,
FeatureCan,
BranchSelect,
BranchSelectButton,
@@ -35,31 +33,21 @@ import {
momentFormatter,
tansformDateValue,
handleDateChange,
compose,
} from '@/utils';
import { CLASSES } from '@/constants/classes';
import { useMoneyOutDialogContext } from '../MoneyOutDialogProvider';
import {
useObserveTransactionNoSettings,
useSetPrimaryBranchToForm,
useForeignAccount,
BranchRowDivider,
} from '../utils';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { MoneyInOutTransactionNoField } from '../../_components';
/**
* Other expense form fields.
*/
function OtherExpnseFormFields({
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
export default function OtherExpnseFormFields() {
// Money in dialog context.
const { accounts, account, branches } = useMoneyOutDialogContext();
@@ -68,31 +56,6 @@ function OtherExpnseFormFields({
const amountFieldRef = useAutofocus();
// Handle tranaction number changing.
const handleTransactionNumberChange = () => {
openDialog('transaction-number-form');
};
// Handle transaction no. field blur.
const handleTransactionNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && transactionAutoIncrement) {
openDialog('transaction-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs transaction number settings with form.
useObserveTransactionNoSettings(
transactionNumberPrefix,
transactionNextNumber,
);
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
@@ -147,42 +110,7 @@ function OtherExpnseFormFields({
</Col>
<Col xs={5}>
{/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'transaction_number'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="transaction_number" />}
className={'form-group--transaction_number'}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleTransactionNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleTransactionNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T
id={
'cash_flow.setting_your_auto_generated_transaction_number'
}
/>
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</Field>
<MoneyInOutTransactionNoField />
</Col>
</Row>
{/*------------ amount -----------*/}
@@ -296,12 +224,3 @@ function OtherExpnseFormFields({
</React.Fragment>
);
}
export default compose(
withDialogActions,
withSettings(({ cashflowSetting }) => ({
transactionAutoIncrement: cashflowSetting?.autoIncrement,
transactionNextNumber: cashflowSetting?.nextNumber,
transactionNumberPrefix: cashflowSetting?.numberPrefix,
})),
)(OtherExpnseFormFields);

View File

@@ -17,8 +17,6 @@ import {
InputPrependText,
MoneyInputGroup,
FieldRequiredHint,
InputPrependButton,
Icon,
If,
Col,
Row,
@@ -34,30 +32,19 @@ import {
momentFormatter,
tansformDateValue,
handleDateChange,
compose,
} from '@/utils';
import { useMoneyOutDialogContext } from '../MoneyOutDialogProvider';
import {
useObserveTransactionNoSettings,
useSetPrimaryBranchToForm,
useForeignAccount,
BranchRowDivider,
} from '../../MoneyOutDialog/utils';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { MoneyInOutTransactionNoField } from '../../_components';
/**
* Owner drawings form fields.
*/
function OwnerDrawingsFormFields({
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
export default function OwnerDrawingsFormFields() {
// Money out dialog context.
const { accounts, account, branches } = useMoneyOutDialogContext();
const { values } = useFormikContext();
@@ -65,31 +52,6 @@ function OwnerDrawingsFormFields({
const amountFieldRef = useAutofocus();
// Handle tranaction number changing.
const handleTransactionNumberChange = () => {
openDialog('transaction-number-form');
};
// Handle transaction no. field blur.
const handleTransactionNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && transactionAutoIncrement) {
openDialog('transaction-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs transaction number settings with form.
useObserveTransactionNoSettings(
transactionNumberPrefix,
transactionNextNumber,
);
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
@@ -144,42 +106,7 @@ function OwnerDrawingsFormFields({
</Col>
<Col xs={5}>
{/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'transaction_number'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="transaction_number" />}
className={'form-group--transaction_number'}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleTransactionNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleTransactionNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T
id={
'cash_flow.setting_your_auto_generated_transaction_number'
}
/>
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</Field>
<MoneyInOutTransactionNoField />
</Col>
</Row>
{/*------------ amount -----------*/}
@@ -291,12 +218,3 @@ function OwnerDrawingsFormFields({
</React.Fragment>
);
}
export default compose(
withDialogActions,
withSettings(({ cashflowSetting }) => ({
transactionAutoIncrement: cashflowSetting?.autoIncrement,
transactionNextNumber: cashflowSetting?.nextNumber,
transactionNumberPrefix: cashflowSetting?.numberPrefix,
})),
)(OwnerDrawingsFormFields);

View File

@@ -48,19 +48,12 @@ import {
} from '../utils';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { MoneyInOutTransactionNoField } from '../../_components';
/**
* Transfer to account form fields.
*/
function TransferToAccountFormFields({
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
export default function TransferToAccountFormFields() {
// Money in dialog context.
const { accounts, account, branches } = useMoneyOutDialogContext();
const { values } = useFormikContext();
@@ -68,31 +61,6 @@ function TransferToAccountFormFields({
const accountRef = useAutofocus();
// Handle tranaction number changing.
const handleTransactionNumberChange = () => {
openDialog('transaction-number-form');
};
// Handle transaction no. field blur.
const handleTransactionNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && transactionAutoIncrement) {
openDialog('transaction-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs transaction number settings with form.
useObserveTransactionNoSettings(
transactionNumberPrefix,
transactionNextNumber,
);
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
@@ -147,42 +115,7 @@ function TransferToAccountFormFields({
</Col>
<Col xs={5}>
{/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'transaction_number'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="transaction_number" />}
className={'form-group--transaction_number'}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleTransactionNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleTransactionNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T
id={
'cash_flow.setting_your_auto_generated_transaction_number'
}
/>
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</Field>
<MoneyInOutTransactionNoField />
</Col>
</Row>
{/*------------ amount -----------*/}
@@ -298,11 +231,3 @@ function TransferToAccountFormFields({
</React.Fragment>
);
}
export default compose(
withDialogActions,
withSettings(({ cashflowSetting }) => ({
transactionAutoIncrement: cashflowSetting?.autoIncrement,
transactionNextNumber: cashflowSetting?.nextNumber,
transactionNumberPrefix: cashflowSetting?.numberPrefix,
})),
)(TransferToAccountFormFields);

View File

@@ -0,0 +1,127 @@
// @ts-nocheck
import React from 'react';
import { useFormikContext } from 'formik';
import { InputGroup, Position, ControlGroup } from '@blueprintjs/core';
import * as R from 'ramda';
import {
FFormGroup,
Icon,
InputPrependButton,
FormattedMessage as T,
} from '@/components';
import { useUpdateEffect } from '@/hooks';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
/**
* Syncs cashflow auto-increment settings to the form once update.
*/
export const MoneyInOutSyncIncrementSettingsToForm = R.compose(
withDialogActions,
withSettings(({ cashflowSetting }) => ({
transactionAutoIncrement: cashflowSetting?.autoIncrement,
transactionNextNumber: cashflowSetting?.nextNumber,
transactionNumberPrefix: cashflowSetting?.numberPrefix,
})),
)(
({
// #withSettings
transactionAutoIncrement,
transactionNextNumber,
transactionNumberPrefix,
}) => {
const { setFieldValue } = useFormikContext();
useUpdateEffect(() => {
// Do not update if the invoice auto-increment is disabled.
if (!transactionAutoIncrement) return null;
const transactionNumber = transactionNumber(
transactionNumberPrefix,
transactionNextNumber,
);
setFieldValue('transaction_number', transactionNumber);
}, [setFieldValue, transactionNumberPrefix, transactionNextNumber]);
return null;
},
);
/**
* Money In/Out transaction number field.
*/
export const MoneyInOutTransactionNoField = R.compose(
withDialogActions,
withSettings(({ cashflowSetting }) => ({
transactionAutoIncrement: cashflowSetting?.autoIncrement,
transactionNextNumber: cashflowSetting?.nextNumber,
transactionNumberPrefix: cashflowSetting?.numberPrefix,
})),
)(
({
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
}) => {
const { values, setFieldValue } = useFormikContext();
// Handle tranaction number changing.
const handleTransactionNumberChange = () => {
openDialog('transaction-number-form');
};
// Handle transaction no. field blur.
const handleTransactionNoBlur = (event) => {
const newValue = event.target.value;
if (values.transaction_number !== newValue && transactionAutoIncrement) {
openDialog('transaction-number-form', {
initialFormValues: {
onceManualNumber: newValue,
incrementMode: 'manual-transaction',
},
});
}
if (!transactionAutoIncrement) {
setFieldValue('transaction_number', values.transaction_number);
setFieldValue('transaction_number_manually', values.transaction_number);
}
};
return (
<FFormGroup
name={'transaction_number'}
label={<T id={'transaction_number'} />}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={values.transaction_number}
asyncControl={true}
onBlur={handleTransactionNoBlur}
/>
<InputPrependButton
buttonProps={{
onClick: handleTransactionNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T
id={
'cash_flow.setting_your_auto_generated_transaction_number'
}
/>
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FFormGroup>
);
},
);

View File

@@ -7,8 +7,10 @@ export const mapStateToProps = (state, props) => {
};
export const mapDispatchToProps = (dispatch) => ({
openDialog: (name, payload) => dispatch({ type: t.OPEN_DIALOG, name, payload }),
closeDialog: (name, payload) => dispatch({ type: t.CLOSE_DIALOG, name, payload }),
openDialog: (name, payload) =>
dispatch({ type: t.OPEN_DIALOG, name, payload }),
closeDialog: (name, payload) =>
dispatch({ type: t.CLOSE_DIALOG, name, payload }),
});
export default connect(null, mapDispatchToProps);
export default connect(null, mapDispatchToProps);

View File

@@ -65,7 +65,6 @@ function CreditNoteNumberDialogContent({
const handleChange = (values) => {
setReferenceFormValues(values);
};
// Description.
const description =
referenceFormValues?.incrementMode === 'auto'

View File

@@ -14,6 +14,7 @@ import {
transformFormToSettings,
transformSettingsToForm,
} from '@/containers/JournalNumber/utils';
import { DialogsName } from '@/constants/dialogs';
/**
* invoice number dialog's content.
@@ -39,7 +40,7 @@ function InvoiceNumberDialogContent({
// Handle the form success.
const handleSuccess = () => {
setSubmitting(false);
closeDialog('invoice-number-form');
closeDialog(DialogsName.InvoiceNumberSettings);
onConfirm(values);
};
// Handle the form errors.
@@ -56,10 +57,9 @@ function InvoiceNumberDialogContent({
// Save the settings.
saveSettings({ options }).then(handleSuccess).catch(handleErrors);
};
// Handle the dialog close.
const handleClose = () => {
closeDialog('invoice-number-form');
closeDialog(DialogsName.InvoiceNumberSettings);
};
// Handle form change.
const handleChange = (values) => {
@@ -71,17 +71,19 @@ function InvoiceNumberDialogContent({
? intl.get('invoice.auto_increment.auto')
: intl.get('invoice.auto_increment.manually');
const initialFormValues = {
...transformSettingsToForm({
nextNumber,
numberPrefix,
autoIncrement,
}),
...initialValues,
};
return (
<InvoiceNumberDialogProvider>
<ReferenceNumberForm
initialValues={{
...transformSettingsToForm({
nextNumber,
numberPrefix,
autoIncrement,
}),
...initialValues,
}}
initialValues={initialFormValues}
description={description}
onSubmit={handleSubmitForm}
onClose={handleClose}

View File

@@ -2,49 +2,49 @@
import React, { useMemo } from 'react';
import * as Yup from 'yup';
import { Formik, Form } from 'formik';
import { FormattedMessage as T } from '@/components';
import { Intent, Button, Classes } from '@blueprintjs/core';
import '@/style/pages/ReferenceNumber/ReferenceNumber.scss';
import { FormObserver } from '@/components';
import { FormattedMessage as T, FormObserver } from '@/components';
import ReferenceNumberFormContent from './ReferenceNumberFormContent';
import { transformValuesToForm } from './utils';
import { saveInvoke } from '@/utils';
import { saveInvoke, transformToForm } from '@/utils';
const initialFormValues = {
incrementMode: 'auto',
numberPrefix: '',
nextNumber: '',
onceManualNumber: '',
};
// Validation schema.
const validationSchema = Yup.object().shape({
incrementMode: Yup.string(),
numberPrefix: Yup.string(),
nextNumber: Yup.number(),
onceManualNumber: Yup.string(),
});
/**
* Reference number form.
*/
export default function ReferenceNumberForm({
onSubmit,
onClose,
initialValues,
description,
onSubmit,
onClose,
onChange,
}) {
// Validation schema.
const validationSchema = Yup.object().shape({
incrementMode: Yup.string(),
numberPrefix: Yup.string(),
nextNumber: Yup.number(),
manualTransactionNo: Yup.string(),
});
// Initial values.
const formInitialValues = useMemo(
() => ({
...initialValues,
incrementMode:
initialValues.incrementMode === 'auto' &&
initialValues.manualTransactionNo
? 'manual-transaction'
: initialValues.incrementMode,
}),
[initialValues],
);
const formInitialValues = {
...initialFormValues,
...transformToForm(initialValues, initialFormValues),
};
// Handle the form submit.
const handleSubmit = (values, methods) => {
const parsed = transformValuesToForm(values);
saveInvoke(onSubmit, { ...parsed, ...values }, methods);
saveInvoke(onSubmit, { ...values, ...parsed }, methods);
};
return (

View File

@@ -1,17 +1,15 @@
// @ts-nocheck
import React from 'react';
import { FastField, useFormikContext } from 'formik';
import { FormattedMessage as T } from '@/components';
import { FormGroup, InputGroup, Radio } from '@blueprintjs/core';
import { If, Row, Col, ErrorMessage } from '@/components';
import { FormattedMessage as T, Row, Col, ErrorMessage } from '@/components';
import { inputIntent } from '@/utils';
/**
* Reference number form content.
*/
export default function ReferenceNumberFormContent() {
const { values } = useFormikContext();
return (
<>
{/* ------------- Auto increment mode ------------- */}
@@ -27,54 +25,13 @@ export default function ReferenceNumberFormContent() {
/>
)}
</FastField>
<If condition={values.incrementMode === 'auto'}>
<Row>
{/* ------------- Prefix ------------- */}
<Col xs={4}>
<FastField name={'numberPrefix'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'prefix'} />}
className={'form-group--'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'numberPrefix'} />}
>
<InputGroup
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col>
{/* ------------- Next number ------------- */}
<Col xs={6}>
<FastField name={'nextNumber'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'next_number'} />}
className={'form-group--next-number'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'nextNumber'} />}
>
<InputGroup
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col>
</Row>
</If>
<ReferenceNumberAutoIncrement />
{/* ------------- Manual increment mode ------------- */}
<FastField name={'incrementMode'}>
{({ form, field, meta: { error, touched } }) => (
<Radio
label={<T id={'auto_increment.field.manual_this_transaction'} />}
label={<T id={'auto_increment.field.manually'} />}
value="manual"
onChange={() => {
form.setFieldValue('incrementMode', 'manual');
@@ -85,20 +42,70 @@ export default function ReferenceNumberFormContent() {
</FastField>
{/* ------------- Transaction manual increment mode ------------- */}
<If condition={values.manualTransactionNo}>
<FastField name={'incrementMode'}>
{({ form, field, meta: { error, touched } }) => (
<Radio
label={<T id={'auto_increment.field.manually'} />}
value="manual"
onChange={() => {
form.setFieldValue('incrementMode', 'manual-transaction');
}}
checked={field.value === 'manual-transaction'}
/>
)}
</FastField>
</If>
<ReferenceNumberManualOnce />
</>
);
}
function ReferenceNumberAutoIncrement() {
const { values } = useFormikContext();
if (!values.incrementMode === 'auto') return null;
return (
<Row>
{/* ------------- Prefix ------------- */}
<Col xs={4}>
<FastField name={'numberPrefix'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'prefix'} />}
className={'form-group--'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'numberPrefix'} />}
>
<InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup>
)}
</FastField>
</Col>
{/* ------------- Next number ------------- */}
<Col xs={6}>
<FastField name={'nextNumber'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'next_number'} />}
className={'form-group--next-number'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'nextNumber'} />}
>
<InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup>
)}
</FastField>
</Col>
</Row>
);
}
function ReferenceNumberManualOnce() {
const { values } = useFormikContext();
// Do not show the field if the one manual transaction number is not presented.
if (!values.onceManualNumber) return null;
return (
<FastField name={'incrementMode'}>
{({ form, field, meta: { error, touched } }) => (
<Radio
label={<T id={'auto_increment.field.manual_this_transaction'} />}
value="manual"
onChange={() => {
form.setFieldValue('incrementMode', 'manual-transaction');
}}
checked={field.value === 'manual-transaction'}
/>
)}
</FastField>
);
}

View File

@@ -5,6 +5,7 @@ import {
transfromToSnakeCase,
transactionNumber,
} from '@/utils';
import { omit } from 'lodash';
export const defaultInvoiceNoSettings = {
nextNumber: '',
@@ -13,8 +14,8 @@ export const defaultInvoiceNoSettings = {
};
export const transformSettingsToForm = (settings) => ({
...settings,
incrementMode: settings.autoIncrement === 'true' ? 'auto' : 'manual',
...omit(settings, ['autoIncrement']),
incrementMode: settings.autoIncrement ? 'auto' : 'manual',
});
export const transformFormToSettings = (values, group) => {
@@ -25,13 +26,21 @@ export const transformFormToSettings = (values, group) => {
return optionsMapToArray(options).map((option) => ({ ...option, group }));
};
/**
* Transaction number returns auto-increment if the increment mode is auto or
* returns empty string if the increment mode is manually or returns the entered
* manual text if the increment mode is manual once just in this transaction.
*/
export const transformValuesToForm = (values) => {
const incrementNumber =
const autoIncrementNumber = transactionNumber(
values.numberPrefix,
values.nextNumber,
);
const _transactionNumber =
values.incrementMode === 'auto'
? transactionNumber(values.numberPrefix, values.nextNumber)
: values.manualTransactionNo;
const manually = values.incrementMode === 'auto' ? false : true;
return { incrementNumber, manually };
? autoIncrementNumber
: values.incrementMode === 'manual-transaction'
? values.onceManualNumber
: '';
return { transactionNumber: _transactionNumber };
};

View File

@@ -13,6 +13,15 @@ import BillFormHeaderFields from './BillFormHeaderFields';
* Fill form header.
*/
function BillFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<BillFormHeaderFields />
<BillFormBigTotal />
</div>
);
}
function BillFormBigTotal() {
const {
values: { currency_code, entries },
} = useFormikContext();
@@ -21,14 +30,12 @@ function BillFormHeader() {
const totalDueAmount = useMemo(() => sumBy(entries, 'amount'), [entries]);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<BillFormHeaderFields />
<PageFormBigNumber
label={intl.get('due_amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
</div>
<PageFormBigNumber
label={intl.get('due_amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
);
}
export default BillFormHeader;

View File

@@ -38,6 +38,7 @@ import {
import withSettings from '@/containers/Settings/withSettings';
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
import { CreditNoteSyncIncrementSettingsToForm } from './components';
/**
* Credit note form.
@@ -105,7 +106,7 @@ function CreditNoteForm({
open: submitPayload.open,
};
// Handle the request success.
const onSuccess = (response) => {
const onSuccess = () => {
AppToaster.show({
message: intl.get(
isNewMode
@@ -161,7 +162,12 @@ function CreditNoteForm({
<CreditNoteItemsEntriesEditorField />
<CreditNoteFormFooter />
<CreditNoteFloatingActions />
{/*-------- Dialogs --------*/}
<CreditNoteFormDialogs />
{/*-------- Effects --------*/}
<CreditNoteSyncIncrementSettingsToForm />
</Form>
</Formik>
</div>

View File

@@ -1,25 +1,30 @@
// @ts-nocheck
import React from 'react';
import CreditNoteNumberDialog from '@/containers/Dialogs/CreditNoteNumberDialog';
import { useFormikContext } from 'formik';
import CreditNoteNumberDialog from '@/containers/Dialogs/CreditNoteNumberDialog';
/**
* Credit note form dialogs.
*/
export default function CreditNoteFormDialogs() {
const { setFieldValue } = useFormikContext();
// Update the form once the credit number form submit confirm.
const handleCreditNumberFormConfirm = ({ incrementNumber, manually }) => {
setFieldValue('credit_note_number', incrementNumber || '');
setFieldValue('credit_note_no_manually', manually);
const handleCreditNumberFormConfirm = (settings) => {
// Set the credit note transaction no. that cames from dialog to the form.
// the `credit_note_number` will be empty except the increment mode is not auto.
setFieldValue('credit_note_number', settings.transactionNumber);
setFieldValue('credit_note_number_manually', '');
if (settings.incrementMode !== 'auto') {
setFieldValue('credit_note_number_manually', settings.transactionNumber);
}
};
const { setFieldValue } = useFormikContext();
return (
<React.Fragment>
<CreditNoteNumberDialog
dialogName={'credit-number-form'}
onConfirm={handleCreditNumberFormConfirm}
/>
</React.Fragment>
<CreditNoteNumberDialog
dialogName={'credit-number-form'}
onConfirm={handleCreditNumberFormConfirm}
/>
);
}

View File

@@ -13,6 +13,19 @@ import { PageFormBigNumber } from '@/components';
* Credit note header.
*/
function CreditNoteFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<CreditNoteFormHeaderFields />
<CreditNoteFormBigNumber />
</div>
);
}
/**
* Big total number of credit note form header.
* @returns {React.ReactNode}
*/
function CreditNoteFormBigNumber() {
const {
values: { entries, currency_code },
} = useFormikContext();
@@ -21,14 +34,11 @@ function CreditNoteFormHeader() {
const totalAmount = React.useMemo(() => getEntriesTotal(entries), [entries]);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<CreditNoteFormHeaderFields />
<PageFormBigNumber
label={intl.get('credit_note.label_amount_to_credit')}
amount={totalAmount}
currencyCode={currency_code}
/>
</div>
<PageFormBigNumber
label={intl.get('credit_note.label_amount_to_credit')}
amount={totalAmount}
currencyCode={currency_code}
/>
);
}

View File

@@ -1,5 +1,6 @@
// @ts-nocheck
import React from 'react';
import * as R from 'ramda';
import classNames from 'classnames';
import styled from 'styled-components';
import {
@@ -9,7 +10,8 @@ import {
ControlGroup,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, Field, ErrorMessage } from 'formik';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes';
import {
CustomerSelectField,
@@ -18,11 +20,10 @@ import {
Icon,
FormattedMessage as T,
CustomerDrawerLink,
FFormGroup,
FInputGroup,
} from '@/components';
import {
customerNameFieldShouldUpdate,
useObserveCreditNoSettings,
} from './utils';
import { customerNameFieldShouldUpdate } from './utils';
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
import withSettings from '@/containers/Settings/withSettings';
@@ -30,49 +31,99 @@ import withDialogActions from '@/containers/Dialog/withDialogActions';
import { CreditNoteExchangeRateInputField } from './components';
import {
momentFormatter,
compose,
tansformDateValue,
inputIntent,
handleDateChange,
} from '@/utils';
/**
* Credit note transaction number field.
*/
const CreditNoteTransactionNoField = R.compose(
withDialogActions,
withSettings(({ creditNoteSettings }) => ({
creditAutoIncrement: creditNoteSettings?.autoIncrement,
creditNextNumber: creditNoteSettings?.nextNumber,
creditNumberPrefix: creditNoteSettings?.numberPrefix,
})),
)(
({
// #withDialogActions
openDialog,
// #withSettings
creditAutoIncrement,
}) => {
const { values, setFieldValue } = useFormikContext();
// Handle credit number changing.
const handleCreditNumberChange = () => {
openDialog('credit-number-form');
};
// Handle credit note no. field blur.
const handleCreditNoBlur = (event) => {
const newValue = event.target.value;
// Show the confirmation dialog if the value has changed and auto-increment
// mode is enabled.
if (values.credit_note_no !== newValue && creditAutoIncrement) {
openDialog('credit-number-form', {
initialFormValues: {
onceManualNumber: newValue,
incrementMode: 'manual-transaction',
},
});
}
// Setting the credit note number to the form will be manually in case
// auto-increment is disable.
if (!creditAutoIncrement) {
setFieldValue('credit_note_number', newValue);
setFieldValue('credit_note_number_manually', newValue);
}
};
return (
<FFormGroup
name={'credit_note_number'}
label={<T id={'credit_note.label_credit_note'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
>
<ControlGroup fill={true}>
<FInputGroup
name={'credit_note_number'}
minimal={true}
value={values.credit_note_number}
asyncControl={true}
onBlur={handleCreditNoBlur}
onChange={() => {}}
/>
<InputPrependButton
buttonProps={{
onClick: handleCreditNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T id={'setting_your_auto_generated_credit_note_number'} />
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FFormGroup>
);
},
);
/**
* Credit note form header fields.
*/
function CreditNoteFormHeaderFields({
// #withDialogActions
openDialog,
// #withSettings
creditAutoIncrement,
creditNumberPrefix,
creditNextNumber,
}) {
export default function CreditNoteFormHeaderFields({}) {
// Credit note form context.
const { customers } = useCreditNoteFormContext();
// Handle credit number changing.
const handleCreditNumberChange = () => {
openDialog('credit-number-form');
};
// Handle credit no. field blur.
const handleCreditNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && creditAutoIncrement) {
openDialog('credit-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs credit number settings with form.
useObserveCreditNoSettings(creditNumberPrefix, creditNextNumber);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
@@ -119,7 +170,6 @@ function CreditNoteFormHeaderFields({
name={'exchange_rate'}
formGroupProps={{ label: ' ', inline: true }}
/>
{/* ----------- Credit note date ----------- */}
<FastField name={'credit_note_date'}>
{({ form, field: { value }, meta: { error, touched } }) => (
@@ -146,43 +196,8 @@ function CreditNoteFormHeaderFields({
)}
</FastField>
{/* ----------- Credit note # ----------- */}
<Field name={'credit_note_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'credit_note.label_credit_note'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
className={classNames(
'form-group--credit_note_number',
CLASSES.FILL,
)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="credit_note_number" />}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleCreditNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleCreditNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T id={'setting_your_auto_generated_credit_note_number'} />
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</Field>
<CreditNoteTransactionNoField />
{/* ----------- Reference ----------- */}
<FastField name={'reference_no'}>
{({ field, meta: { error, touched } }) => (
@@ -200,14 +215,6 @@ function CreditNoteFormHeaderFields({
</div>
);
}
export default compose(
withDialogActions,
withSettings(({ creditNoteSettings }) => ({
creditAutoIncrement: creditNoteSettings?.autoIncrement,
creditNextNumber: creditNoteSettings?.nextNumber,
creditNumberPrefix: creditNoteSettings?.numberPrefix,
})),
)(CreditNoteFormHeaderFields);
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;

View File

@@ -1,16 +1,18 @@
// @ts-nocheck
import React from 'react';
import React, { useEffect } from 'react';
import { useFormikContext } from 'formik';
import * as R from 'ramda';
import { ExchangeRateInputGroup } from '@/components';
import { useCurrentOrganization } from '@/hooks/state';
import { useCreditNoteIsForeignCustomer } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
/**
* credit exchange rate input field.
* @returns {JSX.Element}
*/
export function CreditNoteExchangeRateInputField({ ...props }) {
export function CreditNoteExchangeRateInputField({ ...props }) {
const currentOrganization = useCurrentOrganization();
const { values } = useFormikContext();
@@ -27,4 +29,30 @@ import { useCreditNoteIsForeignCustomer } from './utils';
{...props}
/>
);
}
}
/**
* Syncs credit note auto-increment settings to form.
* @return {React.ReactNode}
*/
export const CreditNoteSyncIncrementSettingsToForm = R.compose(
withSettings(({ creditNoteSettings }) => ({
creditAutoIncrement: creditNoteSettings?.autoIncrement,
creditNextNumber: creditNoteSettings?.nextNumber,
creditNumberPrefix: creditNoteSettings?.numberPrefix,
})),
)(({ creditAutoIncrement, creditNextNumber, creditNumberPrefix }) => {
const { setFieldValue } = useFormikContext();
useEffect(() => {
// Do not update if the credit note auto-increment mode is disabled.
if (!creditAutoIncrement) return;
setFieldValue(
'credit_note_number',
transactionNumber(creditNumberPrefix, creditNextNumber),
);
}, [setFieldValue, creditNumberPrefix, creditNextNumber]);
return null;
});

View File

@@ -40,7 +40,8 @@ export const defaultCreditNote = {
customer_id: '',
credit_note_date: moment(new Date()).format('YYYY-MM-DD'),
credit_note_number: '',
credit_note_no_manually: false,
// Holds the credit note number that entered manually only.
credit_note_number_manually: false,
open: '',
reference_no: '',
note: '',
@@ -130,18 +131,6 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => {
);
};
/**
* Syncs invoice no. settings with form.
*/
export const useObserveCreditNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const creditNo = transactionNumber(prefix, nextNumber);
setFieldValue('credit_note_number', creditNo);
}, [setFieldValue, prefix, nextNumber]);
};
export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = useCreditNoteFormContext();

View File

@@ -19,6 +19,7 @@ import EstimateFloatingActions from './EstimateFloatingActions';
import EstimateFormFooter from './EstimateFormFooter';
import EstimateFormDialogs from './EstimateFormDialogs';
import EstimtaeFormTopBar from './EstimtaeFormTopBar';
import { EstimateIncrementSyncSettingsToForm } from './components';
import withSettings from '@/containers/Settings/withSettings';
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
@@ -31,6 +32,7 @@ import {
defaultEstimate,
transfromsFormValuesToRequest,
handleErrors,
resetFormState,
} from './utils';
/**
@@ -40,7 +42,7 @@ function EstimateForm({
// #withSettings
estimateNextNumber,
estimateNumberPrefix,
estimateIncrementMode,
estimateAutoIncrementMode,
// #withCurrentOrganization
organization: { base_currency },
@@ -66,14 +68,16 @@ function EstimateForm({
? { ...transformToEditForm(estimate) }
: {
...defaultEstimate,
...(estimateIncrementMode && {
// If the auto-increment mode is enabled, take the next estimate
// number from the settings.
...(estimateAutoIncrementMode && {
estimate_number: estimateNumber,
}),
entries: orderingLinesIndexes(defaultEstimate.entries),
currency_code: base_currency,
}),
}),
[estimate, estimateNumber, estimateIncrementMode, base_currency],
[estimate, estimateNumber, estimateAutoIncrementMode, base_currency],
);
// Handles form submit.
@@ -118,7 +122,7 @@ function EstimateForm({
history.push('/estimates');
}
if (submitPayload.resetForm) {
resetForm();
resetFormState({ resetForm, initialValues, values });
}
};
// Handle the request error.
@@ -161,7 +165,11 @@ function EstimateForm({
<EstimateFormFooter />
<EstimateFloatingActions />
{/*------- Dialogs -------*/}
<EstimateFormDialogs />
{/*------- Effects -------*/}
<EstimateIncrementSyncSettingsToForm />
</Form>
</Formik>
</div>
@@ -172,7 +180,7 @@ export default compose(
withSettings(({ estimatesSettings }) => ({
estimateNextNumber: estimatesSettings?.nextNumber,
estimateNumberPrefix: estimatesSettings?.numberPrefix,
estimateIncrementMode: estimatesSettings?.autoIncrement,
estimateAutoIncrementMode: estimatesSettings?.autoIncrement,
})),
withCurrentOrganization(),
)(EstimateForm);

View File

@@ -9,10 +9,14 @@ import EstimateNumberDialog from '@/containers/Dialogs/EstimateNumberDialog';
export default function EstimateFormDialogs() {
const { setFieldValue } = useFormikContext();
// Update the form once the invoice number form submit confirm.
const handleEstimateNumberFormConfirm = ({ incrementNumber, manually }) => {
setFieldValue('estimate_number', incrementNumber || '');
setFieldValue('estimate_number_manually', manually);
// Update the form once the estimate number form submit confirm.
const handleEstimateNumberFormConfirm = (settings) => {
setFieldValue('estimate_number', settings.transactionNumber);
setFieldValue('estimate_number_manually', '');
if (settings.incrementMode !== 'auto') {
setFieldValue('estimate_number_manually', settings.transactionNumber);
}
};
return (

View File

@@ -12,6 +12,19 @@ import { PageFormBigNumber } from '@/components';
// Estimate form top header.
function EstimateFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<EstimateFormHeaderFields />
<EstimateFormBigTotal />
</div>
);
}
/**
* Big total of estimate form header.
* @returns {React.ReactNode}
*/
function EstimateFormBigTotal() {
const {
values: { entries, currency_code },
} = useFormikContext();
@@ -20,15 +33,11 @@ function EstimateFormHeader() {
const totalDueAmount = useMemo(() => getEntriesTotal(entries), [entries]);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<EstimateFormHeaderFields />
<PageFormBigNumber
label={intl.get('amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
</div>
<PageFormBigNumber
label={intl.get('amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
);
}

View File

@@ -1,5 +1,6 @@
// @ts-nocheck
import React from 'react';
import * as R from 'ramda';
import styled from 'styled-components';
import classNames from 'classnames';
import {
@@ -10,12 +11,21 @@ import {
ControlGroup,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FeatureCan, FFormGroup, FormattedMessage as T } from '@/components';
import { FastField, Field, ErrorMessage } from 'formik';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import {
FeatureCan,
FFormGroup,
FInputGroup,
FormattedMessage as T,
CustomerSelectField,
FieldRequiredHint,
Icon,
InputPrependButton,
CustomerDrawerLink,
} from '@/components';
import {
momentFormatter,
compose,
tansformDateValue,
inputIntent,
handleDateChange,
@@ -23,57 +33,99 @@ import {
import { customersFieldShouldUpdate } from './utils';
import { CLASSES } from '@/constants/classes';
import { Features } from '@/constants';
import {
CustomerSelectField,
FieldRequiredHint,
Icon,
InputPrependButton,
CustomerDrawerLink,
} from '@/components';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import withSettings from '@/containers/Settings/withSettings';
import { ProjectsSelect } from '@/containers/Projects/components';
import {
EstimateExchangeRateInputField,
EstimateProjectSelectButton,
} from './components';
import { useObserveEstimateNoSettings } from './utils';
import { useEstimateFormContext } from './EstimateFormProvider';
/**
* Estimate number field of estimate form.
*/
const EstimateFormEstimateNumberField = R.compose(
withDialogActions,
withSettings(({ estimatesSettings }) => ({
estimateNextNumber: estimatesSettings?.nextNumber,
estimateNumberPrefix: estimatesSettings?.numberPrefix,
estimateAutoIncrement: estimatesSettings?.autoIncrement,
})),
)(
({
// #withDialogActions
openDialog,
// #withSettings
estimateAutoIncrement,
}) => {
const { values, setFieldValue } = useFormikContext();
const handleEstimateNumberBtnClick = () => {
openDialog('estimate-number-form', {});
};
// Handle estimate no. field blur.
const handleEstimateNoBlur = (event) => {
const newValue = event.target.value;
// Show the confirmation dialog if the value has changed and auto-increment
// mode is enabled.
if (values.estimate_number !== newValue && estimateAutoIncrement) {
openDialog('estimate-number-form', {
initialFormValues: {
onceManualNumber: newValue,
incrementMode: 'manual-transaction',
},
});
}
// Setting the estimate number to the form will be manually in case
// auto-increment is disable.
if (!estimateAutoIncrement) {
setFieldValue('estimate_number', newValue);
setFieldValue('estimate_number_manually', newValue);
}
};
return (
<FFormGroup
name={'estimate_number'}
label={<T id={'estimate'} />}
inline={true}
>
<ControlGroup fill={true}>
<FInputGroup
name={'estimate_number'}
minimal={true}
asyncControl={true}
onBlur={handleEstimateNoBlur}
onChange={() => {}}
/>
<InputPrependButton
buttonProps={{
onClick: handleEstimateNumberBtnClick,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: <T id={'setting_your_auto_generated_estimate_number'} />,
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FFormGroup>
);
},
);
/**
* Estimate form header.
*/
function EstimateFormHeader({
// #withDialogActions
openDialog,
// #withSettings
estimateAutoIncrement,
estimateNumberPrefix,
estimateNextNumber,
}) {
export default function EstimateFormHeader() {
const { customers, projects } = useEstimateFormContext();
const handleEstimateNumberBtnClick = () => {
openDialog('estimate-number-form', {});
};
const handleEstimateNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && estimateAutoIncrement) {
openDialog('estimate-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs estimate number settings with the form.
useObserveEstimateNoSettings(estimateNumberPrefix, estimateNextNumber);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
@@ -113,13 +165,12 @@ function EstimateFormHeader({
)}
</FastField>
{/* ----------- Exchange rate ----------- */}
{/* ----------- Exchange Rate ----------- */}
<EstimateExchangeRateInputField
name={'exchange_rate'}
formGroupProps={{ label: ' ', inline: true }}
/>
{/* ----------- Estimate date ----------- */}
{/* ----------- Estimate Date ----------- */}
<FastField name={'estimate_date'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
@@ -176,40 +227,7 @@ function EstimateFormHeader({
</FastField>
{/* ----------- Estimate number ----------- */}
<Field name={'estimate_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'estimate'} />}
inline={true}
className={('form-group--estimate-number', CLASSES.FILL)}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="estimate_number" />}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleEstimateNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleEstimateNumberBtnClick,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T id={'setting_your_auto_generated_estimate_number'} />
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</Field>
<EstimateFormEstimateNumberField />
{/* ----------- Reference ----------- */}
<FastField name={'reference'}>
@@ -246,15 +264,6 @@ function EstimateFormHeader({
);
}
export default compose(
withDialogActions,
withSettings(({ estimatesSettings }) => ({
estimateNextNumber: estimatesSettings?.nextNumber,
estimateNumberPrefix: estimatesSettings?.numberPrefix,
estimateAutoIncrement: estimatesSettings?.autoIncrement,
})),
)(EstimateFormHeader);
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -1,18 +1,21 @@
// @ts-nocheck
import React from 'react';
import React, { useEffect } from 'react';
import intl from 'react-intl-universal';
import { Button } from '@blueprintjs/core';
import * as R from 'ramda';
import { useFormikContext } from 'formik';
import { ExchangeRateInputGroup } from '@/components';
import { useCurrentOrganization } from '@/hooks/state';
import { useEstimateIsForeignCustomer } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
import { useUpdateEffect } from '@/hooks';
/**
* Estimate exchange rate input field.
* @returns {JSX.Element}
*/
export function EstimateExchangeRateInputField({ ...props }) {
export function EstimateExchangeRateInputField({ ...props }) {
const currentOrganization = useCurrentOrganization();
const { values } = useFormikContext();
@@ -35,6 +38,37 @@ import { useEstimateIsForeignCustomer } from './utils';
* Estimate project select.
* @returns {JSX.Element}
*/
export function EstimateProjectSelectButton({ label }) {
export function EstimateProjectSelectButton({ label }) {
return <Button text={label ?? intl.get('select_project')} />;
}
/**
* Syncs the estimate auto-increment settings to estimate form.
* @returns {React.ReactNode}
*/
export const EstimateIncrementSyncSettingsToForm = R.compose(
withSettings(({ estimatesSettings }) => ({
estimateNextNumber: estimatesSettings?.nextNumber,
estimateNumberPrefix: estimatesSettings?.numberPrefix,
estimateAutoIncrement: estimatesSettings?.autoIncrement,
})),
)(({ estimateNextNumber, estimateNumberPrefix, estimateAutoIncrement }) => {
const { setFieldValue } = useFormikContext();
useUpdateEffect(() => {
// Do not update if the estimate auto-increment mode is disabled.
if (!estimateAutoIncrement) return null;
setFieldValue(
'estimate_number',
transactionNumber(estimateNumberPrefix, estimateNextNumber),
);
}, [
setFieldValue,
estimateNumberPrefix,
estimateNextNumber,
estimateAutoIncrement,
]);
return null;
});

View File

@@ -7,7 +7,6 @@ import { useFormikContext } from 'formik';
import { omit, first } from 'lodash';
import {
defaultFastFieldShouldUpdate,
transactionNumber,
repeatValue,
transformToForm,
formattedAmount,
@@ -37,6 +36,8 @@ export const defaultEstimate = {
estimate_date: moment(new Date()).format('YYYY-MM-DD'),
expiration_date: moment(new Date()).format('YYYY-MM-DD'),
estimate_number: '',
// Holds the estimate number that entered manually only.
estimate_number_manually: '',
delivered: '',
reference: '',
note: '',
@@ -74,18 +75,6 @@ export const transformToEditForm = (estimate) => {
};
};
/**
* Syncs estimate number of the settings with the context form.
*/
export const useObserveEstimateNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const estimateNo = transactionNumber(prefix, nextNumber);
setFieldValue('estimate_number', estimateNo);
}, [setFieldValue, prefix, nextNumber]);
};
/**
* Detarmines customers fast field when update.
*/
@@ -154,6 +143,8 @@ export const transfromsFormValuesToRequest = (values) => {
);
return {
...omit(values, ['estimate_number_manually', 'estimate_number']),
// The `estimate_number_manually` will be presented just if the auto-increment
// is disable, always both attributes hold the same value in manual mode.
...(values.estimate_number_manually && {
estimate_number: values.estimate_number,
}),
@@ -235,3 +226,17 @@ export const useEstimateIsForeignCustomer = () => {
);
return isForeignCustomer;
};
/**
* Resets the form values.
*/
export const resetFormState = ({ initialValues, values, resetForm }) => {
resetForm({
values: {
// Reset the all values except the warehouse and brand id.
...initialValues,
warehouse_id: values.warehouse_id,
brand_id: values.brand_id,
},
});
};

View File

@@ -31,7 +31,9 @@ import {
defaultInvoice,
transformErrors,
transformValueToRequest,
resetFormState,
} from './utils';
import { InvoiceNoSyncSettingsToForm } from './components';
/**
* Invoice form.
@@ -40,7 +42,7 @@ function InvoiceForm({
// #withSettings
invoiceNextNumber,
invoiceNumberPrefix,
invoiceIncrementMode,
invoiceAutoIncrementMode,
// #withCurrentOrganization
organization: { base_currency },
@@ -64,23 +66,21 @@ function InvoiceForm({
invoiceNextNumber,
);
// Form initial values.
const initialValues = useMemo(
() => ({
...(!isEmpty(invoice)
? { ...transformToEditForm(invoice) }
: {
...defaultInvoice,
...(invoiceIncrementMode && {
invoice_no: invoiceNumber,
}),
entries: orderingLinesIndexes(defaultInvoice.entries),
currency_code: base_currency,
...newInvoice,
const initialValues = {
...(!isEmpty(invoice)
? { ...transformToEditForm(invoice) }
: {
...defaultInvoice,
// If the auto-increment mode is enabled, take the next invoice
// number from the settings.
...(invoiceAutoIncrementMode && {
invoice_no: invoiceNumber,
}),
}),
[invoice, newInvoice, invoiceNumber, invoiceIncrementMode, base_currency],
);
entries: orderingLinesIndexes(defaultInvoice.entries),
currency_code: base_currency,
...newInvoice,
}),
};
// Handles form submit.
const handleSubmit = (values, { setSubmitting, setErrors, resetForm }) => {
setSubmitting(true);
@@ -105,7 +105,6 @@ function InvoiceForm({
delivered: submitPayload.deliver,
from_estimate_id: estimateId,
};
// Handle the request success.
const onSuccess = () => {
AppToaster.show({
@@ -123,10 +122,9 @@ function InvoiceForm({
history.push('/invoices');
}
if (submitPayload.resetForm) {
resetForm();
resetFormState({ resetForm, initialValues, values });
}
};
// Handle the request error.
const onError = ({
response: {
@@ -144,7 +142,6 @@ function InvoiceForm({
createInvoiceMutate(form).then(onSuccess).catch(onError);
}
};
// Create invoice form schema.
const CreateInvoiceFormSchema = getCreateInvoiceFormSchema();
@@ -172,7 +169,12 @@ function InvoiceForm({
<InvoiceItemsEntriesEditorField />
<InvoiceFormFooter />
<InvoiceFloatingActions />
{/*---------- Dialogs ----------*/}
<InvoiceFormDialogs />
{/*---------- Effects ----------*/}
<InvoiceNoSyncSettingsToForm />
</Form>
</Formik>
</div>
@@ -184,7 +186,7 @@ export default compose(
withSettings(({ invoiceSettings }) => ({
invoiceNextNumber: invoiceSettings?.nextNumber,
invoiceNumberPrefix: invoiceSettings?.numberPrefix,
invoiceIncrementMode: invoiceSettings?.autoIncrement,
invoiceAutoIncrementMode: invoiceSettings?.autoIncrement,
})),
withCurrentOrganization(),
)(InvoiceForm);

View File

@@ -1,7 +1,8 @@
// @ts-nocheck
import React from 'react';
import InvoiceNumberDialog from '@/containers/Dialogs/InvoiceNumberDialog';
import { useFormikContext } from 'formik';
import InvoiceNumberDialog from '@/containers/Dialogs/InvoiceNumberDialog';
import { DialogsName } from '@/constants/dialogs';
/**
* Invoice form dialogs.
@@ -10,17 +11,21 @@ export default function InvoiceFormDialogs() {
const { setFieldValue } = useFormikContext();
// Update the form once the invoice number form submit confirm.
const handleInvoiceNumberFormConfirm = ({ incrementNumber, manually }) => {
setFieldValue('invoice_no', incrementNumber || '');
setFieldValue('invoice_no_manually', manually);
const handleInvoiceNumberFormConfirm = (settings) => {
// Set the invoice transaction no. that cames from dialog to the form.
// the `invoice_no_manually` will be empty except the increment mode is not auto.
setFieldValue('invoice_no', settings.transactionNumber);
setFieldValue('invoice_no_manually', '');
if (settings.incrementMode !== 'auto') {
setFieldValue('invoice_no_manually', settings.transactionNumber);
}
};
return (
<>
<InvoiceNumberDialog
dialogName={'invoice-number-form'}
onConfirm={handleInvoiceNumberFormConfirm}
/>
</>
<InvoiceNumberDialog
dialogName={DialogsName.InvoiceNumberSettings}
onConfirm={handleInvoiceNumberFormConfirm}
/>
);
}

View File

@@ -14,6 +14,19 @@ import { useInvoiceTotal } from './utils';
* Invoice form header section.
*/
function InvoiceFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<InvoiceFormHeaderFields />
<InvoiceFormBigTotal />
</div>
);
}
/**
* Big total of invoice form header.
* @returns {React.ReactNode}
*/
function InvoiceFormBigTotal() {
const {
values: { currency_code },
} = useFormikContext();
@@ -22,14 +35,11 @@ function InvoiceFormHeader() {
const totalDueAmount = useInvoiceTotal();
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<InvoiceFormHeaderFields />
<PageFormBigNumber
label={intl.get('due_amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
</div>
<PageFormBigNumber
label={intl.get('due_amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
);
}
export default InvoiceFormHeader;

View File

@@ -10,7 +10,8 @@ import {
ControlGroup,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, Field, ErrorMessage, useFormikContext } from 'formik';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import * as R from 'ramda';
import {
FFormGroup,
@@ -23,14 +24,16 @@ import {
Icon,
InputPrependButton,
FeatureCan,
FInputGroup,
} from '@/components';
import { momentFormatter, compose, tansformDateValue } from '@/utils';
import { CLASSES } from '@/constants/classes';
import { inputIntent, handleDateChange } from '@/utils';
import {
useObserveInvoiceNoSettings,
customerNameFieldShouldUpdate,
} from './utils';
momentFormatter,
tansformDateValue,
inputIntent,
handleDateChange,
} from '@/utils';
import { CLASSES } from '@/constants/classes';
import { customerNameFieldShouldUpdate } from './utils';
import { useInvoiceFormContext } from './InvoiceFormProvider';
import {
@@ -42,48 +45,98 @@ import {
ProjectBillableEntriesLink,
} from '@/containers/Projects/components';
import { Features } from '@/constants';
import { DialogsName } from '@/constants/dialogs';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
/**
* Invoice number field of invoice form.
*/
const InvoiceFormInvoiceNumberField = R.compose(
withDialogActions,
withSettings(({ invoiceSettings }) => ({
invoiceAutoIncrement: invoiceSettings?.autoIncrement,
})),
)(
({
// #withDialogActions
openDialog,
// #withSettings
invoiceAutoIncrement,
}) => {
// Formik context.
const { values, setFieldValue } = useFormikContext();
// Handle invoice number changing.
const handleInvoiceNumberChange = () => {
openDialog(DialogsName.InvoiceNumberSettings);
};
// Handle invoice no. field blur.
const handleInvoiceNoBlur = (event) => {
const newValue = event.target.value;
// Show the confirmation dialog if the value has changed and auto-increment
// mode is enabled.
if (values.invoice_no !== newValue && invoiceAutoIncrement) {
openDialog(DialogsName.InvoiceNumberSettings, {
initialFormValues: {
onceManualNumber: newValue,
incrementMode: 'manual-transaction',
},
});
}
// Setting the invoice number to the form will be manually in case
// auto-increment is disable.
if (!invoiceAutoIncrement) {
setFieldValue('invoice_no', newValue);
setFieldValue('invoice_no_manually', newValue);
}
};
return (
<FFormGroup
name={'invoice_no'}
label={<T id={'invoice_no'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
fastField={true}
>
<ControlGroup fill={true}>
<FInputGroup
name={'invoice_no'}
minimal={true}
asyncControl={true}
onBlur={handleInvoiceNoBlur}
onChange={() => {}}
/>
<InputPrependButton
buttonProps={{
onClick: handleInvoiceNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: <T id={'setting_your_auto_generated_invoice_number'} />,
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FFormGroup>
);
},
);
InvoiceFormInvoiceNumberField.displayName = 'InvoiceFormInvoiceNumberField';
/**
* Invoice form header fields.
*/
function InvoiceFormHeaderFields({
// #withDialogActions
openDialog,
// #withSettings
invoiceAutoIncrement,
invoiceNumberPrefix,
invoiceNextNumber,
}) {
export default function InvoiceFormHeaderFields() {
// Invoice form context.
const { customers, projects } = useInvoiceFormContext();
// Formik context.
const { values } = useFormikContext();
// Handle invoice number changing.
const handleInvoiceNumberChange = () => {
openDialog('invoice-number-form');
};
// Handle invoice no. field blur.
const handleInvoiceNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && invoiceAutoIncrement) {
openDialog('invoice-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs invoice number settings with form.
useObserveInvoiceNoSettings(invoiceNumberPrefix, invoiceNextNumber);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
@@ -189,40 +242,7 @@ function InvoiceFormHeaderFields({
</Row>
{/* ----------- Invoice number ----------- */}
<Field name={'invoice_no'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'invoice_no'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
className={classNames('form-group--invoice-no', CLASSES.FILL)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="invoice_no" />}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleInvoiceNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleInvoiceNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T id={'setting_your_auto_generated_invoice_number'} />
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</Field>
<InvoiceFormInvoiceNumberField />
{/* ----------- Reference ----------- */}
<FastField name={'reference_no'}>
@@ -264,15 +284,6 @@ function InvoiceFormHeaderFields({
);
}
export default compose(
withDialogActions,
withSettings(({ invoiceSettings }) => ({
invoiceAutoIncrement: invoiceSettings?.autoIncrement,
invoiceNextNumber: invoiceSettings?.nextNumber,
invoiceNumberPrefix: invoiceSettings?.numberPrefix,
})),
)(InvoiceFormHeaderFields);
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -1,11 +1,15 @@
// @ts-nocheck
import React from 'react';
import intl from 'react-intl-universal';
import * as R from 'ramda';
import { Button } from '@blueprintjs/core';
import { useFormikContext } from 'formik';
import { ExchangeRateInputGroup } from '@/components';
import { useCurrentOrganization } from '@/hooks/state';
import { useInvoiceIsForeignCustomer } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { useUpdateEffect } from '@/hooks';
import { transactionNumber } from '@/utils';
/**
* Invoice exchange rate input field.
@@ -37,3 +41,28 @@ export function InvoiceExchangeRateInputField({ ...props }) {
export function InvoiceProjectSelectButton({ label }) {
return <Button text={label ?? intl.get('select_project')} />;
}
/**
* Syncs invoice auto-increment settings to invoice form once update.
*/
export const InvoiceNoSyncSettingsToForm = R.compose(
withSettings(({ invoiceSettings }) => ({
invoiceAutoIncrement: invoiceSettings?.autoIncrement,
invoiceNextNumber: invoiceSettings?.nextNumber,
invoiceNumberPrefix: invoiceSettings?.numberPrefix,
})),
)(({ invoiceAutoIncrement, invoiceNextNumber, invoiceNumberPrefix }) => {
const { setFieldValue } = useFormikContext();
useUpdateEffect(() => {
// Do not update if the invoice auto-increment mode is disabled.
if (!invoiceAutoIncrement) return null;
setFieldValue(
'invoice_no',
transactionNumber(invoiceNumberPrefix, invoiceNextNumber),
);
}, [setFieldValue, invoiceNumberPrefix, invoiceNextNumber]);
return null;
});

View File

@@ -43,7 +43,8 @@ export const defaultInvoice = {
due_date: moment().format('YYYY-MM-DD'),
delivered: '',
invoice_no: '',
invoice_no_manually: false,
// Holds the invoice number that entered manually only.
invoice_no_manually: '',
reference_no: '',
invoice_message: '',
terms_conditions: '',
@@ -108,18 +109,6 @@ export const transformErrors = (errors, { setErrors }) => {
}
};
/**
* Syncs invoice no. settings with form.
*/
export const useObserveInvoiceNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const invoiceNo = transactionNumber(prefix, nextNumber);
setFieldValue('invoice_no', invoiceNo);
}, [setFieldValue, prefix, nextNumber]);
};
/**
* Detarmines customer name field when should update.
*/
@@ -166,6 +155,8 @@ export function transformValueToRequest(values) {
);
return {
...omit(values, ['invoice_no', 'invoice_no_manually']),
// The `invoice_no_manually` will be presented just if the auto-increment
// is disable, always both attributes hold the same value in manual mode.
...(values.invoice_no_manually && {
invoice_no: values.invoice_no,
}),
@@ -279,3 +270,14 @@ export const useInvoiceIsForeignCustomer = () => {
);
return isForeignCustomer;
};
export const resetFormState = ({ initialValues, values, resetForm }) => {
resetForm({
values: {
// Reset the all values except the warehouse and brand id.
...initialValues,
warehouse_id: values.warehouse_id,
brand_id: values.brand_id,
},
});
};

View File

@@ -1,11 +1,11 @@
// @ts-nocheck
import React, { useMemo } from 'react';
import { sumBy, isEmpty, defaultTo } from 'lodash';
import intl from 'react-intl-universal';
import classNames from 'classnames';
import { Formik, Form } from 'formik';
import { omit, sumBy, pick, isEmpty, defaultTo } from 'lodash';
import { Intent } from '@blueprintjs/core';
import { useHistory } from 'react-router-dom';
import { Intent } from '@blueprintjs/core';
import '@/style/pages/PaymentReceive/PageForm.scss';
@@ -35,7 +35,9 @@ import {
transformToEditForm,
transformFormToRequest,
transformErrors,
resetFormState,
} from './utils';
import { PaymentReceiveSyncIncrementSettingsToForm } from './components';
/**
* Payment Receive form.
@@ -68,7 +70,6 @@ function PaymentReceiveForm({
paymentReceiveNumberPrefix,
paymentReceiveNextNumber,
);
// Form initial values.
const initialValues = useMemo(
() => ({
@@ -76,10 +77,12 @@ function PaymentReceiveForm({
? transformToEditForm(paymentReceiveEditPage, paymentEntriesEditPage)
: {
...defaultPaymentReceive,
// If the auto-increment mode is enabled, take the next payment
// number from the settings.
...(paymentReceiveAutoIncrement && {
payment_receive_no: nextPaymentNumber,
deposit_account_id: defaultTo(preferredDepositAccount, ''),
}),
deposit_account_id: defaultTo(preferredDepositAccount, ''),
currency_code: base_currency,
}),
}),
@@ -113,7 +116,8 @@ function PaymentReceiveForm({
const form = transformFormToRequest(values);
// Handle request response success.
const onSaved = (response) => {
const onSaved = () => {
setSubmitting(false);
AppToaster.show({
message: intl.get(
paymentReceiveId
@@ -122,13 +126,12 @@ function PaymentReceiveForm({
),
intent: Intent.SUCCESS,
});
setSubmitting(false);
if (submitPayload.redirect) {
history.push('/payment-receives');
}
if (submitPayload.resetForm) {
resetForm();
resetFormState({ resetForm, initialValues, values });
}
};
// Handle request response errors.
@@ -176,6 +179,9 @@ function PaymentReceiveForm({
<PaymentReceiveFormFooter />
<PaymentReceiveFloatingActions />
{/* ------- Effects ------- */}
<PaymentReceiveSyncIncrementSettingsToForm />
{/* ------- Alerts & Dialogs ------- */}
<PaymentReceiveFormAlerts />
<PaymentReceiveFormDialogs />

View File

@@ -9,17 +9,21 @@ import PaymentReceiveNumberDialog from '@/containers/Dialogs/PaymentReceiveNumbe
export default function PaymentReceiveFormDialogs() {
const { setFieldValue } = useFormikContext();
const handleUpdatePaymentNumber = ({ incrementNumber, manually }) => {
setFieldValue('payment_receive_no', incrementNumber);
setFieldValue('payment_receive_no_manually', manually)
const handleUpdatePaymentNumber = (settings) => {
// Set the payment transaction no. that cames from dialog to the form.
// the `payment_receive_no_manually` will be empty except the increment mode is not auto.
setFieldValue('payment_receive_no', settings.transactionNumber);
setFieldValue('payment_receive_no_manually', '');
if (settings.incrementMode !== 'auto') {
setFieldValue('payment_receive_no_manually', settings.transactionNumber);
}
};
return (
<>
<PaymentReceiveNumberDialog
dialogName={'payment-receive-number-form'}
onConfirm={handleUpdatePaymentNumber}
/>
</>
<PaymentReceiveNumberDialog
dialogName={'payment-receive-number-form'}
onConfirm={handleUpdatePaymentNumber}
/>
);
}

View File

@@ -13,6 +13,21 @@ import PaymentReceiveHeaderFields from './PaymentReceiveHeaderFields';
* Payment receive form header.
*/
function PaymentReceiveFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
<PaymentReceiveHeaderFields />
<PaymentReceiveFormBigTotal />
</div>
</div>
);
}
/**
* Big total amount of payment receive form.
* @returns {React.ReactNode}
*/
function PaymentReceiveFormBigTotal() {
// Formik form context.
const {
values: { currency_code, entries },
@@ -25,20 +40,14 @@ function PaymentReceiveFormHeader() {
);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
<PaymentReceiveHeaderFields />
<div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}>
<div class="big-amount">
<span class="big-amount__label">
<T id={'amount_received'} />
</span>
<h1 class="big-amount__number">
<Money amount={paymentFullAmount} currency={currency_code} />
</h1>
</div>
</div>
<div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}>
<div class="big-amount">
<span class="big-amount__label">
<T id={'amount_received'} />
</span>
<h1 class="big-amount__number">
<Money amount={paymentFullAmount} currency={currency_code} />
</h1>
</div>
</div>
);

View File

@@ -12,13 +12,13 @@ import {
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { toSafeInteger } from 'lodash';
import { FeatureCan, FormattedMessage as T } from '@/components';
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
import * as R from 'ramda';
import { FInputGroup, FeatureCan, FormattedMessage as T } from '@/components';
import { useAutofocus } from '@/hooks';
import { CLASSES } from '@/constants/classes';
import {
compose,
safeSumBy,
momentFormatter,
tansformDateValue,
@@ -48,10 +48,8 @@ import {
import withDialogActions from '@/containers/Dialog/withDialogActions';
import withSettings from '@/containers/Settings/withSettings';
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
import {
useObservePaymentNoSettings,
amountPaymentEntries,
fullAmountPaymentEntries,
customersFieldShouldUpdate,
@@ -59,21 +57,91 @@ import {
} from './utils';
import { Features } from '@/constants';
/**
* Payment receive number field.
*/
const PaymentReceivePaymentNoField = R.compose(
withSettings(({ paymentReceiveSettings }) => ({
paymentReceiveAutoIncrement: paymentReceiveSettings?.autoIncrement,
})),
withDialogActions,
)(
({
// #withDialogActions
openDialog,
// #withSettings
paymentReceiveAutoIncrement,
}) => {
const { values, setFieldValue } = useFormikContext();
// Handle click open payment receive number dialog.
const handleClickOpenDialog = () => {
openDialog('payment-receive-number-form');
};
// Handle payment number field blur.
const handlePaymentNoBlur = (event) => {
const newValue = event.target.value;
// Show the confirmation dialog if the value has changed and auto-increment
// mode is enabled.
if (
values.payment_receive_no !== newValue &&
paymentReceiveAutoIncrement
) {
openDialog('payment-receive-number-form', {
initialFormValues: {
onceManualNumber: newValue,
incrementMode: 'manual-transaction',
},
});
}
// Setting the payment number to the form will be manually in case
// auto-increment is disable.
if (!paymentReceiveAutoIncrement) {
setFieldValue('payment_receive_no', newValue);
setFieldValue('payment_receive_no_manually', newValue);
}
};
return (
<FFormGroup
name={'payment_receive_no'}
label={<T id={'payment_receive_no'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
>
<ControlGroup fill={true}>
<FInputGroup
name={'payment_receive_no'}
minimal={true}
value={values.payment_receive_no}
asyncControl={true}
onBlur={handlePaymentNoBlur}
onChange={() => {}}
/>
<InputPrependButton
buttonProps={{
onClick: handleClickOpenDialog,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T id={'setting_your_auto_generated_payment_receive_number'} />
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FFormGroup>
);
},
);
/**
* Payment receive header fields.
*/
function PaymentReceiveHeaderFields({
// #withCurrentOrganization
organization: { base_currency },
// #withDialogActions
openDialog,
// #withSettings
paymentReceiveAutoIncrement,
paymentReceiveNumberPrefix,
paymentReceiveNextNumber,
}) {
export default function PaymentReceiveHeaderFields() {
// Payment receive form context.
const { customers, accounts, projects, isNewMode } =
usePaymentReceiveFormContext();
@@ -104,30 +172,6 @@ function PaymentReceiveHeaderFields({
const newEntries = amountPaymentEntries(toSafeInteger(value), entries);
setFieldValue('entries', newEntries);
};
// Handle click open payment receive number dialog.
const handleClickOpenDialog = () => {
openDialog('payment-receive-number-form');
};
// Handle payment number field blur.
const handlePaymentNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && paymentReceiveAutoIncrement) {
openDialog('payment-receive-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs payment receive number from settings to the form.
useObservePaymentNoSettings(
paymentReceiveNumberPrefix,
paymentReceiveNextNumber,
);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
@@ -246,43 +290,7 @@ function PaymentReceiveHeaderFields({
</Field>
{/* ------------ Payment receive no. ------------ */}
<FastField name={'payment_receive_no'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'payment_receive_no'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
className={('form-group--payment_receive_no', CLASSES.FILL)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="payment_receive_no" />}
>
<ControlGroup fill={true}>
<InputGroup
intent={inputIntent({ error, touched })}
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handlePaymentNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleClickOpenDialog,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T
id={'setting_your_auto_generated_payment_receive_number'}
/>
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</FastField>
<PaymentReceivePaymentNoField />
{/* ------------ Deposit account ------------ */}
<FFormGroup
@@ -349,16 +357,6 @@ function PaymentReceiveHeaderFields({
);
}
export default compose(
withSettings(({ paymentReceiveSettings }) => ({
paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber,
paymentReceiveNumberPrefix: paymentReceiveSettings?.numberPrefix,
paymentReceiveAutoIncrement: paymentReceiveSettings?.autoIncrement,
})),
withDialogActions,
withCurrentOrganization(),
)(PaymentReceiveHeaderFields);
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -1,14 +1,17 @@
// @ts-nocheck
import React from 'react';
import React, { useEffect, useLayoutEffect } from 'react';
import moment from 'moment';
import intl from 'react-intl-universal';
import { Button } from '@blueprintjs/core';
import { useFormikContext } from 'formik';
import * as R from 'ramda';
import { Money, ExchangeRateInputGroup, MoneyFieldCell } from '@/components';
import { useCurrentOrganization } from '@/hooks/state';
import { useEstimateIsForeignCustomer } from './utils';
import { transactionNumber } from '@/utils';
import withSettings from '@/containers/Settings/withSettings';
/**
* Invoice date cell.
@@ -109,6 +112,41 @@ export function PaymentReceiveExchangeRateInputField({ ...props }) {
* payment receive project select.
* @returns {JSX.Element}
*/
export function PaymentReceiveProjectSelectButton({ label }) {
export function PaymentReceiveProjectSelectButton({ label }) {
return <Button text={label ?? intl.get('select_project')} />;
}
/**
* Syncs the auto-increment settings to payment receive form.
* @returns {React.ReactNode}
*/
export const PaymentReceiveSyncIncrementSettingsToForm = R.compose(
withSettings(({ paymentReceiveSettings }) => ({
paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber,
paymentReceiveNumberPrefix: paymentReceiveSettings?.numberPrefix,
paymentReceiveAutoIncrement: paymentReceiveSettings?.autoIncrement,
})),
)(
({
paymentReceiveNextNumber,
paymentReceiveNumberPrefix,
paymentReceiveAutoIncrement,
}) => {
const { setFieldValue } = useFormikContext();
useLayoutEffect(() => {
if (!paymentReceiveAutoIncrement) return;
setFieldValue(
'payment_receive_no',
transactionNumber(paymentReceiveNumberPrefix, paymentReceiveNextNumber),
);
}, [
setFieldValue,
paymentReceiveNumberPrefix,
paymentReceiveNextNumber,
paymentReceiveAutoIncrement,
]);
return null;
},
);

View File

@@ -17,7 +17,6 @@ import {
} from '@/utils';
import { useCurrentOrganization } from '@/hooks/state';
// Default payment receive entry.
export const defaultPaymentReceiveEntry = {
index: '',
@@ -37,8 +36,10 @@ export const defaultPaymentReceive = {
payment_date: moment(new Date()).format('YYYY-MM-DD'),
reference_no: '',
payment_receive_no: '',
// Holds the payment number that entered manually only.
payment_receive_no_manually: '',
statement: '',
full_amount: '',
full_amount: '',
currency_code: '',
branch_id: '',
exchange_rate: 1,
@@ -123,18 +124,6 @@ export const fullAmountPaymentEntries = (entries) => {
}));
};
/**
* Syncs payment receive number settings with form.
*/
export const useObservePaymentNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const invoiceNo = transactionNumber(prefix, nextNumber);
setFieldValue('payment_receive_no', invoiceNo);
}, [setFieldValue, prefix, nextNumber]);
};
/**
* Detarmines the customers fast-field should update.
*/
@@ -168,6 +157,8 @@ export const transformFormToRequest = (form) => {
return {
...omit(form, ['payment_receive_no_manually', 'payment_receive_no']),
// The `payment_receive_no_manually` will be presented just if the auto-increment
// is disable, always both attributes hold the same value in manual mode.
...(form.payment_receive_no_manually && {
payment_receive_no: form.payment_receive_no,
}),
@@ -264,3 +255,13 @@ export const useEstimateIsForeignCustomer = () => {
);
return isForeignCustomer;
};
export const resetFormState = ({ initialValues, values, resetForm }) => {
resetForm({
values: {
// Reset the all values except the brand id.
...initialValues,
brand_id: values.brand_id,
},
});
};

View File

@@ -33,7 +33,9 @@ import {
defaultReceipt,
handleErrors,
transformFormValuesToRequest,
resetFormState,
} from './utils';
import { ReceiptSyncIncrementSettingsToForm } from './components';
/**
* Receipt form.
@@ -121,7 +123,7 @@ function ReceiptForm({
history.push('/receipts');
}
if (submitPayload.resetForm) {
resetForm();
resetFormState();
}
};
@@ -165,7 +167,11 @@ function ReceiptForm({
<ReceiptFormFooter />
<ReceiptFormFloatingActions />
{/*---------- Dialogs ---------*/}
<ReceiptFormDialogs />
{/*---------- Effects ---------*/}
<ReceiptSyncIncrementSettingsToForm />
</Form>
</Formik>
</div>

View File

@@ -10,9 +10,15 @@ export default function ReceiptFormDialogs() {
const { setFieldValue } = useFormikContext();
// Update the form once the receipt number form submit confirm.
const handleReceiptNumberFormConfirm = ({ incrementNumber, manually }) => {
setFieldValue('receipt_number', incrementNumber || '');
setFieldValue('receipt_number_manually', manually);
const handleReceiptNumberFormConfirm = (settings) => {
// Set the receipt transaction no. that cames from dialog to the form.
// the `receipt_no_manually` will be empty except the increment mode is not auto.
setFieldValue('receipt_number', settings.transactionNumber);
setFieldValue('receipt_number_manually', '');
if (settings.incrementMode !== 'auto') {
setFieldValue('receipt_number_manually', settings.transactionNumber);
}
};
return (

View File

@@ -17,6 +17,21 @@ function ReceiptFormHeader({
// #ownProps
onReceiptNumberChanged,
}) {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<ReceiptFormHeaderFields
onReceiptNumberChanged={onReceiptNumberChanged}
/>
<ReceiptFormHeaderBigTotal />
</div>
);
}
/**
* The big total amount of receipt form.
* @returns {React.ReactNode}
*/
function ReceiptFormHeaderBigTotal() {
const {
values: { currency_code, entries },
} = useFormikContext();
@@ -25,16 +40,11 @@ function ReceiptFormHeader({
const totalDueAmount = useMemo(() => getEntriesTotal(entries), [entries]);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<ReceiptFormHeaderFields
onReceiptNumberChanged={onReceiptNumberChanged}
/>
<PageFormBigNumber
label={intl.get('due_amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
</div>
<PageFormBigNumber
label={intl.get('due_amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
);
}

View File

@@ -10,9 +10,12 @@ import {
ControlGroup,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { CLASSES } from '@/constants/classes';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import * as R from 'ramda';
import { CLASSES } from '@/constants/classes';
import { ACCOUNT_TYPE } from '@/constants/accountTypes';
import { Features } from '@/constants';
import {
FFormGroup,
AccountsSelect,
@@ -23,64 +26,112 @@ import {
CustomerDrawerLink,
FormattedMessage as T,
FeatureCan,
FInputGroup,
} from '@/components';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { ACCOUNT_TYPE } from '@/constants/accountTypes';
import { ProjectsSelect } from '@/containers/Projects/components';
import {
momentFormatter,
compose,
tansformDateValue,
handleDateChange,
inputIntent,
} from '@/utils';
import { useReceiptFormContext } from './ReceiptFormProvider';
import {
accountsFieldShouldUpdate,
customersFieldShouldUpdate,
useObserveReceiptNoSettings,
} from './utils';
import { accountsFieldShouldUpdate, customersFieldShouldUpdate } from './utils';
import {
ReceiptExchangeRateInputField,
ReceiptProjectSelectButton,
} from './components';
import { Features } from '@/constants';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions';
/**
* Receipt number field of receipt form.
*/
const ReceiptFormReceiptNumberField = R.compose(
withDialogActions,
withSettings(({ receiptSettings }) => ({
receiptAutoIncrement: receiptSettings?.autoIncrement,
})),
)(
({
// #withDialogActions
openDialog,
// #withSettings
receiptAutoIncrement,
}) => {
const { values, setFieldValue } = useFormikContext();
const handleReceiptNumberChange = () => {
openDialog('receipt-number-form', {});
};
const handleReceiptNoBlur = (event) => {
const newValue = event.target.value;
// Show the confirmation dialog if the value has changed and auto-increment
// mode is enabled.
if (values.receipt_number !== newValue && receiptAutoIncrement) {
openDialog('receipt-number-form', {
initialFormValues: {
onceManualNumber: newValue,
incrementMode: 'manual-transaction',
},
});
}
// Setting the receipt number to the form will be manually in case
// auto-increment is disable.
if (!receiptAutoIncrement) {
setFieldValue('receipt_number', newValue);
setFieldValue('receipt_number_manually', newValue);
}
};
return (
<FFormGroup
name={'receipt_number'}
label={<T id={'receipt'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
>
<ControlGroup fill={true}>
<FInputGroup
name={'receipt_number'}
minimal={true}
value={values.receipt_number}
asyncControl={true}
onBlur={handleReceiptNoBlur}
onChange={() => {}}
/>
<InputPrependButton
buttonProps={{
onClick: handleReceiptNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T id={'setting_your_auto_generated_payment_receive_number'} />
),
position: Position.BOTTOM_LEFT,
}}
inputProps={{
leftIcon: <Icon icon={'date-range'} />,
}}
/>
</ControlGroup>
</FFormGroup>
);
},
);
/**
* Receipt form header fields.
*/
function ReceiptFormHeader({
//#withDialogActions
openDialog,
// #withSettings
receiptAutoIncrement,
receiptNextNumber,
receiptNumberPrefix,
}) {
export default function ReceiptFormHeader() {
const { accounts, customers, projects } = useReceiptFormContext();
const handleReceiptNumberChange = useCallback(() => {
openDialog('receipt-number-form', {});
}, [openDialog]);
const handleReceiptNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && receiptAutoIncrement) {
openDialog('receipt-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Synsc receipt number settings with the form.
useObserveReceiptNoSettings(receiptNumberPrefix, receiptNextNumber);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
@@ -176,45 +227,7 @@ function ReceiptFormHeader({
</FastField>
{/* ----------- Receipt number ----------- */}
<FastField name={'receipt_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'receipt'} />}
inline={true}
className={('form-group--receipt_number', CLASSES.FILL)}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="receipt_number" />}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleReceiptNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleReceiptNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T
id={'setting_your_auto_generated_payment_receive_number'}
/>
),
position: Position.BOTTOM_LEFT,
}}
inputProps={{
leftIcon: <Icon icon={'date-range'} />,
}}
/>
</ControlGroup>
</FormGroup>
)}
</FastField>
<ReceiptFormReceiptNumberField />
{/* ----------- Reference ----------- */}
<FastField name={'reference_no'}>
@@ -251,15 +264,6 @@ function ReceiptFormHeader({
);
}
export default compose(
withDialogActions,
withSettings(({ receiptSettings }) => ({
receiptAutoIncrement: receiptSettings?.autoIncrement,
receiptNextNumber: receiptSettings?.nextNumber,
receiptNumberPrefix: receiptSettings?.numberPrefix,
})),
)(ReceiptFormHeader);
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -3,9 +3,14 @@ import React from 'react';
import intl from 'react-intl-universal';
import { Button } from '@blueprintjs/core';
import { useFormikContext } from 'formik';
import * as R from 'ramda';
import { ExchangeRateInputGroup } from '@/components';
import { useCurrentOrganization } from '@/hooks/state';
import { useReceiptIsForeignCustomer } from './utils';
import { useUpdateEffect } from '@/hooks';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
/**
* Receipt exchange rate input field.
@@ -37,3 +42,34 @@ export function ReceiptExchangeRateInputField({ ...props }) {
export function ReceiptProjectSelectButton({ label }) {
return <Button text={label ?? intl.get('select_project')} />;
}
/**
* Syncs receipt auto-increment settings to form.
* @return {React.ReactNode}
*/
export const ReceiptSyncIncrementSettingsToForm = R.compose(
withSettings(({ receiptSettings }) => ({
receiptAutoIncrement: receiptSettings?.autoIncrement,
receiptNextNumber: receiptSettings?.nextNumber,
receiptNumberPrefix: receiptSettings?.numberPrefix,
})),
)(({ receiptAutoIncrement, receiptNextNumber, receiptNumberPrefix }) => {
const { setFieldValue } = useFormikContext();
useUpdateEffect(() => {
// Do not update if the receipt auto-increment mode is disabled.
if (!receiptAutoIncrement) return;
setFieldValue(
'receipt_number',
transactionNumber(receiptNumberPrefix, receiptNextNumber),
);
}, [
setFieldValue,
receiptNumberPrefix,
receiptAutoIncrement,
receiptNextNumber,
]);
return null;
});

View File

@@ -36,6 +36,8 @@ export const defaultReceipt = {
customer_id: '',
deposit_account_id: '',
receipt_number: '',
// Holds the receipt number that entered manually only.
receipt_number_manually: '',
receipt_date: moment(new Date()).format('YYYY-MM-DD'),
reference_no: '',
receipt_message: '',
@@ -77,15 +79,6 @@ export const transformToEditForm = (receipt) => {
};
};
export const useObserveReceiptNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const receiptNo = transactionNumber(prefix, nextNumber);
setFieldValue('receipt_number', receiptNo);
}, [setFieldValue, prefix, nextNumber]);
};
/**
* Detarmines entries fast field should update.
*/
@@ -248,3 +241,14 @@ export const useReceiptIsForeignCustomer = () => {
);
return isForeignCustomer;
};
export const resetFormState = ({ initialValues, values, resetForm }) => {
resetForm({
values: {
// Reset the all values except the warehouse and brand id.
...initialValues,
warehouse_id: values.warehouse_id,
brand_id: values.brand_id,
},
});
};

View File

@@ -37,6 +37,9 @@ const commonInvalidateQueries = (queryClient) => {
// Invalidate item warehouses.
queryClient.invalidateQueries(t.ITEM_WAREHOUSES_LOCATION);
// Invalidate mutate base currency abilities.
queryClient.invalidateQueries(t.ORGANIZATION_MUTATE_BASE_CURRENCY_ABILITIES);
};
/**

View File

@@ -22,6 +22,9 @@ const commonInvalidateQueries = (queryClient) => {
// Invalidate financial reports.
queryClient.invalidateQueries(t.FINANCIAL_REPORT);
queryClient.invalidateQueries(t.CASH_FLOW_TRANSACTION);
// Invalidate mutate base currency abilities.
queryClient.invalidateQueries(t.ORGANIZATION_MUTATE_BASE_CURRENCY_ABILITIES);
};
/**

View File

@@ -43,6 +43,9 @@ const commonInvalidateQueries = (queryClient) => {
// Invalidate financial reports.
queryClient.invalidateQueries(t.FINANCIAL_REPORT);
// Invalidate mutate base currency abilities.
queryClient.invalidateQueries(t.ORGANIZATION_MUTATE_BASE_CURRENCY_ABILITIES);
};
/**

View File

@@ -27,6 +27,9 @@ const commonInvalidateQueries = (queryClient) => {
queryClient.invalidateQueries(t.SALE_INVOICE_SMS_DETAIL);
queryClient.invalidateQueries(t.SALE_RECEIPT_SMS_DETAIL);
queryClient.invalidateQueries(t.PAYMENT_RECEIVE_SMS_DETAIL);
// Invalidate mutate base currency abilities.
queryClient.invalidateQueries(t.ORGANIZATION_MUTATE_BASE_CURRENCY_ABILITIES);
};
// Customers response selector.

View File

@@ -30,6 +30,9 @@ const commonInvalidateQueries = (queryClient) => {
// Invalidate landed cost.
queryClient.invalidateQueries(t.LANDED_COST);
queryClient.invalidateQueries(t.LANDED_COST_TRANSACTION);
// Invalidate mutate base currency abilities.
queryClient.invalidateQueries(t.ORGANIZATION_MUTATE_BASE_CURRENCY_ABILITIES);
};
const transformExpenses = (response) => ({

View File

@@ -20,6 +20,9 @@ const commonInvalidateQueries = (queryClient) => {
// Invalidate financial reports.
queryClient.invalidateQueries(t.FINANCIAL_REPORT);
// Invalidate mutate base currency abilities.
queryClient.invalidateQueries(t.ORGANIZATION_MUTATE_BASE_CURRENCY_ABILITIES);
};
/**

View File

@@ -38,6 +38,9 @@ const commonInvalidateQueries = (queryClient) => {
// Invalidate item warehouses.
queryClient.invalidateQueries(t.ITEM_WAREHOUSES_LOCATION);
// Invalidate mutate base currency abilities.
queryClient.invalidateQueries(t.ORGANIZATION_MUTATE_BASE_CURRENCY_ABILITIES);
};
/**

View File

@@ -21,6 +21,9 @@ const commonInvalidateQueries = (client) => {
client.invalidateQueries(t.ACCOUNTS);
client.invalidateQueries(t.ACCOUNT);
// Invalidate settings.
client.invalidateQueries([t.SETTING, t.SETTING_MANUAL_JOURNALS]);
// Invalidate financial reports.
client.invalidateQueries(t.FINANCIAL_REPORT);

View File

@@ -33,6 +33,9 @@ const commonInvalidateQueries = (queryClient) => {
// Invalidate the settings.
queryClient.invalidateQueries([t.SETTING, t.SETTING_RECEIPTS]);
// Invalidate mutate base currency abilities.
queryClient.invalidateQueries(t.ORGANIZATION_MUTATE_BASE_CURRENCY_ABILITIES);
};
/**

Some files were not shown because too many files have changed in this diff Show More