Compare commits

...

28 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
a.bouhuolia
05c2232b97 chore(webapp): refactor the setup organization form to use Formik binded component 2023-05-12 12:31:55 +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
a.bouhuolia
40bddfdfeb fix(webapp): accrual typo 2023-05-11 21:07:49 +02:00
a.bouhuolia
d6e2f01d70 fix(server): accrual typo 2023-05-11 21:07:01 +02:00
a.bouhuolia
2344d3d34d fix(webapp): general, accoutant and items preferences 2023-05-11 01:47:09 +02:00
132 changed files with 1873 additions and 1965 deletions

View File

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

View File

@@ -2,6 +2,24 @@
All notable changes to Bigcapital server-side will be in this file. 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 ## [0.8.3] - 06-04-2023
`@bigcaptial/monorepo` `@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. - [Bug Tracker](https://github.com/bigcapitalhq/bigcapital/issues) - Notify us new bugs.
- [Source Code](https://github.com/bigcapitalhq/bigcapital) - Github repo. - [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. 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 - ./data/logs/nginx/:/var/log/nginx
- ./docker/certbot/certs/:/var/certs - ./docker/certbot/certs/:/var/certs
ports: ports:
- "80:80" - "${PUBLIC_PROXY_PORT:-80}:80"
- "443:443" - "${PUBLIC_PROXY_SSL_PORT:-443}:443"
tty: true tty: true
depends_on: depends_on:
- server - server
- webapp - webapp
webapp: webapp:
container_name: bigcapital-webapp container_name: bigcapital-webapp
image: ghcr.io/bigcapitalhq/webapp:latest image: ghcr.io/bigcapitalhq/webapp:latest

View File

@@ -41,7 +41,7 @@ export default class BalanceSheetStatementController extends BaseFinancialReport
get balanceSheetValidationSchema(): ValidationChain[] { get balanceSheetValidationSchema(): ValidationChain[] {
return [ return [
...this.sheetNumberFormatValidationSchema, ...this.sheetNumberFormatValidationSchema,
query('accounting_method').optional().isIn(['cash', 'accural']), query('accounting_method').optional().isIn(['cash', 'accrual']),
query('from_date').optional(), query('from_date').optional(),
query('to_date').optional(), query('to_date').optional(),

View File

@@ -58,7 +58,7 @@ export default class OrganizationController extends BaseController {
private get organizationValidationSchema(): ValidationChain[] { private get organizationValidationSchema(): ValidationChain[] {
return [ return [
check('name').exists().trim(), check('name').exists().trim(),
check('industry').optional().isString(), check('industry').optional({ nullable: true }).isString().trim().escape(),
check('location').exists().isString().isISO31661Alpha2(), check('location').exists().isString().isISO31661Alpha2(),
check('base_currency').exists().isISO4217(), check('base_currency').exists().isISO4217(),
check('timezone').exists().isIn(moment.tz.names()), check('timezone').exists().isIn(moment.tz.names()),

View File

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

View File

@@ -8,7 +8,7 @@ export default class SeedSettings extends TenantSeeder {
up() { up() {
const settings = [ const settings = [
// Orgnization settings. // Orgnization settings.
{ group: 'organization', key: 'accounting_basis', value: 'accural' }, { group: 'organization', key: 'accounting_basis', value: 'accrual' },
// Accounts settings. // Accounts settings.
{ group: 'accounts', key: 'account_code_unique', value: true }, { group: 'accounts', key: 'account_code_unique', value: true },

View File

@@ -44,7 +44,7 @@ export interface IBalanceSheetQuery extends IFinancialSheetBranchesQuery {
numberFormat: INumberFormatQuery; numberFormat: INumberFormatQuery;
noneTransactions: boolean; noneTransactions: boolean;
noneZero: boolean; noneZero: boolean;
basis: 'cash' | 'accural'; basis: 'cash' | 'accrual';
accountIds: number[]; accountIds: number[];
percentageOfColumn: boolean; percentageOfColumn: boolean;

View File

@@ -4,7 +4,7 @@ export interface ITrialBalanceSheetQuery {
fromDate: Date | string; fromDate: Date | string;
toDate: Date | string; toDate: Date | string;
numberFormat: INumberFormatQuery; numberFormat: INumberFormatQuery;
basis: 'cash' | 'accural'; basis: 'cash' | 'accrual';
noneZero: boolean; noneZero: boolean;
noneTransactions: boolean; noneTransactions: boolean;
onlyActive: boolean; onlyActive: boolean;

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ export const getDefaultPLQuery = (): IProfitLossSheetQuery => ({
formatMoney: 'total', formatMoney: 'total',
precision: 2, precision: 2,
}, },
basis: 'accural', basis: 'accrual',
noneZero: false, noneZero: false,
noneTransactions: false, noneTransactions: false,

View File

@@ -35,7 +35,7 @@ export default class TrialBalanceSheetService extends FinancialSheet {
formatMoney: 'total', formatMoney: 'total',
precision: 2, precision: 2,
}, },
basis: 'accural', basis: 'accrual',
noneZero: false, noneZero: false,
noneTransactions: true, noneTransactions: true,
onlyActive: false, onlyActive: false,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,10 +5,10 @@ USER root
WORKDIR /app WORKDIR /app
# Install dependencies # Install dependencies
COPY package.json ./ COPY package*.json ./
COPY lerna.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 install
RUN npm run bootstrap RUN npm run bootstrap

View File

@@ -1205,9 +1205,9 @@
} }
}, },
"@blueprintjs-formik/core": { "@blueprintjs-formik/core": {
"version": "0.2.1", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/@blueprintjs-formik/core/-/core-0.2.1.tgz", "resolved": "https://registry.npmjs.org/@blueprintjs-formik/core/-/core-0.3.3.tgz",
"integrity": "sha512-YGJe+QorDGbkWDSUg6x69LYGN62Kgvb92Iz/voqmszVRKj4KcoPvd/7coF8Jmu+ZQE6LcwM/9ccB2i63L99ITA==", "integrity": "sha512-ko7g54YSEcSq2K/GEpmiTG0foGLqe7DwgXGhkGxYEiHhLAUv8WvQmrFsm8e/KOW7n8mLGq0uaZVe2l8m3JTGGQ==",
"requires": { "requires": {
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"lodash.keyby": "^4.6.0", "lodash.keyby": "^4.6.0",
@@ -7298,6 +7298,11 @@
"locate-path": "^3.0.0" "locate-path": "^3.0.0"
} }
}, },
"flat": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="
},
"flat-cache": { "flat-cache": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",

View File

@@ -3,7 +3,7 @@
"version": "1.7.1", "version": "1.7.1",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@blueprintjs-formik/core": "^0.2.1", "@blueprintjs-formik/core": "^0.3.3",
"@blueprintjs-formik/datetime": "^0.3.4", "@blueprintjs-formik/datetime": "^0.3.4",
"@blueprintjs-formik/select": "^0.2.3", "@blueprintjs-formik/select": "^0.2.3",
"@blueprintjs/core": "^3.50.2", "@blueprintjs/core": "^3.50.2",
@@ -16,8 +16,6 @@
"@casl/react": "^2.3.0", "@casl/react": "^2.3.0",
"@craco/craco": "^5.9.0", "@craco/craco": "^5.9.0",
"@reduxjs/toolkit": "^1.2.5", "@reduxjs/toolkit": "^1.2.5",
"@sentry/react": "^6.13.2",
"@sentry/tracing": "^6.13.2",
"@testing-library/jest-dom": "^4.2.4", "@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0", "@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1", "@testing-library/user-event": "^7.2.1",
@@ -45,6 +43,7 @@
"deepdash": "^5.3.9", "deepdash": "^5.3.9",
"dependency-graph": "^0.11.0", "dependency-graph": "^0.11.0",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"flat": "^5.0.2",
"formik": "^2.2.5", "formik": "^2.2.5",
"http-proxy-middleware": "^1.0.0", "http-proxy-middleware": "^1.0.0",
"jest": "24.9.0", "jest": "24.9.0",

View File

@@ -20,24 +20,6 @@
--> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <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. Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build. 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" href="https://cdnjs.cloudflare.com/ajax/libs/flexboxgrid/6.3.1/flexboxgrid.min.css"
type="text/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> </body>
</html> </html>

View File

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

View File

@@ -46,7 +46,7 @@ const SelectButton = styled(Button)`
margin-right: 12px; margin-right: 12px;
border-radius: 1px; border-radius: 1px;
} }
&:not([class*='bp3-intent-']) { &:not([class*='bp3-intent-']):not(.bp3-disabled) {
&, &,
&:hover { &:hover {
background: #fff; background: #fff;

View File

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

View File

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

View File

@@ -8,6 +8,19 @@ import { PageFormBigNumber, FormattedMessage as T } from '@/components';
import MakeJournalEntriesHeaderFields from './MakeJournalEntriesHeaderFields'; import MakeJournalEntriesHeaderFields from './MakeJournalEntriesHeaderFields';
export default function MakeJournalEntriesHeader() { 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 { const {
values: { entries, currency_code }, values: { entries, currency_code },
} = useFormikContext(); } = useFormikContext();
@@ -17,14 +30,10 @@ export default function MakeJournalEntriesHeader() {
const total = Math.max(totalCredit, totalDebit); const total = Math.max(totalCredit, totalDebit);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}> <PageFormBigNumber
<MakeJournalEntriesHeaderFields /> label={<T id={'amount'} />}
amount={total}
<PageFormBigNumber currencyCode={currency_code}
label={<T id={'amount'} />} />
amount={total}
currencyCode={currency_code}
/>
</div>
); );
} }

View File

@@ -6,14 +6,14 @@ import {
Position, Position,
ControlGroup, ControlGroup,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { FastField, ErrorMessage } from 'formik'; import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import * as R from 'ramda';
import classNames from 'classnames'; import classNames from 'classnames';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { import {
momentFormatter, momentFormatter,
compose,
inputIntent, inputIntent,
handleDateChange, handleDateChange,
tansformDateValue, tansformDateValue,
@@ -25,54 +25,101 @@ import {
Icon, Icon,
InputPrependButton, InputPrependButton,
CurrencySelectList, CurrencySelectList,
FormattedMessage as T FormattedMessage as T,
FInputGroup,
FFormGroup,
} from '@/components'; } from '@/components';
import { useMakeJournalFormContext } from './MakeJournalProvider'; import { useMakeJournalFormContext } from './MakeJournalProvider';
import { JournalExchangeRateInputField } from './components'; import { JournalExchangeRateInputField } from './components';
import { currenciesFieldShouldUpdate } from './utils';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import withDialogActions from '@/containers/Dialog/withDialogActions';
import {
currenciesFieldShouldUpdate, /**
useObserveJournalNoSettings, * Journal number field of make journal form.
} from './utils'; */
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. * Make journal entries header.
*/ */
function MakeJournalEntriesHeader({ export default function MakeJournalEntriesHeader({}) {
// #ownProps
onJournalNumberChanged,
// #withDialog
openDialog,
// #withSettings
journalAutoIncrement,
journalNextNumber,
journalNumberPrefix,
}) {
const { currencies } = useMakeJournalFormContext(); 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 ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/*------------ Posting date -----------*/} {/*------------ Posting date -----------*/}
@@ -106,46 +153,7 @@ function MakeJournalEntriesHeader({
</FastField> </FastField>
{/*------------ Journal number -----------*/} {/*------------ Journal number -----------*/}
<FastField name={'journal_number'}> <MakeJournalTransactionNoField />
{({ 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>
{/*------------ Reference -----------*/} {/*------------ Reference -----------*/}
<FastField name={'reference'}> <FastField name={'reference'}>
@@ -219,12 +227,3 @@ function MakeJournalEntriesHeader({
</div> </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(); const { setFieldValue } = useFormikContext();
// Update the form once the journal number form submit confirm. // Update the form once the journal number form submit confirm.
const handleConfirm = ({ manually, incrementNumber }) => { const handleConfirm = (settings) => {
setFieldValue('journal_number', incrementNumber || ''); // Set the invoice transaction no. that cames from dialog to the form.
setFieldValue('journal_number_manually', manually); // 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 ( return (
<> <JournalNumberDialog
<JournalNumberDialog dialogName={'journal-number-form'}
dialogName={'journal-number-form'} onConfirm={handleConfirm}
onConfirm={handleConfirm} />
/>
</>
); );
} }

View File

@@ -1,9 +1,10 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { useEffect } from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Menu, MenuItem, Position, Button } from '@blueprintjs/core'; import { Menu, MenuItem, Position, Button } from '@blueprintjs/core';
import { Popover2 } from '@blueprintjs/popover2'; import { Popover2 } from '@blueprintjs/popover2';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import * as R from 'ramda';
import { import {
ExchangeRateInputGroup, ExchangeRateInputGroup,
@@ -24,6 +25,9 @@ import { CellType, Features, Align } from '@/constants';
import { useCurrentOrganization, useFeatureCan } from '@/hooks/state'; import { useCurrentOrganization, useFeatureCan } from '@/hooks/state';
import { useJournalIsForeign } from './utils'; import { useJournalIsForeign } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
import { useUpdateEffect } from '@/hooks';
/** /**
* Contact header cell. * 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 { Intent } from '@blueprintjs/core';
import { sumBy, setWith, toSafeInteger, get, first } from 'lodash'; import { sumBy, setWith, toSafeInteger, get, first } from 'lodash';
import { import {
transactionNumber,
updateTableCell, updateTableCell,
repeatValue, repeatValue,
transformToForm, transformToForm,
@@ -46,7 +45,7 @@ export const defaultEntry = {
export const defaultManualJournal = { export const defaultManualJournal = {
journal_number: '', journal_number: '',
journal_number_manually: false, journal_number_manually: '',
journal_type: 'Journal', journal_type: 'Journal',
date: moment(new Date()).format('YYYY-MM-DD'), date: moment(new Date()).format('YYYY-MM-DD'),
description: '', 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. * Detarmines entries fast field should update.
*/ */

View File

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

View File

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

View File

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

View File

@@ -16,14 +16,12 @@ import {
InputPrependText, InputPrependText,
MoneyInputGroup, MoneyInputGroup,
FieldRequiredHint, FieldRequiredHint,
Icon,
Col, Col,
Row, Row,
If, If,
FeatureCan, FeatureCan,
BranchSelect, BranchSelect,
BranchSelectButton, BranchSelectButton,
InputPrependButton,
ExchangeRateMutedField, ExchangeRateMutedField,
} from '@/components'; } from '@/components';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
@@ -35,65 +33,26 @@ import {
momentFormatter, momentFormatter,
tansformDateValue, tansformDateValue,
handleDateChange, handleDateChange,
compose,
} from '@/utils'; } from '@/utils';
import { useMoneyInDailogContext } from '../MoneyInDialogProvider'; import { useMoneyInDailogContext } from '../MoneyInDialogProvider';
import { import {
useObserveTransactionNoSettings,
useSetPrimaryBranchToForm, useSetPrimaryBranchToForm,
useForeignAccount, useForeignAccount,
BranchRowDivider, BranchRowDivider,
} from '../utils'; } from '../utils';
import withSettings from '@/containers/Settings/withSettings'; import { MoneyInOutTransactionNoField } from '../../_components';
import withDialogActions from '@/containers/Dialog/withDialogActions';
/** /**
* Other income form fields. * Other income form fields.
*/ */
function OtherIncomeFormFields({ export default function OtherIncomeFormFields() {
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
// Money in dialog context. // Money in dialog context.
const { accounts, account, branches } = useMoneyInDailogContext(); const { accounts, account, branches } = useMoneyInDailogContext();
const { values } = useFormikContext(); const { values } = useFormikContext();
const amountFieldRef = useAutofocus(); const amountFieldRef = useAutofocus();
const isForeigAccount = useForeignAccount(); 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. // Sets the primary branch to form.
useSetPrimaryBranchToForm(); useSetPrimaryBranchToForm();
@@ -149,42 +108,7 @@ function OtherIncomeFormFields({
</Col> </Col>
<Col xs={5}> <Col xs={5}>
{/*------------ Transaction number -----------*/} {/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}> <MoneyInOutTransactionNoField />
{({ 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>
</Col> </Col>
</Row> </Row>
{/*------------ amount -----------*/} {/*------------ amount -----------*/}
@@ -298,12 +222,3 @@ function OtherIncomeFormFields({
</React.Fragment> </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, InputPrependText,
MoneyInputGroup, MoneyInputGroup,
FieldRequiredHint, FieldRequiredHint,
Icon,
Col, Col,
Row, Row,
If, If,
InputPrependButton,
ExchangeRateMutedField, ExchangeRateMutedField,
BranchSelect, BranchSelect,
BranchSelectButton, BranchSelectButton,
@@ -35,31 +33,20 @@ import {
momentFormatter, momentFormatter,
tansformDateValue, tansformDateValue,
handleDateChange, handleDateChange,
compose,
} from '@/utils'; } from '@/utils';
import { useMoneyInDailogContext } from '../MoneyInDialogProvider'; import { useMoneyInDailogContext } from '../MoneyInDialogProvider';
import { import {
useObserveTransactionNoSettings,
useSetPrimaryBranchToForm, useSetPrimaryBranchToForm,
useForeignAccount, useForeignAccount,
BranchRowDivider, BranchRowDivider,
} from '../../MoneyInDialog/utils'; } from '../../MoneyInDialog/utils';
import withSettings from '@/containers/Settings/withSettings'; import { MoneyInOutTransactionNoField } from '../../_components';
import withDialogActions from '@/containers/Dialog/withDialogActions';
/** /**
/** /**
* Owner contribution form fields. * Owner contribution form fields.
*/ */
function OwnerContributionFormFields({ export default function OwnerContributionFormFields() {
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
// Money in dialog context. // Money in dialog context.
const { accounts, account, branches } = useMoneyInDailogContext(); const { accounts, account, branches } = useMoneyInDailogContext();
@@ -69,31 +56,6 @@ function OwnerContributionFormFields({
const isForeigAccount = useForeignAccount(); 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. // Sets the primary branch to form.
useSetPrimaryBranchToForm(); useSetPrimaryBranchToForm();
@@ -148,42 +110,7 @@ function OwnerContributionFormFields({
</Col> </Col>
<Col xs={5}> <Col xs={5}>
{/*------------ Transaction number -----------*/} {/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}> <MoneyInOutTransactionNoField />
{({ 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>
</Col> </Col>
</Row> </Row>
{/*------------ amount -----------*/} {/*------------ amount -----------*/}
@@ -294,12 +221,3 @@ function OwnerContributionFormFields({
</React.Fragment> </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, InputPrependText,
MoneyInputGroup, MoneyInputGroup,
FieldRequiredHint, FieldRequiredHint,
Icon,
Col, Col,
Row, Row,
If, If,
InputPrependButton,
ExchangeRateMutedField, ExchangeRateMutedField,
FeatureCan, FeatureCan,
BranchSelect, BranchSelect,
@@ -35,30 +33,20 @@ import {
momentFormatter, momentFormatter,
tansformDateValue, tansformDateValue,
handleDateChange, handleDateChange,
compose,
} from '@/utils'; } from '@/utils';
import { useMoneyInDailogContext } from '../MoneyInDialogProvider'; import { useMoneyInDailogContext } from '../MoneyInDialogProvider';
import { import {
useObserveTransactionNoSettings,
useSetPrimaryBranchToForm, useSetPrimaryBranchToForm,
useForeignAccount, useForeignAccount,
BranchRowDivider, BranchRowDivider,
} from '../../MoneyInDialog/utils'; } from '../../MoneyInDialog/utils';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import { MoneyInOutTransactionNoField } from '../../_components';
/** /**
* Transfer from account form fields. * Transfer from account form fields.
*/ */
function TransferFromAccountFormFields({ export default function TransferFromAccountFormFields() {
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
// Money in dialog context. // Money in dialog context.
const { accounts, account, branches } = useMoneyInDailogContext(); const { accounts, account, branches } = useMoneyInDailogContext();
@@ -67,33 +55,9 @@ function TransferFromAccountFormFields({
const { values } = useFormikContext(); 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. // Sets the primary branch to form.
useSetPrimaryBranchToForm(); useSetPrimaryBranchToForm();
// Syncs transaction number settings with form.
useObserveTransactionNoSettings(
transactionNumberPrefix,
transactionNextNumber,
);
return ( return (
<React.Fragment> <React.Fragment>
<FeatureCan feature={Features.Branches}> <FeatureCan feature={Features.Branches}>
@@ -145,42 +109,7 @@ function TransferFromAccountFormFields({
</Col> </Col>
<Col xs={5}> <Col xs={5}>
{/*------------ Transaction number -----------*/} {/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}> <MoneyInOutTransactionNoField />
{({ 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>
</Col> </Col>
</Row> </Row>
{/*------------ amount -----------*/} {/*------------ amount -----------*/}
@@ -296,12 +225,3 @@ function TransferFromAccountFormFields({
</React.Fragment> </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; break;
} }
}; };
return <React.Fragment>{handleTransactionType()}</React.Fragment>; return <React.Fragment>{handleTransactionType()}</React.Fragment>;
} }

View File

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

View File

@@ -16,11 +16,9 @@ import {
InputPrependText, InputPrependText,
MoneyInputGroup, MoneyInputGroup,
FieldRequiredHint, FieldRequiredHint,
Icon,
Col, Col,
Row, Row,
If, If,
InputPrependButton,
FeatureCan, FeatureCan,
BranchSelect, BranchSelect,
BranchSelectButton, BranchSelectButton,
@@ -35,31 +33,21 @@ import {
momentFormatter, momentFormatter,
tansformDateValue, tansformDateValue,
handleDateChange, handleDateChange,
compose,
} from '@/utils'; } from '@/utils';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { useMoneyOutDialogContext } from '../MoneyOutDialogProvider'; import { useMoneyOutDialogContext } from '../MoneyOutDialogProvider';
import { import {
useObserveTransactionNoSettings,
useSetPrimaryBranchToForm, useSetPrimaryBranchToForm,
useForeignAccount, useForeignAccount,
BranchRowDivider, BranchRowDivider,
} from '../utils'; } from '../utils';
import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import { MoneyInOutTransactionNoField } from '../../_components';
/** /**
* Other expense form fields. * Other expense form fields.
*/ */
function OtherExpnseFormFields({ export default function OtherExpnseFormFields() {
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
// Money in dialog context. // Money in dialog context.
const { accounts, account, branches } = useMoneyOutDialogContext(); const { accounts, account, branches } = useMoneyOutDialogContext();
@@ -68,31 +56,6 @@ function OtherExpnseFormFields({
const amountFieldRef = useAutofocus(); 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. // Sets the primary branch to form.
useSetPrimaryBranchToForm(); useSetPrimaryBranchToForm();
@@ -147,42 +110,7 @@ function OtherExpnseFormFields({
</Col> </Col>
<Col xs={5}> <Col xs={5}>
{/*------------ Transaction number -----------*/} {/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}> <MoneyInOutTransactionNoField />
{({ 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>
</Col> </Col>
</Row> </Row>
{/*------------ amount -----------*/} {/*------------ amount -----------*/}
@@ -296,12 +224,3 @@ function OtherExpnseFormFields({
</React.Fragment> </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, InputPrependText,
MoneyInputGroup, MoneyInputGroup,
FieldRequiredHint, FieldRequiredHint,
InputPrependButton,
Icon,
If, If,
Col, Col,
Row, Row,
@@ -34,30 +32,19 @@ import {
momentFormatter, momentFormatter,
tansformDateValue, tansformDateValue,
handleDateChange, handleDateChange,
compose,
} from '@/utils'; } from '@/utils';
import { useMoneyOutDialogContext } from '../MoneyOutDialogProvider'; import { useMoneyOutDialogContext } from '../MoneyOutDialogProvider';
import { import {
useObserveTransactionNoSettings,
useSetPrimaryBranchToForm, useSetPrimaryBranchToForm,
useForeignAccount, useForeignAccount,
BranchRowDivider, BranchRowDivider,
} from '../../MoneyOutDialog/utils'; } from '../../MoneyOutDialog/utils';
import withSettings from '@/containers/Settings/withSettings'; import { MoneyInOutTransactionNoField } from '../../_components';
import withDialogActions from '@/containers/Dialog/withDialogActions';
/** /**
* Owner drawings form fields. * Owner drawings form fields.
*/ */
function OwnerDrawingsFormFields({ export default function OwnerDrawingsFormFields() {
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
// Money out dialog context. // Money out dialog context.
const { accounts, account, branches } = useMoneyOutDialogContext(); const { accounts, account, branches } = useMoneyOutDialogContext();
const { values } = useFormikContext(); const { values } = useFormikContext();
@@ -65,31 +52,6 @@ function OwnerDrawingsFormFields({
const amountFieldRef = useAutofocus(); 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. // Sets the primary branch to form.
useSetPrimaryBranchToForm(); useSetPrimaryBranchToForm();
@@ -144,42 +106,7 @@ function OwnerDrawingsFormFields({
</Col> </Col>
<Col xs={5}> <Col xs={5}>
{/*------------ Transaction number -----------*/} {/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}> <MoneyInOutTransactionNoField />
{({ 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>
</Col> </Col>
</Row> </Row>
{/*------------ amount -----------*/} {/*------------ amount -----------*/}
@@ -291,12 +218,3 @@ function OwnerDrawingsFormFields({
</React.Fragment> </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'; } from '../utils';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import withDialogActions from '@/containers/Dialog/withDialogActions';
import { MoneyInOutTransactionNoField } from '../../_components';
/** /**
* Transfer to account form fields. * Transfer to account form fields.
*/ */
function TransferToAccountFormFields({ export default function TransferToAccountFormFields() {
// #withDialogActions
openDialog,
// #withSettings
transactionAutoIncrement,
transactionNumberPrefix,
transactionNextNumber,
}) {
// Money in dialog context. // Money in dialog context.
const { accounts, account, branches } = useMoneyOutDialogContext(); const { accounts, account, branches } = useMoneyOutDialogContext();
const { values } = useFormikContext(); const { values } = useFormikContext();
@@ -68,31 +61,6 @@ function TransferToAccountFormFields({
const accountRef = useAutofocus(); 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. // Sets the primary branch to form.
useSetPrimaryBranchToForm(); useSetPrimaryBranchToForm();
@@ -147,42 +115,7 @@ function TransferToAccountFormFields({
</Col> </Col>
<Col xs={5}> <Col xs={5}>
{/*------------ Transaction number -----------*/} {/*------------ Transaction number -----------*/}
<Field name={'transaction_number'}> <MoneyInOutTransactionNoField />
{({ 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>
</Col> </Col>
</Row> </Row>
{/*------------ amount -----------*/} {/*------------ amount -----------*/}
@@ -298,11 +231,3 @@ function TransferToAccountFormFields({
</React.Fragment> </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) => ({ export const mapDispatchToProps = (dispatch) => ({
openDialog: (name, payload) => dispatch({ type: t.OPEN_DIALOG, name, payload }), openDialog: (name, payload) =>
closeDialog: (name, payload) => dispatch({ type: t.CLOSE_DIALOG, 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) => { const handleChange = (values) => {
setReferenceFormValues(values); setReferenceFormValues(values);
}; };
// Description. // Description.
const description = const description =
referenceFormValues?.incrementMode === 'auto' referenceFormValues?.incrementMode === 'auto'

View File

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

View File

@@ -30,7 +30,7 @@ export const getDefaultGeneralLedgerQuery = () => {
return { return {
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'accural', basis: 'accrual',
filterByOption: 'with-transactions', filterByOption: 'with-transactions',
branchesIds: [], branchesIds: [],
accountsIds: [], accountsIds: [],

View File

@@ -13,7 +13,7 @@ export const getDefaultJournalQuery = () => {
return { return {
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'accural', basis: 'accrual',
}; };
}; };

View File

@@ -27,7 +27,7 @@ export default function RadiosAccountingBasis(props) {
{...rest} {...rest}
> >
<Radio label={intl.get('cash')} value="cash" /> <Radio label={intl.get('cash')} value="cash" />
<Radio label={intl.get('accrual')} value="accural" /> <Radio label={intl.get('accrual')} value="accrual" />
</RadioGroup> </RadioGroup>
)} )}
</FastField> </FastField>

View File

@@ -13,7 +13,7 @@ export function getDefaultTrialBalanceQuery() {
return { return {
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'accural', basis: 'accrual',
filterByOption: 'with-transactions', filterByOption: 'with-transactions',
branchesIds: [], branchesIds: [],
}; };

View File

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

View File

@@ -1,17 +1,15 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { FastField, useFormikContext } from 'formik'; import { FastField, useFormikContext } from 'formik';
import { FormattedMessage as T } from '@/components';
import { FormGroup, InputGroup, Radio } from '@blueprintjs/core'; 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'; import { inputIntent } from '@/utils';
/** /**
* Reference number form content. * Reference number form content.
*/ */
export default function ReferenceNumberFormContent() { export default function ReferenceNumberFormContent() {
const { values } = useFormikContext();
return ( return (
<> <>
{/* ------------- Auto increment mode ------------- */} {/* ------------- Auto increment mode ------------- */}
@@ -27,54 +25,13 @@ export default function ReferenceNumberFormContent() {
/> />
)} )}
</FastField> </FastField>
<ReferenceNumberAutoIncrement />
<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>
{/* ------------- Manual increment mode ------------- */} {/* ------------- Manual increment mode ------------- */}
<FastField name={'incrementMode'}> <FastField name={'incrementMode'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<Radio <Radio
label={<T id={'auto_increment.field.manual_this_transaction'} />} label={<T id={'auto_increment.field.manually'} />}
value="manual" value="manual"
onChange={() => { onChange={() => {
form.setFieldValue('incrementMode', 'manual'); form.setFieldValue('incrementMode', 'manual');
@@ -85,20 +42,70 @@ export default function ReferenceNumberFormContent() {
</FastField> </FastField>
{/* ------------- Transaction manual increment mode ------------- */} {/* ------------- Transaction manual increment mode ------------- */}
<If condition={values.manualTransactionNo}> <ReferenceNumberManualOnce />
<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>
</> </>
); );
} }
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, transfromToSnakeCase,
transactionNumber, transactionNumber,
} from '@/utils'; } from '@/utils';
import { omit } from 'lodash';
export const defaultInvoiceNoSettings = { export const defaultInvoiceNoSettings = {
nextNumber: '', nextNumber: '',
@@ -13,8 +14,8 @@ export const defaultInvoiceNoSettings = {
}; };
export const transformSettingsToForm = (settings) => ({ export const transformSettingsToForm = (settings) => ({
...settings, ...omit(settings, ['autoIncrement']),
incrementMode: settings.autoIncrement === 'true' ? 'auto' : 'manual', incrementMode: settings.autoIncrement ? 'auto' : 'manual',
}); });
export const transformFormToSettings = (values, group) => { export const transformFormToSettings = (values, group) => {
@@ -25,13 +26,21 @@ export const transformFormToSettings = (values, group) => {
return optionsMapToArray(options).map((option) => ({ ...option, 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) => { export const transformValuesToForm = (values) => {
const incrementNumber = const autoIncrementNumber = transactionNumber(
values.numberPrefix,
values.nextNumber,
);
const _transactionNumber =
values.incrementMode === 'auto' values.incrementMode === 'auto'
? transactionNumber(values.numberPrefix, values.nextNumber) ? autoIncrementNumber
: values.manualTransactionNo; : values.incrementMode === 'manual-transaction'
? values.onceManualNumber
const manually = values.incrementMode === 'auto' ? false : true; : '';
return { transactionNumber: _transactionNumber };
return { incrementNumber, manually };
}; };

View File

@@ -2,12 +2,18 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
const Schema = Yup.object().shape({ const Schema = Yup.object().shape({
accounting_basis: Yup.string().required(), organization: Yup.object({
account_code_required: Yup.boolean().nullable(), accountingBasis: Yup.string().required(),
account_code_unique: Yup.boolean().nullable(), }),
withdrawal_account: Yup.number().nullable(), accounts: Yup.object({
preferred_deposit_account: Yup.number().nullable(), accountCodeRequired: Yup.boolean().nullable(),
preferred_advance_deposit: Yup.number().nullable(), accountCodeUnique: Yup.boolean().nullable(),
}),
paymentReceives: Yup.object({
preferredDepositAccount: Yup.number().nullable(),
preferredAdvanceDeposit: Yup.number().nullable(),
withdrawalAccount: Yup.number().nullable(),
})
}); });
export const AccountantSchema = Schema; export const AccountantSchema = Schema;

View File

@@ -1,16 +1,9 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Form, FastField, useFormikContext } from 'formik'; import { Form, useFormikContext } from 'formik';
import styled from 'styled-components'; import styled from 'styled-components';
import { import { FormGroup, Radio, Button, Intent } from '@blueprintjs/core';
FormGroup,
RadioGroup,
Radio,
Checkbox,
Button,
Intent,
} from '@blueprintjs/core';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { import {
@@ -19,8 +12,9 @@ import {
FieldRequiredHint, FieldRequiredHint,
CardFooterActions, CardFooterActions,
FFormGroup, FFormGroup,
FCheckbox,
FRadioGroup,
} from '@/components'; } from '@/components';
import { handleStringChange, inputIntent } from '@/utils';
import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from '@/constants/accountTypes'; import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from '@/constants/accountTypes';
import { useAccountantFormContext } from './AccountantFormProvider'; import { useAccountantFormContext } from './AccountantFormProvider';
@@ -30,7 +24,6 @@ import { useAccountantFormContext } from './AccountantFormProvider';
export default function AccountantForm() { export default function AccountantForm() {
const history = useHistory(); const history = useHistory();
const { accounts } = useAccountantFormContext(); const { accounts } = useAccountantFormContext();
const { isSubmitting } = useFormikContext(); const { isSubmitting } = useFormikContext();
const handleCloseClick = () => { const handleCloseClick = () => {
@@ -48,78 +41,54 @@ export default function AccountantForm() {
} }
className={'accounts-checkbox'} className={'accounts-checkbox'}
> >
{/*------------ Account code (required) -----------*/} {/*------------ Account Code (required) -----------*/}
<FastField name={'account_code_required'} type={'checkbox'}> <FFormGroup inline={true} name={'accounts.accountCodeRequired'}>
{({ field }) => ( <FCheckbox
<FormGroup inline={true}> inline={true}
<Checkbox label={
inline={true} <T id={'make_account_code_required_when_create_a_new_accounts'} />
label={ }
<T name={'accounts.accountCodeRequired'}
id={'make_account_code_required_when_create_a_new_accounts'} />
/> </FFormGroup>
}
name={'account_code_required'}
{...field}
/>
</FormGroup>
)}
</FastField>
{/*------------ Account code (unique) -----------*/} {/*------------ Account Code (unique) -----------*/}
<FastField name={'account_code_unique'} type={'checkbox'}> <FFormGroup
{({ field }) => ( name={'accounts.accountCodeUnique'}
<FormGroup inline={true}> type={'checkbox'}
<Checkbox inline={true}
inline={true} >
label={ <FCheckbox
<T inline={true}
id={ label={
'should_account_code_be_unique_when_create_a_new_account' <T
} id={'should_account_code_be_unique_when_create_a_new_account'}
/>
}
name={'account_code_unique'}
{...field}
/> />
</FormGroup> }
)} name={'accounts.accountCodeUnique'}
</FastField> />
</FFormGroup>
</FormGroup> </FormGroup>
{/* ----------- Accounting basis ----------- */} {/* ----------- Accounting Basis ----------- */}
<FastField name={'accounting_basis'}> <FormGroup
{({ name={'organization.accountingBasis'}
form: { setFieldValue }, labelInfo={<FieldRequiredHint />}
field: { value }, label={
meta: { error, touched }, <strong>
}) => ( <T id={'accounting_basis_'} />
<FormGroup </strong>
labelInfo={<FieldRequiredHint />} }
intent={inputIntent({ error, touched })} >
label={ <FRadioGroup name={'organization.accountingBasis'} inline={true}>
<strong> <Radio label={intl.get('cash')} value="cash" />
<T id={'accounting_basis_'} /> <Radio label={intl.get('accrual')} value="accrual" />
</strong> </FRadioGroup>
} </FormGroup>
>
<RadioGroup
inline={true}
selectedValue={value}
onChange={handleStringChange((_value) => {
setFieldValue('accounting_basis', _value);
})}
>
<Radio label={intl.get('cash')} value="cash" />
<Radio label={intl.get('accrual')} value="accrual" />
</RadioGroup>
</FormGroup>
)}
</FastField>
{/* ----------- Deposit customer account ----------- */} {/* ----------- Deposit Customer Account ----------- */}
<AccountantFormGroup <AccountantFormGroup
name={'preferred_deposit_account'} name={'paymentReceives.preferredDepositAccount'}
label={ label={
<strong> <strong>
<T id={'deposit_customer_account'} /> <T id={'deposit_customer_account'} />
@@ -136,7 +105,7 @@ export default function AccountantForm() {
fastField={true} fastField={true}
> >
<AccountsSelect <AccountsSelect
name={'preferred_deposit_account'} name={'paymentReceives.preferredDepositAccount'}
items={accounts} items={accounts}
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByTypes={[ filterByTypes={[
@@ -148,9 +117,9 @@ export default function AccountantForm() {
/> />
</AccountantFormGroup> </AccountantFormGroup>
{/* ----------- Withdrawal vendor account ----------- */} {/* ----------- Withdrawal Vendor Account ----------- */}
<AccountantFormGroup <AccountantFormGroup
name={'withdrawal_account'} name={'billPayments.withdrawalAccount'}
label={ label={
<strong> <strong>
<T id={'withdrawal_vendor_account'} /> <T id={'withdrawal_vendor_account'} />
@@ -167,7 +136,7 @@ export default function AccountantForm() {
fastField={true} fastField={true}
> >
<AccountsSelect <AccountsSelect
name={'withdrawal_account'} name={'billPayments.withdrawalAccount'}
items={accounts} items={accounts}
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByTypes={[ filterByTypes={[
@@ -179,9 +148,9 @@ export default function AccountantForm() {
/> />
</AccountantFormGroup> </AccountantFormGroup>
{/* ----------- Withdrawal customer account ----------- */} {/* ----------- Withdrawal Customer Account ----------- */}
<AccountantFormGroup <AccountantFormGroup
name={'preferred_advance_deposit'} name={'paymentReceives.preferredAdvanceDeposit'}
label={ label={
<strong> <strong>
<T id={'customer_advance_deposit'} /> <T id={'customer_advance_deposit'} />
@@ -198,7 +167,7 @@ export default function AccountantForm() {
fastField={true} fastField={true}
> >
<AccountsSelect <AccountsSelect
name={'preferred_advance_deposit'} name={'paymentReceives.preferredAdvanceDeposit'}
items={accounts} items={accounts}
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByParentTypes={[ACCOUNT_PARENT_TYPE.CURRENT_ASSET]} filterByParentTypes={[ACCOUNT_PARENT_TYPE.CURRENT_ASSET]}

View File

@@ -1,55 +1,66 @@
// @ts-nocheck // @ts-nocheck
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import * as R from 'ramda';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Formik } from 'formik'; import { Formik } from 'formik';
import { pick } from 'lodash';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { flatten, unflatten } from 'flat';
import { AppToaster } from '@/components'; import { AppToaster } from '@/components';
import withDashboardActions from '@/containers/Dashboard/withDashboardActions'; import withDashboardActions from '@/containers/Dashboard/withDashboardActions';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import AccountantForm from './AccountantForm'; import AccountantForm from './AccountantForm';
import { AccountantSchema } from './Accountant.schema'; import { AccountantSchema } from './Accountant.schema';
import { useAccountantFormContext } from './AccountantFormProvider'; import { useAccountantFormContext } from './AccountantFormProvider';
import { transformToOptions } from './utils'; import { transferObjectOptionsToArray } from './utils';
import { compose, transformGeneralSettings } from '@/utils'; import { compose, transformToForm, transfromToSnakeCase } from '@/utils';
import '@/style/pages/Preferences/Accounting.scss'; import '@/style/pages/Preferences/Accounting.scss';
const defaultFormValues = flatten({
organization: {
accountingBasis: 'accrual',
},
accounts: {
accountCodeRequired: false,
accountCodeUnique: false,
},
billPayments: {
withdrawalAccount: '',
},
paymentReceives: {
preferredDepositAccount: '',
preferredAdvanceDeposit: '',
},
});
// Accountant preferences. // Accountant preferences.
function AccountantFormPage({ function AccountantFormPage({
//# withDashboardActions //# withDashboardActions
changePreferencesPageTitle, changePreferencesPageTitle,
// #withSettings // #withSettings
organizationSettings, allSettings,
paymentReceiveSettings,
accountsSettings,
billPaymentSettings,
}) { }) {
const { saveSettingMutate } = useAccountantFormContext(); const { saveSettingMutate } = useAccountantFormContext();
const accountantSettings = {
...billPaymentSettings,
...accountsSettings,
...pick(organizationSettings, ['accountingBasis']),
...pick(paymentReceiveSettings, ['preferredDepositAccount', 'preferredAdvanceDeposit']),
};
const initialValues = {
...transformGeneralSettings(accountantSettings),
};
useEffect(() => { useEffect(() => {
changePreferencesPageTitle(intl.get('accountant')); changePreferencesPageTitle(intl.get('accountant'));
}, [changePreferencesPageTitle]); }, [changePreferencesPageTitle]);
const initialValues = unflatten({
...defaultFormValues,
...transformToForm(flatten(allSettings), defaultFormValues),
});
// Handle the form submitting.
const handleFormSubmit = (values, { setSubmitting }) => { const handleFormSubmit = (values, { setSubmitting }) => {
const options = transformToOptions(values); const options = R.compose(
transferObjectOptionsToArray,
transfromToSnakeCase,
)(values);
setSubmitting(true); setSubmitting(true);
const onSuccess = () => { const onSuccess = () => {
AppToaster.show({ AppToaster.show({
message: intl.get('the_accountant_preferences_has_been_saved'), message: intl.get('the_accountant_preferences_has_been_saved'),
@@ -57,8 +68,7 @@ function AccountantFormPage({
}); });
setSubmitting(false); setSubmitting(false);
}; };
const onError = () => {
const onError = (errors) => {
setSubmitting(false); setSubmitting(false);
}; };
saveSettingMutate({ options }).then(onSuccess).catch(onError); saveSettingMutate({ options }).then(onSuccess).catch(onError);
@@ -75,18 +85,8 @@ function AccountantFormPage({
} }
export default compose( export default compose(
withSettings( withSettings(({ allSettings }) => ({
({ allSettings,
organizationSettings, })),
paymentReceiveSettings,
accountsSettings,
billPaymentSettings,
}) => ({
organizationSettings,
paymentReceiveSettings,
accountsSettings,
billPaymentSettings,
}),
),
withDashboardActions, withDashboardActions,
)(AccountantFormPage); )(AccountantFormPage);

View File

@@ -17,7 +17,7 @@ function AccountantFormProvider({ ...props }) {
// Fetches the accounts list. // Fetches the accounts list.
const { isLoading: isAccountsLoading, data: accounts } = useAccounts(); const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
//Fetches Organization Settings. // Fetches Organization Settings.
const { isLoading: isSettingsLoading } = useSettings(); const { isLoading: isSettingsLoading } = useSettings();
// Save Organization Settings. // Save Organization Settings.
@@ -29,7 +29,7 @@ function AccountantFormProvider({ ...props }) {
isAccountsLoading, isAccountsLoading,
saveSettingMutate, saveSettingMutate,
}; };
// Detarmines whether if any query is loading.
const isLoading = isSettingsLoading || isAccountsLoading; const isLoading = isSettingsLoading || isAccountsLoading;
return ( return (

View File

@@ -1,38 +1,6 @@
// @ts-nocheck // @ts-nocheck
export const transformToOptions = (option) => {
return [
{
key: 'accounting_basis',
value: option.accounting_basis,
group: 'organization',
},
{
key: 'withdrawal_account',
value: option.withdrawal_account,
group: 'bill_payments',
},
{
key: 'preferred_deposit_account',
value: option.preferred_deposit_account,
group: 'payment_receives',
},
{
key: 'preferred_advance_deposit',
value: option.preferred_advance_deposit,
group: 'payment_receives',
},
{
key: 'account_code_required',
value: option.account_code_required,
group: 'accounts',
},
{
key: 'account_code_unique',
value: option.account_code_unique,
group: 'accounts',
},
];
};
export const transferObjectOptionsToArray = (input) =>
Object.entries(input).flatMap(([group, options]) =>
Object.entries(options).map(([key, value]) => ({ group, key, value })),
);

View File

@@ -10,6 +10,7 @@ import { ActionMenuList, useCurrenciesTableColumns } from './components';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import withDialogActions from '@/containers/Dialog/withDialogActions';
import withAlertActions from '@/containers/Alert/withAlertActions'; import withAlertActions from '@/containers/Alert/withAlertActions';
import styled from 'styled-components';
/** /**
* Currencies table. * Currencies table.
@@ -46,7 +47,7 @@ function CurrenciesDataTable({
}; };
return ( return (
<DataTable <CurrencieDataTable
columns={columns} columns={columns}
data={currencies} data={currencies}
loading={isCurrenciesLoading} loading={isCurrenciesLoading}
@@ -68,3 +69,11 @@ export default compose(
withDialogActions, withDialogActions,
withAlertActions, withAlertActions,
)(CurrenciesDataTable); )(CurrenciesDataTable);
const CurrencieDataTable = styled(DataTable)`
.table .th,
.table .td {
padding-top: 0.4rem;
padding-bottom: 0.4rem;
}
`;

View File

@@ -3,15 +3,17 @@ import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import classNames from 'classnames'; import classNames from 'classnames';
import { Form } from 'formik'; import { Form } from 'formik';
import { Button, FormGroup, InputGroup, Intent } from '@blueprintjs/core'; import { Button, FormGroup, Intent } from '@blueprintjs/core';
import { TimezonePicker } from '@blueprintjs/timezone'; import { TimezonePicker } from '@blueprintjs/timezone';
import { ErrorMessage, FastField } from 'formik'; import { ErrorMessage, FastField } from 'formik';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { import {
ListSelect,
FieldRequiredHint, FieldRequiredHint,
FormattedMessage as T, FormattedMessage as T,
FFormGroup,
FInputGroup,
FSelect,
} from '@/components'; } from '@/components';
import { inputIntent } from '@/utils'; import { inputIntent } from '@/utils';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
@@ -46,153 +48,114 @@ export default function PreferencesGeneralForm({ isSubmitting }) {
return ( return (
<Form> <Form>
{/* ---------- Organization name ---------- */} {/* ---------- Organization name ---------- */}
<FastField name={'name'}> <FFormGroup
{({ field, meta: { error, touched } }) => ( name={'name'}
<FormGroup label={<T id={'organization_name'} />}
label={<T id={'organization_name'} />} labelInfo={<FieldRequiredHint />}
labelInfo={<FieldRequiredHint />} inline={true}
inline={true} helperText={<T id={'shown_on_sales_forms_and_purchase_orders'} />}
intent={inputIntent({ error, touched })} fastField={true}
className={'form-group--org-name'} >
helperText={<T id={'shown_on_sales_forms_and_purchase_orders'} />} <FInputGroup medium={'true'} name={'name'} fastField={true} />
> </FFormGroup>
<InputGroup medium={'true'} {...field} />
</FormGroup>
)}
</FastField>
{/* ---------- Industry ---------- */} {/* ---------- Industry ---------- */}
<FastField name={'industry'}> <FFormGroup
{({ field, meta: { error, touched } }) => ( name={'industry'}
<FormGroup label={<T id={'organization_industry'} />}
label={<T id={'organization_industry'} />} inline={true}
inline={true} fastField={true}
intent={inputIntent({ error, touched })} >
helperText={<ErrorMessage name="industry" />} <FInputGroup name={'industry'} medium={'true'} fastField={true} />
className={'form-group--org-industry'} </FFormGroup>
>
<InputGroup medium={'true'} {...field} />
</FormGroup>
)}
</FastField>
{/* ---------- Location ---------- */} {/* ---------- Location ---------- */}
<FastField name={'location'}> <FFormGroup
{({ form, field: { value }, meta: { error, touched } }) => ( name={'location'}
<FormGroup label={<T id={'business_location'} />}
label={<T id={'business_location'} />} inline={true}
className={classNames( fastField={true}
'form-group--business-location', >
CLASSES.FILL, <FSelect
)} name={'location'}
inline={true} items={Countries}
helperText={<ErrorMessage name="location" />} valueAccessor={'countryCode'}
intent={inputIntent({ error, touched })} labelAccessor={'countryCode'}
> textAccessor={'name'}
<ListSelect placeholder={<T id={'select_business_location'} />}
items={Countries} popoverProps={{ minimal: true }}
onItemSelect={({ value }) => { fastField={true}
form.setFieldValue('location', value); />
}} </FFormGroup>
selectedItem={value}
selectedItemProp={'value'}
defaultText={<T id={'select_business_location'} />}
textProp={'name'}
popoverProps={{ minimal: true }}
/>
</FormGroup>
)}
</FastField>
{/* ---------- Base currency ---------- */} {/* ---------- Base currency ---------- */}
<FastField <FFormGroup
name={'base_currency'} name={'base_currency'}
baseCurrencyDisabled={baseCurrencyDisabled} baseCurrencyDisabled={baseCurrencyDisabled}
label={<T id={'base_currency'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
helperText={
<T
id={'you_can_t_change_the_base_currency_as_there_are_transactions'}
/>
}
fastField={true}
shouldUpdate={shouldBaseCurrencyUpdate} shouldUpdate={shouldBaseCurrencyUpdate}
> >
{({ form, field: { value }, meta: { error, touched } }) => ( <FSelect
<FormGroup name={'base_currency'}
label={<T id={'base_currency'} />} items={Currencies}
labelInfo={<FieldRequiredHint />} valueAccessor={'key'}
className={classNames('form-group--base-currency', CLASSES.FILL)} textAccessor={'name'}
inline={true} labelAccessor={'key'}
intent={inputIntent({ error, touched })} placeholder={<T id={'select_base_currency'} />}
helperText={ popoverProps={{ minimal: true }}
<T disabled={baseCurrencyDisabled}
id={ fastField={true}
'you_can_t_change_the_base_currency_as_there_are_transactions' shouldUpdate={shouldBaseCurrencyUpdate}
} baseCurrencyDisabled={baseCurrencyDisabled}
/> />
} </FFormGroup>
>
<ListSelect
items={Currencies}
onItemSelect={(currency) => {
form.setFieldValue('base_currency', currency.key);
}}
selectedItem={value}
selectedItemProp={'key'}
defaultText={<T id={'select_base_currency'} />}
textProp={'name'}
labelProp={'key'}
popoverProps={{ minimal: true }}
disabled={baseCurrencyDisabled}
/>
</FormGroup>
)}
</FastField>
{/* --------- Fiscal Year ----------- */} {/* --------- Fiscal Year ----------- */}
<FastField name={'fiscal_year'}> <FFormGroup
{({ form, field: { value }, meta: { error, touched } }) => ( name={'fiscal_year'}
<FormGroup label={<T id={'fiscal_year'} />}
label={<T id={'fiscal_year'} />} labelInfo={<FieldRequiredHint />}
labelInfo={<FieldRequiredHint />} inline={true}
className={classNames('form-group--fiscal-year', CLASSES.FILL)} helperText={<T id={'for_reporting_you_can_specify_any_month'} />}
inline={true} fastField={true}
intent={inputIntent({ error, touched })} >
helperText={<T id={'for_reporting_you_can_specify_any_month'} />} <FSelect
> name={'fiscal_year'}
<ListSelect items={FiscalYear}
items={FiscalYear} valueAccessor={'key'}
onItemSelect={(option) => { textAccessor={'name'}
form.setFieldValue('fiscal_year', option.key); placeholder={<T id={'select_fiscal_year'} />}
}} popoverProps={{ minimal: true }}
selectedItem={value} fastField={true}
selectedItemProp={'key'} />
defaultText={<T id={'select_fiscal_year'} />} </FFormGroup>
textProp={'name'}
popoverProps={{ minimal: true }}
/>
</FormGroup>
)}
</FastField>
{/* ---------- Language ---------- */} {/* ---------- Language ---------- */}
<FastField name={'language'}> <FormGroup
{({ form, field: { value }, meta: { error, touched } }) => ( name={'language'}
<FormGroup label={<T id={'language'} />}
label={<T id={'language'} />} labelInfo={<FieldRequiredHint />}
labelInfo={<FieldRequiredHint />} inline={true}
inline={true} fastField={true}
className={classNames('form-group--language', CLASSES.FILL)} >
intent={inputIntent({ error, touched })} <FSelect
helperText={<ErrorMessage name="language" />} name={'language'}
> items={Languages}
<ListSelect valueAccessor={'value'}
items={Languages} textAccessor={'name'}
selectedItemProp={'value'} placeholder={<T id={'select_language'} />}
textProp={'name'} popoverProps={{ minimal: true }}
defaultText={<T id={'select_language'} />} fastField={true}
selectedItem={value} />
onItemSelect={(item) => </FormGroup>
form.setFieldValue('language', item.value)
}
popoverProps={{ minimal: true }}
/>
</FormGroup>
)}
</FastField>
{/* ---------- Time zone ---------- */} {/* ---------- Time zone ---------- */}
<FastField name={'timezone'}> <FastField name={'timezone'}>
@@ -222,30 +185,24 @@ export default function PreferencesGeneralForm({ isSubmitting }) {
</FastField> </FastField>
{/* --------- Data format ----------- */} {/* --------- Data format ----------- */}
<FastField name={'date_format'}> <FFormGroup
{({ form, field: { value }, meta: { error, touched } }) => ( name={'date_format'}
<FormGroup label={<T id={'date_format'} />}
label={<T id={'date_format'} />} labelInfo={<FieldRequiredHint />}
labelInfo={<FieldRequiredHint />} inline={true}
inline={true} helperText={<ErrorMessage name="date_format" />}
className={classNames('form-group--date-format', CLASSES.FILL)} fastField={true}
intent={inputIntent({ error, touched })} >
helperText={<ErrorMessage name="date_format" />} <FSelect
> name={'date_format'}
<ListSelect items={dateFormats}
items={dateFormats} valueAccessor={'key'}
onItemSelect={(dateFormat) => { textAccessor={'label'}
form.setFieldValue('date_format', dateFormat.key); placeholder={<T id={'select_date_format'} />}
}} popoverProps={{ minimal: true }}
selectedItem={value} fastField={true}
selectedItemProp={'key'} />
defaultText={<T id={'select_date_format'} />} </FFormGroup>
textProp={'label'}
popoverProps={{ minimal: true }}
/>
</FormGroup>
)}
</FastField>
<CardFooterActions> <CardFooterActions>
<Button loading={isSubmitting} intent={Intent.PRIMARY} type="submit"> <Button loading={isSubmitting} intent={Intent.PRIMARY} type="submit">

View File

@@ -40,6 +40,7 @@ function GeneralFormPage({
// Initial values. // Initial values.
const initialValues = { const initialValues = {
...defaultValues,
...transformToForm(organization.metadata, defaultValues), ...transformToForm(organization.metadata, defaultValues),
}; };
// Handle the form submit. // Handle the form submit.

View File

@@ -23,6 +23,7 @@ function GeneralFormProvider({ ...props }) {
const { isLoading: isOrganizationLoading, data: organization } = const { isLoading: isOrganizationLoading, data: organization } =
useCurrentOrganization(); useCurrentOrganization();
// Fetch date format options.
const { data: dateFormats, isLoading: isDateFormatsLoading } = const { data: dateFormats, isLoading: isDateFormatsLoading } =
useDateFormats(); useDateFormats();

View File

@@ -1,8 +1,9 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { Form, useFormikContext } from 'formik'; import { Form, useFormikContext } from 'formik';
import { FormGroup, Button, Intent } from '@blueprintjs/core'; import { Button, Intent } from '@blueprintjs/core';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import styled from 'styled-components';
import { import {
AccountsSelect, AccountsSelect,
FieldRequiredHint, FieldRequiredHint,
@@ -30,7 +31,7 @@ export default function ItemForm() {
return ( return (
<Form> <Form>
{/* ----------- Preferred Sell Account ----------- */} {/* ----------- Preferred Sell Account ----------- */}
<FormGroup <ItemFormGroup
name={'preferred_sell_account'} name={'preferred_sell_account'}
label={ label={
<strong> <strong>
@@ -53,10 +54,10 @@ export default function ItemForm() {
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]} filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]}
/> />
</FormGroup> </ItemFormGroup>
{/* ----------- Preferred Cost Account ----------- */} {/* ----------- Preferred Cost Account ----------- */}
<FFormGroup <ItemFormGroup
name={'preferred_cost_account'} name={'preferred_cost_account'}
label={ label={
<strong> <strong>
@@ -79,10 +80,10 @@ export default function ItemForm() {
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByParentTypes={[ACCOUNT_PARENT_TYPE.EXPENSE]} filterByParentTypes={[ACCOUNT_PARENT_TYPE.EXPENSE]}
/> />
</FFormGroup> </ItemFormGroup>
{/* ----------- Preferred Inventory Account ----------- */} {/* ----------- Preferred Inventory Account ----------- */}
<FFormGroup <ItemFormGroup
name={'preferred_inventory_account'} name={'preferred_inventory_account'}
label={ label={
<strong> <strong>
@@ -105,7 +106,7 @@ export default function ItemForm() {
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByTypes={[ACCOUNT_TYPE.INVENTORY]} filterByTypes={[ACCOUNT_TYPE.INVENTORY]}
/> />
</FFormGroup> </ItemFormGroup>
<CardFooterActions> <CardFooterActions>
<Button intent={Intent.PRIMARY} loading={isSubmitting} type="submit"> <Button intent={Intent.PRIMARY} loading={isSubmitting} type="submit">
@@ -118,3 +119,7 @@ export default function ItemForm() {
</Form> </Form>
); );
} }
const ItemFormGroup = styled(FFormGroup)`
max-width: 400px;
`;

View File

@@ -11,10 +11,21 @@ import ItemPreferencesForm from './ItemPreferencesForm';
import { useItemPreferencesFormContext } from './ItemPreferencesFormProvider'; import { useItemPreferencesFormContext } from './ItemPreferencesFormProvider';
import withDashboardActions from '@/containers/Dashboard/withDashboardActions'; import withDashboardActions from '@/containers/Dashboard/withDashboardActions';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import { compose, optionsMapToArray, transformGeneralSettings } from '@/utils'; import {
compose,
optionsMapToArray,
transformGeneralSettings,
transformToForm,
} from '@/utils';
import '@/style/pages/Preferences/Accounting.scss'; import '@/style/pages/Preferences/Accounting.scss';
const defaultFormValues = {
preferred_sell_account: '',
preferred_cost_account: '',
preferred_inventory_account: '',
};
// item form page preferences. // item form page preferences.
function ItemPreferencesFormPage({ function ItemPreferencesFormPage({
// #withSettings // #withSettings
@@ -25,16 +36,13 @@ function ItemPreferencesFormPage({
}) { }) {
const { saveSettingMutate } = useItemPreferencesFormContext(); const { saveSettingMutate } = useItemPreferencesFormContext();
const itemPerferencesSettings = {
...omit(itemsSettings, ['tableSize']),
};
// Initial values. // Initial values.
const initialValues = { const initialValues = {
preferred_sell_account: '', ...defaultFormValues,
preferred_cost_account: '', ...transformToForm(
preferred_inventory_account: '', transformGeneralSettings(itemsSettings),
...transformGeneralSettings(itemPerferencesSettings), defaultFormValues,
),
}; };
useEffect(() => { useEffect(() => {

View File

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

View File

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

View File

@@ -1,25 +1,30 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import CreditNoteNumberDialog from '@/containers/Dialogs/CreditNoteNumberDialog';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import CreditNoteNumberDialog from '@/containers/Dialogs/CreditNoteNumberDialog';
/** /**
* Credit note form dialogs. * Credit note form dialogs.
*/ */
export default function CreditNoteFormDialogs() { export default function CreditNoteFormDialogs() {
const { setFieldValue } = useFormikContext();
// Update the form once the credit number form submit confirm. // Update the form once the credit number form submit confirm.
const handleCreditNumberFormConfirm = ({ incrementNumber, manually }) => { const handleCreditNumberFormConfirm = (settings) => {
setFieldValue('credit_note_number', incrementNumber || ''); // Set the credit note transaction no. that cames from dialog to the form.
setFieldValue('credit_note_no_manually', manually); // 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 ( return (
<React.Fragment> <CreditNoteNumberDialog
<CreditNoteNumberDialog dialogName={'credit-number-form'}
dialogName={'credit-number-form'} onConfirm={handleCreditNumberFormConfirm}
onConfirm={handleCreditNumberFormConfirm} />
/>
</React.Fragment>
); );
} }

View File

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

View File

@@ -1,5 +1,6 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import * as R from 'ramda';
import classNames from 'classnames'; import classNames from 'classnames';
import styled from 'styled-components'; import styled from 'styled-components';
import { import {
@@ -9,7 +10,8 @@ import {
ControlGroup, ControlGroup,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FastField, Field, ErrorMessage } from 'formik'; import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { import {
CustomerSelectField, CustomerSelectField,
@@ -18,11 +20,10 @@ import {
Icon, Icon,
FormattedMessage as T, FormattedMessage as T,
CustomerDrawerLink, CustomerDrawerLink,
FFormGroup,
FInputGroup,
} from '@/components'; } from '@/components';
import { import { customerNameFieldShouldUpdate } from './utils';
customerNameFieldShouldUpdate,
useObserveCreditNoSettings,
} from './utils';
import { useCreditNoteFormContext } from './CreditNoteFormProvider'; import { useCreditNoteFormContext } from './CreditNoteFormProvider';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
@@ -30,49 +31,99 @@ import withDialogActions from '@/containers/Dialog/withDialogActions';
import { CreditNoteExchangeRateInputField } from './components'; import { CreditNoteExchangeRateInputField } from './components';
import { import {
momentFormatter, momentFormatter,
compose,
tansformDateValue, tansformDateValue,
inputIntent, inputIntent,
handleDateChange, handleDateChange,
} from '@/utils'; } 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. * Credit note form header fields.
*/ */
function CreditNoteFormHeaderFields({ export default function CreditNoteFormHeaderFields({}) {
// #withDialogActions
openDialog,
// #withSettings
creditAutoIncrement,
creditNumberPrefix,
creditNextNumber,
}) {
// Credit note form context. // Credit note form context.
const { customers } = useCreditNoteFormContext(); 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 ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}
@@ -119,7 +170,6 @@ function CreditNoteFormHeaderFields({
name={'exchange_rate'} name={'exchange_rate'}
formGroupProps={{ label: ' ', inline: true }} formGroupProps={{ label: ' ', inline: true }}
/> />
{/* ----------- Credit note date ----------- */} {/* ----------- Credit note date ----------- */}
<FastField name={'credit_note_date'}> <FastField name={'credit_note_date'}>
{({ form, field: { value }, meta: { error, touched } }) => ( {({ form, field: { value }, meta: { error, touched } }) => (
@@ -146,43 +196,8 @@ function CreditNoteFormHeaderFields({
)} )}
</FastField> </FastField>
{/* ----------- Credit note # ----------- */} {/* ----------- Credit note # ----------- */}
<Field name={'credit_note_number'}> <CreditNoteTransactionNoField />
{({ 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>
{/* ----------- Reference ----------- */} {/* ----------- Reference ----------- */}
<FastField name={'reference_no'}> <FastField name={'reference_no'}>
{({ field, meta: { error, touched } }) => ( {({ field, meta: { error, touched } }) => (
@@ -200,14 +215,6 @@ function CreditNoteFormHeaderFields({
</div> </div>
); );
} }
export default compose(
withDialogActions,
withSettings(({ creditNoteSettings }) => ({
creditAutoIncrement: creditNoteSettings?.autoIncrement,
creditNextNumber: creditNoteSettings?.nextNumber,
creditNumberPrefix: creditNoteSettings?.numberPrefix,
})),
)(CreditNoteFormHeaderFields);
const CustomerButtonLink = styled(CustomerDrawerLink)` const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px; font-size: 11px;

View File

@@ -1,16 +1,18 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { useEffect } from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import * as R from 'ramda';
import { ExchangeRateInputGroup } from '@/components'; import { ExchangeRateInputGroup } from '@/components';
import { useCurrentOrganization } from '@/hooks/state'; import { useCurrentOrganization } from '@/hooks/state';
import { useCreditNoteIsForeignCustomer } from './utils'; import { useCreditNoteIsForeignCustomer } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
/** /**
* credit exchange rate input field. * credit exchange rate input field.
* @returns {JSX.Element} * @returns {JSX.Element}
*/ */
export function CreditNoteExchangeRateInputField({ ...props }) { export function CreditNoteExchangeRateInputField({ ...props }) {
const currentOrganization = useCurrentOrganization(); const currentOrganization = useCurrentOrganization();
const { values } = useFormikContext(); const { values } = useFormikContext();
@@ -27,4 +29,30 @@ import { useCreditNoteIsForeignCustomer } from './utils';
{...props} {...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: '', customer_id: '',
credit_note_date: moment(new Date()).format('YYYY-MM-DD'), credit_note_date: moment(new Date()).format('YYYY-MM-DD'),
credit_note_number: '', credit_note_number: '',
credit_note_no_manually: false, // Holds the credit note number that entered manually only.
credit_note_number_manually: false,
open: '', open: '',
reference_no: '', reference_no: '',
note: '', 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 = () => { export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext(); const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = useCreditNoteFormContext(); const { branches, isBranchesSuccess } = useCreditNoteFormContext();

View File

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

View File

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

View File

@@ -12,6 +12,19 @@ import { PageFormBigNumber } from '@/components';
// Estimate form top header. // Estimate form top header.
function EstimateFormHeader() { 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 { const {
values: { entries, currency_code }, values: { entries, currency_code },
} = useFormikContext(); } = useFormikContext();
@@ -20,15 +33,11 @@ function EstimateFormHeader() {
const totalDueAmount = useMemo(() => getEntriesTotal(entries), [entries]); const totalDueAmount = useMemo(() => getEntriesTotal(entries), [entries]);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}> <PageFormBigNumber
<EstimateFormHeaderFields /> label={intl.get('amount')}
amount={totalDueAmount}
<PageFormBigNumber currencyCode={currency_code}
label={intl.get('amount')} />
amount={totalDueAmount}
currencyCode={currency_code}
/>
</div>
); );
} }

View File

@@ -1,5 +1,6 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import * as R from 'ramda';
import styled from 'styled-components'; import styled from 'styled-components';
import classNames from 'classnames'; import classNames from 'classnames';
import { import {
@@ -10,12 +11,21 @@ import {
ControlGroup, ControlGroup,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FeatureCan, FFormGroup, FormattedMessage as T } from '@/components'; import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { FastField, Field, ErrorMessage } from 'formik';
import {
FeatureCan,
FFormGroup,
FInputGroup,
FormattedMessage as T,
CustomerSelectField,
FieldRequiredHint,
Icon,
InputPrependButton,
CustomerDrawerLink,
} from '@/components';
import { import {
momentFormatter, momentFormatter,
compose,
tansformDateValue, tansformDateValue,
inputIntent, inputIntent,
handleDateChange, handleDateChange,
@@ -23,57 +33,99 @@ import {
import { customersFieldShouldUpdate } from './utils'; import { customersFieldShouldUpdate } from './utils';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { Features } from '@/constants'; import { Features } from '@/constants';
import {
CustomerSelectField,
FieldRequiredHint,
Icon,
InputPrependButton,
CustomerDrawerLink,
} from '@/components';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import withDialogActions from '@/containers/Dialog/withDialogActions';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import { ProjectsSelect } from '@/containers/Projects/components'; import { ProjectsSelect } from '@/containers/Projects/components';
import { import {
EstimateExchangeRateInputField, EstimateExchangeRateInputField,
EstimateProjectSelectButton, EstimateProjectSelectButton,
} from './components'; } from './components';
import { useObserveEstimateNoSettings } from './utils';
import { useEstimateFormContext } from './EstimateFormProvider'; 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. * Estimate form header.
*/ */
function EstimateFormHeader({ export default function EstimateFormHeader() {
// #withDialogActions
openDialog,
// #withSettings
estimateAutoIncrement,
estimateNumberPrefix,
estimateNextNumber,
}) {
const { customers, projects } = useEstimateFormContext(); 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 ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}
@@ -113,13 +165,12 @@ function EstimateFormHeader({
)} )}
</FastField> </FastField>
{/* ----------- Exchange rate ----------- */} {/* ----------- Exchange Rate ----------- */}
<EstimateExchangeRateInputField <EstimateExchangeRateInputField
name={'exchange_rate'} name={'exchange_rate'}
formGroupProps={{ label: ' ', inline: true }} formGroupProps={{ label: ' ', inline: true }}
/> />
{/* ----------- Estimate Date ----------- */}
{/* ----------- Estimate date ----------- */}
<FastField name={'estimate_date'}> <FastField name={'estimate_date'}>
{({ form, field: { value }, meta: { error, touched } }) => ( {({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup <FormGroup
@@ -176,40 +227,7 @@ function EstimateFormHeader({
</FastField> </FastField>
{/* ----------- Estimate number ----------- */} {/* ----------- Estimate number ----------- */}
<Field name={'estimate_number'}> <EstimateFormEstimateNumberField />
{({ 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>
{/* ----------- Reference ----------- */} {/* ----------- Reference ----------- */}
<FastField name={'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)` const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px; font-size: 11px;
margin-top: 6px; margin-top: 6px;

View File

@@ -1,18 +1,21 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { useEffect } from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Button } from '@blueprintjs/core'; import { Button } from '@blueprintjs/core';
import * as R from 'ramda';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { ExchangeRateInputGroup } from '@/components'; import { ExchangeRateInputGroup } from '@/components';
import { useCurrentOrganization } from '@/hooks/state'; import { useCurrentOrganization } from '@/hooks/state';
import { useEstimateIsForeignCustomer } from './utils'; import { useEstimateIsForeignCustomer } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
import { useUpdateEffect } from '@/hooks';
/** /**
* Estimate exchange rate input field. * Estimate exchange rate input field.
* @returns {JSX.Element} * @returns {JSX.Element}
*/ */
export function EstimateExchangeRateInputField({ ...props }) { export function EstimateExchangeRateInputField({ ...props }) {
const currentOrganization = useCurrentOrganization(); const currentOrganization = useCurrentOrganization();
const { values } = useFormikContext(); const { values } = useFormikContext();
@@ -35,6 +38,37 @@ import { useEstimateIsForeignCustomer } from './utils';
* Estimate project select. * Estimate project select.
* @returns {JSX.Element} * @returns {JSX.Element}
*/ */
export function EstimateProjectSelectButton({ label }) { export function EstimateProjectSelectButton({ label }) {
return <Button text={label ?? intl.get('select_project')} />; 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 { omit, first } from 'lodash';
import { import {
defaultFastFieldShouldUpdate, defaultFastFieldShouldUpdate,
transactionNumber,
repeatValue, repeatValue,
transformToForm, transformToForm,
formattedAmount, formattedAmount,
@@ -37,6 +36,8 @@ export const defaultEstimate = {
estimate_date: moment(new Date()).format('YYYY-MM-DD'), estimate_date: moment(new Date()).format('YYYY-MM-DD'),
expiration_date: moment(new Date()).format('YYYY-MM-DD'), expiration_date: moment(new Date()).format('YYYY-MM-DD'),
estimate_number: '', estimate_number: '',
// Holds the estimate number that entered manually only.
estimate_number_manually: '',
delivered: '', delivered: '',
reference: '', reference: '',
note: '', 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. * Detarmines customers fast field when update.
*/ */
@@ -154,6 +143,8 @@ export const transfromsFormValuesToRequest = (values) => {
); );
return { return {
...omit(values, ['estimate_number_manually', 'estimate_number']), ...omit(values, ['estimate_number_manually', 'estimate_number']),
// The `estimate_number_manually` will be presented just if the auto-increment
// is disable, always both attributes hold the same value in manual mode.
...(values.estimate_number_manually && { ...(values.estimate_number_manually && {
estimate_number: values.estimate_number, estimate_number: values.estimate_number,
}), }),
@@ -235,3 +226,17 @@ export const useEstimateIsForeignCustomer = () => {
); );
return isForeignCustomer; 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, defaultInvoice,
transformErrors, transformErrors,
transformValueToRequest, transformValueToRequest,
resetFormState,
} from './utils'; } from './utils';
import { InvoiceNoSyncSettingsToForm } from './components';
/** /**
* Invoice form. * Invoice form.
@@ -40,7 +42,7 @@ function InvoiceForm({
// #withSettings // #withSettings
invoiceNextNumber, invoiceNextNumber,
invoiceNumberPrefix, invoiceNumberPrefix,
invoiceIncrementMode, invoiceAutoIncrementMode,
// #withCurrentOrganization // #withCurrentOrganization
organization: { base_currency }, organization: { base_currency },
@@ -64,23 +66,21 @@ function InvoiceForm({
invoiceNextNumber, invoiceNextNumber,
); );
// Form initial values. // Form initial values.
const initialValues = useMemo( const initialValues = {
() => ({ ...(!isEmpty(invoice)
...(!isEmpty(invoice) ? { ...transformToEditForm(invoice) }
? { ...transformToEditForm(invoice) } : {
: { ...defaultInvoice,
...defaultInvoice, // If the auto-increment mode is enabled, take the next invoice
...(invoiceIncrementMode && { // number from the settings.
invoice_no: invoiceNumber, ...(invoiceAutoIncrementMode && {
}), invoice_no: invoiceNumber,
entries: orderingLinesIndexes(defaultInvoice.entries),
currency_code: base_currency,
...newInvoice,
}), }),
}), entries: orderingLinesIndexes(defaultInvoice.entries),
[invoice, newInvoice, invoiceNumber, invoiceIncrementMode, base_currency], currency_code: base_currency,
); ...newInvoice,
}),
};
// Handles form submit. // Handles form submit.
const handleSubmit = (values, { setSubmitting, setErrors, resetForm }) => { const handleSubmit = (values, { setSubmitting, setErrors, resetForm }) => {
setSubmitting(true); setSubmitting(true);
@@ -105,7 +105,6 @@ function InvoiceForm({
delivered: submitPayload.deliver, delivered: submitPayload.deliver,
from_estimate_id: estimateId, from_estimate_id: estimateId,
}; };
// Handle the request success. // Handle the request success.
const onSuccess = () => { const onSuccess = () => {
AppToaster.show({ AppToaster.show({
@@ -123,10 +122,9 @@ function InvoiceForm({
history.push('/invoices'); history.push('/invoices');
} }
if (submitPayload.resetForm) { if (submitPayload.resetForm) {
resetForm(); resetFormState({ resetForm, initialValues, values });
} }
}; };
// Handle the request error. // Handle the request error.
const onError = ({ const onError = ({
response: { response: {
@@ -144,7 +142,6 @@ function InvoiceForm({
createInvoiceMutate(form).then(onSuccess).catch(onError); createInvoiceMutate(form).then(onSuccess).catch(onError);
} }
}; };
// Create invoice form schema. // Create invoice form schema.
const CreateInvoiceFormSchema = getCreateInvoiceFormSchema(); const CreateInvoiceFormSchema = getCreateInvoiceFormSchema();
@@ -172,7 +169,12 @@ function InvoiceForm({
<InvoiceItemsEntriesEditorField /> <InvoiceItemsEntriesEditorField />
<InvoiceFormFooter /> <InvoiceFormFooter />
<InvoiceFloatingActions /> <InvoiceFloatingActions />
{/*---------- Dialogs ----------*/}
<InvoiceFormDialogs /> <InvoiceFormDialogs />
{/*---------- Effects ----------*/}
<InvoiceNoSyncSettingsToForm />
</Form> </Form>
</Formik> </Formik>
</div> </div>
@@ -184,7 +186,7 @@ export default compose(
withSettings(({ invoiceSettings }) => ({ withSettings(({ invoiceSettings }) => ({
invoiceNextNumber: invoiceSettings?.nextNumber, invoiceNextNumber: invoiceSettings?.nextNumber,
invoiceNumberPrefix: invoiceSettings?.numberPrefix, invoiceNumberPrefix: invoiceSettings?.numberPrefix,
invoiceIncrementMode: invoiceSettings?.autoIncrement, invoiceAutoIncrementMode: invoiceSettings?.autoIncrement,
})), })),
withCurrentOrganization(), withCurrentOrganization(),
)(InvoiceForm); )(InvoiceForm);

View File

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

View File

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

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