mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
Compare commits
27 Commits
docker-com
...
v0.9.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b6d0bc016 | ||
|
|
e6336b1451 | ||
|
|
46290c4d37 | ||
|
|
ff2b7563c8 | ||
|
|
b9572420ed | ||
|
|
35ebb9c2aa | ||
|
|
3ebeb29dc0 | ||
|
|
8e98068538 | ||
|
|
6a72594faf | ||
|
|
728729094a | ||
|
|
93d540fbd2 | ||
|
|
eb9b6ce717 | ||
|
|
f716d42d26 | ||
|
|
1c4c364f06 | ||
|
|
162ad91547 | ||
|
|
2950e5ede4 | ||
|
|
73b041d8d2 | ||
|
|
7bf008a9cb | ||
|
|
4d9e3ccfb4 | ||
|
|
1bfe19f26c | ||
|
|
a371cedb67 | ||
|
|
4ed9c36ebd | ||
|
|
e24b23ce7e | ||
|
|
19fe6e2423 | ||
|
|
aec09f178b | ||
|
|
ffe51bae07 | ||
|
|
68231d5edb |
30
.env.example
30
.env.example
@@ -8,26 +8,32 @@ MAIL_FROM_NAME=
|
|||||||
MAIL_FROM_ADDRESS=
|
MAIL_FROM_ADDRESS=
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
DB_USER=
|
DB_HOST=mysql
|
||||||
DB_HOST=
|
DB_USER=bigcapital
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=bigcapital
|
||||||
DB_CHARSET=
|
DB_ROOT_PASSWORD=root
|
||||||
|
DB_CHARSET=utf8
|
||||||
|
|
||||||
# System database
|
# System database
|
||||||
SYSTEM_DB_NAME=bigcapital_system
|
SYSTEM_DB_NAME=bigcapital_system
|
||||||
|
# SYSTEM_DB_USER=
|
||||||
|
# SYSTEM_DB_PASSWORD=
|
||||||
|
# SYSTEM_DB_NAME=
|
||||||
|
# SYSTEM_DB_CHARSET=
|
||||||
|
|
||||||
# Tenants databases
|
# Tenant databases
|
||||||
TENANT_DB_NAME_PERFIX=bigcapital_tenant_
|
TENANT_DB_NAME_PERFIX=bigcapital_tenant_
|
||||||
|
# TENANT_DB_HOST=
|
||||||
# MongoDB
|
# TENANT_DB_USER=
|
||||||
MONGODB_DATABASE_URL=mongodb://localhost/bigcapital
|
# TENANT_DB_PASSWORD=
|
||||||
|
# TENANT_DB_CHARSET=
|
||||||
# Authentication
|
|
||||||
JWT_SECRET=b0JDZW56RnV6aEthb0RGPXVEcUI
|
|
||||||
|
|
||||||
# Application
|
# Application
|
||||||
BASE_URL=https://bigcapital.ly
|
BASE_URL=https://bigcapital.ly
|
||||||
CONTACT_US_MAIL=support@bigcapital.ly
|
JWT_SECRET=b0JDZW56RnV6aEthb0RGPXVEcUI
|
||||||
|
|
||||||
|
# Jobs MongoDB
|
||||||
|
MONGODB_DATABASE_URL=mongodb://localhost/bigcapital
|
||||||
|
|
||||||
# App proxy
|
# App proxy
|
||||||
PUBLIC_PROXY_PORT=80
|
PUBLIC_PROXY_PORT=80
|
||||||
|
|||||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
docker/nginx/scripts/build-nginx.sh text eol=lf
|
||||||
|
docker/mariadb/docker-entrypoint.sh text eol=lf
|
||||||
@@ -94,12 +94,12 @@ services:
|
|||||||
mysql:
|
mysql:
|
||||||
container_name: bigcapital-mysql
|
container_name: bigcapital-mysql
|
||||||
build:
|
build:
|
||||||
context: ./docker/mysql
|
context: ./docker/mariadb
|
||||||
environment:
|
environment:
|
||||||
- MYSQL_DATABASE=${SYSTEM_DB_NAME}
|
- MYSQL_DATABASE=${SYSTEM_DB_NAME}
|
||||||
- MYSQL_USER=${DB_USER}
|
- MYSQL_USER=${DB_USER}
|
||||||
- MYSQL_PASSWORD=${DB_PASSWORD}
|
- MYSQL_PASSWORD=${DB_PASSWORD}
|
||||||
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
|
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
|
||||||
volumes:
|
volumes:
|
||||||
- mysql:/var/lib/mysql
|
- mysql:/var/lib/mysql
|
||||||
expose:
|
expose:
|
||||||
|
|||||||
@@ -6,14 +6,14 @@
|
|||||||
version: '3.3'
|
version: '3.3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
mysql:
|
mariadb:
|
||||||
build:
|
build:
|
||||||
context: ./docker/mysql
|
context: ./docker/mariadb
|
||||||
environment:
|
environment:
|
||||||
- MYSQL_DATABASE=${SYSTEM_DB_NAME}
|
- MYSQL_DATABASE=${SYSTEM_DB_NAME}
|
||||||
- MYSQL_USER=${DB_USER}
|
- MYSQL_USER=${DB_USER}
|
||||||
- MYSQL_PASSWORD=${DB_PASSWORD}
|
- MYSQL_PASSWORD=${DB_PASSWORD}
|
||||||
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
|
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
|
||||||
volumes:
|
volumes:
|
||||||
- mysql:/var/lib/mysql
|
- mysql:/var/lib/mysql
|
||||||
expose:
|
expose:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM mysql:5.7
|
FROM mariadb:10.2
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
ADD my.cnf /etc/mysql/conf.d/my.cnf
|
ADD my.cnf /etc/mysql/conf.d/my.cnf
|
||||||
@@ -17,7 +17,7 @@ ENV MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD
|
|||||||
COPY ./init.sql /scripts/init.template.sql
|
COPY ./init.sql /scripts/init.template.sql
|
||||||
COPY ./docker-entrypoint.sh /docker-entrypoint-initdb.d/docker-initialize.sh
|
COPY ./docker-entrypoint.sh /docker-entrypoint-initdb.d/docker-initialize.sh
|
||||||
|
|
||||||
# The scripts in the docker-entrypoint-initdb.d/ directory are executed as
|
# The scripts in the `docker-entrypoint-initdb.d/` directory are executed as
|
||||||
# the mysql user inside the MySQL Docker container.
|
# the mysql user inside the MySQL Docker container.
|
||||||
RUN chown -R mysql:root /docker-entrypoint-initdb.d
|
RUN chown -R mysql:root /docker-entrypoint-initdb.d
|
||||||
RUN chown -R mysql:root /scripts
|
RUN chown -R mysql:root /scripts
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
GRANT ALL PRIVILEGES ON *.* TO '{MYSQL_USER}'@'%' IDENTIFIED BY '{MYSQL_PASSWORD}' WITH GRANT OPTION;
|
GRANT ALL PRIVILEGES ON *.* TO '{MYSQL_USER}'@'%' IDENTIFIED BY '{MYSQL_PASSWORD}' WITH GRANT OPTION;
|
||||||
|
|
||||||
FLUSH PRIVILEGES;
|
FLUSH PRIVILEGES;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"version": "0.0.0",
|
"version": "0.9.6",
|
||||||
"npmClient": "npm"
|
"npmClient": "npm"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bigcapital/server",
|
"name": "@bigcapital/server",
|
||||||
"version": "1.7.1",
|
"version": "0.9.5",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/server.ts",
|
"main": "src/server.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ exports.getCommonWebpackOptions = ({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ export interface ILedger {
|
|||||||
|
|
||||||
getEntries(): ILedgerEntry[];
|
getEntries(): ILedgerEntry[];
|
||||||
|
|
||||||
|
filter(cb: (entry: ILedgerEntry) => boolean): ILedger;
|
||||||
|
|
||||||
whereAccountId(accountId: number): ILedger;
|
whereAccountId(accountId: number): ILedger;
|
||||||
whereContactId(contactId: number): ILedger;
|
whereContactId(contactId: number): ILedger;
|
||||||
whereFromDate(fromDate: Date | string): ILedger;
|
whereFromDate(fromDate: Date | string): ILedger;
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import async from 'async';
|
import async from 'async';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { ILedger, ISaleContactsBalanceQueuePayload } from '@/interfaces';
|
import {
|
||||||
|
ILedger,
|
||||||
|
ILedgerEntry,
|
||||||
|
ISaleContactsBalanceQueuePayload,
|
||||||
|
} from '@/interfaces';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { TenantMetadata } from '@/system/models';
|
import { TenantMetadata } from '@/system/models';
|
||||||
|
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class LedgerContactsBalanceStorage {
|
export class LedgerContactsBalanceStorage {
|
||||||
@@ -49,6 +54,29 @@ export class LedgerContactsBalanceStorage {
|
|||||||
await this.saveContactBalance(tenantId, ledger, contactId, trx);
|
await this.saveContactBalance(tenantId, ledger, contactId, trx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters AP/AR ledger entries.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {Knex.Transaction} trx
|
||||||
|
* @returns {Promise<(entry: ILedgerEntry) => boolean>}
|
||||||
|
*/
|
||||||
|
private filterARAPLedgerEntris = async (
|
||||||
|
tenantId: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
): Promise<(entry: ILedgerEntry) => boolean> => {
|
||||||
|
const { Account } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const ARAPAcounts = await Account.query(trx).whereIn('accountType', [
|
||||||
|
ACCOUNT_TYPE.ACCOUNTS_RECEIVABLE,
|
||||||
|
ACCOUNT_TYPE.ACCOUNTS_PAYABLE,
|
||||||
|
]);
|
||||||
|
const ARAPAcountsIds = ARAPAcounts.map((a) => a.id);
|
||||||
|
|
||||||
|
return (entry: ILedgerEntry) => {
|
||||||
|
return ARAPAcountsIds.indexOf(entry.accountId) !== -1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -63,7 +91,7 @@ export class LedgerContactsBalanceStorage {
|
|||||||
trx?: Knex.Transaction
|
trx?: Knex.Transaction
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const { Contact } = this.tenancy.models(tenantId);
|
const { Contact } = this.tenancy.models(tenantId);
|
||||||
const contact = await Contact.query().findById(contactId);
|
const contact = await Contact.query(trx).findById(contactId);
|
||||||
|
|
||||||
// Retrieves the given tenant metadata.
|
// Retrieves the given tenant metadata.
|
||||||
const tenantMeta = await TenantMetadata.query().findOne({ tenantId });
|
const tenantMeta = await TenantMetadata.query().findOne({ tenantId });
|
||||||
@@ -72,7 +100,15 @@ export class LedgerContactsBalanceStorage {
|
|||||||
const isForeignContact = contact.currencyCode !== tenantMeta.baseCurrency;
|
const isForeignContact = contact.currencyCode !== tenantMeta.baseCurrency;
|
||||||
|
|
||||||
// Filters the ledger base on the given contact id.
|
// Filters the ledger base on the given contact id.
|
||||||
const contactLedger = ledger.whereContactId(contactId);
|
const filterARAPLedgerEntris = await this.filterARAPLedgerEntris(
|
||||||
|
tenantId,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
const contactLedger = ledger
|
||||||
|
// Filter entries only that have contact id.
|
||||||
|
.whereContactId(contactId)
|
||||||
|
// Filter entries on AR/AP accounts.
|
||||||
|
.filter(filterARAPLedgerEntris);
|
||||||
|
|
||||||
const closingBalance = isForeignContact
|
const closingBalance = isForeignContact
|
||||||
? contactLedger
|
? contactLedger
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export class CreateCustomer {
|
|||||||
} as ICustomerEventCreatingPayload);
|
} as ICustomerEventCreatingPayload);
|
||||||
|
|
||||||
// Creates a new contact as customer.
|
// Creates a new contact as customer.
|
||||||
const customer = await Contact.query().insertAndFetch({
|
const customer = await Contact.query(trx).insertAndFetch({
|
||||||
...customerObj,
|
...customerObj,
|
||||||
});
|
});
|
||||||
// Triggers `onCustomerCreated` event.
|
// Triggers `onCustomerCreated` event.
|
||||||
|
|||||||
@@ -1,7 +1,18 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const dotenv = require('dotenv-webpack');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
webpack: {
|
webpack: {
|
||||||
|
plugins: [
|
||||||
|
new dotenv(),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': {
|
||||||
|
MONOREPO_VERSION: JSON.stringify(require('../../lerna.json').version),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
|
||||||
alias: {
|
alias: {
|
||||||
'@': path.resolve(__dirname, 'src'),
|
'@': path.resolve(__dirname, 'src'),
|
||||||
},
|
},
|
||||||
|
|||||||
147
packages/webapp/package-lock.json
generated
147
packages/webapp/package-lock.json
generated
@@ -1929,137 +1929,6 @@
|
|||||||
"reselect": "^4.1.7"
|
"reselect": "^4.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/browser": {
|
|
||||||
"version": "6.19.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.19.7.tgz",
|
|
||||||
"integrity": "sha512-oDbklp4O3MtAM4mtuwyZLrgO1qDVYIujzNJQzXmi9YzymJCuzMLSRDvhY83NNDCRxf0pds4DShgYeZdbSyKraA==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/core": "6.19.7",
|
|
||||||
"@sentry/types": "6.19.7",
|
|
||||||
"@sentry/utils": "6.19.7",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sentry/core": {
|
|
||||||
"version": "6.19.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.19.7.tgz",
|
|
||||||
"integrity": "sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/hub": "6.19.7",
|
|
||||||
"@sentry/minimal": "6.19.7",
|
|
||||||
"@sentry/types": "6.19.7",
|
|
||||||
"@sentry/utils": "6.19.7",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sentry/hub": {
|
|
||||||
"version": "6.19.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.19.7.tgz",
|
|
||||||
"integrity": "sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/types": "6.19.7",
|
|
||||||
"@sentry/utils": "6.19.7",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sentry/minimal": {
|
|
||||||
"version": "6.19.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.19.7.tgz",
|
|
||||||
"integrity": "sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/hub": "6.19.7",
|
|
||||||
"@sentry/types": "6.19.7",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sentry/react": {
|
|
||||||
"version": "6.19.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.19.7.tgz",
|
|
||||||
"integrity": "sha512-VzJeBg/v41jfxUYPkH2WYrKjWc4YiMLzDX0f4Zf6WkJ4v3IlDDSkX6DfmWekjTKBho6wiMkSNy2hJ1dHfGZ9jA==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/browser": "6.19.7",
|
|
||||||
"@sentry/minimal": "6.19.7",
|
|
||||||
"@sentry/types": "6.19.7",
|
|
||||||
"@sentry/utils": "6.19.7",
|
|
||||||
"hoist-non-react-statics": "^3.3.2",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sentry/tracing": {
|
|
||||||
"version": "6.19.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.19.7.tgz",
|
|
||||||
"integrity": "sha512-ol4TupNnv9Zd+bZei7B6Ygnr9N3Gp1PUrNI761QSlHtPC25xXC5ssSD3GMhBgyQrcvpuRcCFHVNNM97tN5cZiA==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/hub": "6.19.7",
|
|
||||||
"@sentry/minimal": "6.19.7",
|
|
||||||
"@sentry/types": "6.19.7",
|
|
||||||
"@sentry/utils": "6.19.7",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sentry/types": {
|
|
||||||
"version": "6.19.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.19.7.tgz",
|
|
||||||
"integrity": "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg=="
|
|
||||||
},
|
|
||||||
"@sentry/utils": {
|
|
||||||
"version": "6.19.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.19.7.tgz",
|
|
||||||
"integrity": "sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/types": "6.19.7",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sheerun/mutationobserver-shim": {
|
"@sheerun/mutationobserver-shim": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz",
|
||||||
@@ -5985,11 +5854,27 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
||||||
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
|
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
|
||||||
},
|
},
|
||||||
|
"dotenv-defaults": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==",
|
||||||
|
"requires": {
|
||||||
|
"dotenv": "^8.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dotenv-expand": {
|
"dotenv-expand": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
|
||||||
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
|
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
|
||||||
},
|
},
|
||||||
|
"dotenv-webpack": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w==",
|
||||||
|
"requires": {
|
||||||
|
"dotenv-defaults": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"duplexer": {
|
"duplexer": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bigcapital/webapp",
|
"name": "@bigcapital/webapp",
|
||||||
"version": "1.7.1",
|
"version": "0.9.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blueprintjs-formik/core": "^0.3.3",
|
"@blueprintjs-formik/core": "^0.3.3",
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
"deep-map-keys": "^2.0.1",
|
"deep-map-keys": "^2.0.1",
|
||||||
"deepdash": "^5.3.9",
|
"deepdash": "^5.3.9",
|
||||||
"dependency-graph": "^0.11.0",
|
"dependency-graph": "^0.11.0",
|
||||||
|
"dotenv-webpack": "^8.0.1",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"flat": "^5.0.2",
|
"flat": "^5.0.2",
|
||||||
"formik": "^2.2.5",
|
"formik": "^2.2.5",
|
||||||
|
|||||||
@@ -1,193 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
Intent,
|
|
||||||
Button,
|
|
||||||
ButtonGroup,
|
|
||||||
Popover,
|
|
||||||
PopoverInteractionKind,
|
|
||||||
Position,
|
|
||||||
Menu,
|
|
||||||
MenuItem,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useFormikContext } from 'formik';
|
|
||||||
import { FormattedMessage as T, If, Icon } from '@/components';
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
|
||||||
import { useHistory } from 'react-router-dom';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make Journal floating actions bar.
|
|
||||||
*/
|
|
||||||
export default function MakeJournalEntriesFooter() {
|
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
// Formik context.
|
|
||||||
const { isSubmitting, submitForm } = useFormikContext();
|
|
||||||
|
|
||||||
// Make journal form context.
|
|
||||||
const {
|
|
||||||
manualJournalId,
|
|
||||||
setSubmitPayload,
|
|
||||||
manualJournalPublished = false,
|
|
||||||
} = useMakeJournalFormContext();
|
|
||||||
|
|
||||||
// Handle `submit & publish` button click.
|
|
||||||
const handleSubmitPublishBtnClick = (event) => {
|
|
||||||
setSubmitPayload({ redirect: true, publish: true });
|
|
||||||
submitForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle `submit, publish & new` button click.
|
|
||||||
const handleSubmitPublishAndNewBtnClick = (event) => {
|
|
||||||
setSubmitPayload({ redirect: false, publish: true, resetForm: true });
|
|
||||||
submitForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle `submit, publish & continue editing` button click.
|
|
||||||
const handleSubmitPublishContinueEditingBtnClick = (event) => {
|
|
||||||
setSubmitPayload({ redirect: false, publish: true });
|
|
||||||
submitForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle `submit as draft` button click.
|
|
||||||
const handleSubmitDraftBtnClick = (event) => {
|
|
||||||
setSubmitPayload({ redirect: true, publish: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle `submit as draft & new` button click.
|
|
||||||
const handleSubmitDraftAndNewBtnClick = (event) => {
|
|
||||||
setSubmitPayload({ redirect: false, publish: false, resetForm: true });
|
|
||||||
submitForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handles submit as draft & continue editing button click.
|
|
||||||
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
|
||||||
setSubmitPayload({ redirect: false, publish: false });
|
|
||||||
submitForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle cancel button action click.
|
|
||||||
const handleCancelBtnClick = (event) => {
|
|
||||||
history.goBack();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClearBtnClick = (event) => {};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
|
||||||
{/* ----------- Save And Publish ----------- */}
|
|
||||||
<If condition={!manualJournalId || !manualJournalPublished}>
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button
|
|
||||||
disabled={isSubmitting}
|
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
type="submit"
|
|
||||||
onClick={handleSubmitPublishBtnClick}
|
|
||||||
text={<T id={'save_publish'} />}
|
|
||||||
/>
|
|
||||||
<Popover
|
|
||||||
content={
|
|
||||||
<Menu>
|
|
||||||
<MenuItem
|
|
||||||
text={<T id={'publish_and_new'} />}
|
|
||||||
onClick={handleSubmitPublishAndNewBtnClick}
|
|
||||||
/>
|
|
||||||
<MenuItem
|
|
||||||
text={<T id={'publish_continue_editing'} />}
|
|
||||||
onClick={handleSubmitPublishContinueEditingBtnClick}
|
|
||||||
/>
|
|
||||||
</Menu>
|
|
||||||
}
|
|
||||||
minimal={true}
|
|
||||||
interactionKind={PopoverInteractionKind.CLICK}
|
|
||||||
position={Position.BOTTOM_LEFT}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
disabled={isSubmitting}
|
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
|
||||||
/>
|
|
||||||
</Popover>
|
|
||||||
</ButtonGroup>
|
|
||||||
{/* ----------- Save As Draft ----------- */}
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button
|
|
||||||
disabled={isSubmitting}
|
|
||||||
className={'ml1'}
|
|
||||||
type="submit"
|
|
||||||
onClick={handleSubmitDraftBtnClick}
|
|
||||||
text={<T id={'save_as_draft'} />}
|
|
||||||
/>
|
|
||||||
<Popover
|
|
||||||
content={
|
|
||||||
<Menu>
|
|
||||||
<MenuItem
|
|
||||||
text={<T id={'save_and_new'} />}
|
|
||||||
onClick={handleSubmitDraftAndNewBtnClick}
|
|
||||||
/>
|
|
||||||
<MenuItem
|
|
||||||
text={<T id={'save_continue_editing'} />}
|
|
||||||
onClick={handleSubmitDraftContinueEditingBtnClick}
|
|
||||||
/>
|
|
||||||
</Menu>
|
|
||||||
}
|
|
||||||
minimal={true}
|
|
||||||
interactionKind={PopoverInteractionKind.CLICK}
|
|
||||||
position={Position.BOTTOM_LEFT}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
disabled={isSubmitting}
|
|
||||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
|
||||||
/>
|
|
||||||
</Popover>
|
|
||||||
</ButtonGroup>
|
|
||||||
</If>
|
|
||||||
{/* ----------- Save and New ----------- */}
|
|
||||||
<If condition={manualJournalId && manualJournalPublished}>
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button
|
|
||||||
disabled={isSubmitting}
|
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
type="submit"
|
|
||||||
onClick={handleSubmitPublishBtnClick}
|
|
||||||
text={<T id={'save'} />}
|
|
||||||
/>
|
|
||||||
<Popover
|
|
||||||
content={
|
|
||||||
<Menu>
|
|
||||||
<MenuItem
|
|
||||||
text={<T id={'save_and_new'} />}
|
|
||||||
onClick={handleSubmitPublishAndNewBtnClick}
|
|
||||||
/>
|
|
||||||
</Menu>
|
|
||||||
}
|
|
||||||
minimal={true}
|
|
||||||
interactionKind={PopoverInteractionKind.CLICK}
|
|
||||||
position={Position.BOTTOM_LEFT}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
disabled={isSubmitting}
|
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
|
||||||
/>
|
|
||||||
</Popover>
|
|
||||||
</ButtonGroup>
|
|
||||||
</If>
|
|
||||||
{/* ----------- Clear & Reset----------- */}
|
|
||||||
<Button
|
|
||||||
className={'ml1'}
|
|
||||||
disabled={isSubmitting}
|
|
||||||
onClick={handleClearBtnClick}
|
|
||||||
text={manualJournalId ? <T id={'reset'} /> : <T id={'clear'} />}
|
|
||||||
/>
|
|
||||||
{/* ----------- Cancel ----------- */}
|
|
||||||
<Button
|
|
||||||
className={'ml1'}
|
|
||||||
onClick={handleCancelBtnClick}
|
|
||||||
text={<T id={'cancel'} />}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -28,6 +28,8 @@ const ERROR = {
|
|||||||
CREDIT_DEBIT_SUMATION_SHOULD_NOT_EQUAL_ZERO:
|
CREDIT_DEBIT_SUMATION_SHOULD_NOT_EQUAL_ZERO:
|
||||||
'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO',
|
'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO',
|
||||||
ENTRIES_SHOULD_ASSIGN_WITH_CONTACT: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT',
|
ENTRIES_SHOULD_ASSIGN_WITH_CONTACT: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT',
|
||||||
|
COULD_NOT_ASSIGN_DIFFERENT_CURRENCY_TO_ACCOUNTS:
|
||||||
|
'COULD_NOT_ASSIGN_DIFFERENT_CURRENCY_TO_ACCOUNTS',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MIN_LINES_NUMBER = 1;
|
export const MIN_LINES_NUMBER = 1;
|
||||||
@@ -161,6 +163,15 @@ export const transformErrors = (resErrors, { setErrors, errors }) => {
|
|||||||
intl.get('journal_number_is_already_used'),
|
intl.get('journal_number_is_already_used'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
(error = getError(ERROR.COULD_NOT_ASSIGN_DIFFERENT_CURRENCY_TO_ACCOUNTS))
|
||||||
|
) {
|
||||||
|
toastMessages.push(
|
||||||
|
intl.get(
|
||||||
|
'make_journal.errors.should_add_accounts_in_same_currency_or_base_currency',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
setErrors({ ...newErrors });
|
setErrors({ ...newErrors });
|
||||||
|
|
||||||
if (toastMessages.length > 0) {
|
if (toastMessages.length > 0) {
|
||||||
|
|||||||
@@ -27,13 +27,10 @@ function MoneyInFloatingActions({
|
|||||||
setSubmitPayload({ publish: false });
|
setSubmitPayload({ publish: false });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit button click.
|
// Handle submit button click.
|
||||||
const handleSubmittBtnClick = (event) => {
|
const handleSubmittBtnClick = (event) => {
|
||||||
setSubmitPayload({ publish: true });
|
setSubmitPayload({ publish: true });
|
||||||
submitForm();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle close button click.
|
// Handle close button click.
|
||||||
const handleCloseBtnClick = (event) => {
|
const handleCloseBtnClick = (event) => {
|
||||||
closeDialog(dialogName);
|
closeDialog(dialogName);
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ function MoneyOutFloatingActions({
|
|||||||
// Handle submit button click.
|
// Handle submit button click.
|
||||||
const handleSubmittBtnClick = (event) => {
|
const handleSubmittBtnClick = (event) => {
|
||||||
setSubmitPayload({ publish: true });
|
setSubmitPayload({ publish: true });
|
||||||
submitForm();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle close button click.
|
// Handle close button click.
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormGroup, Position, Classes, ControlGroup } from '@blueprintjs/core';
|
import { FormGroup, Position, Classes, ControlGroup } from '@blueprintjs/core';
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
||||||
import { FastField, ErrorMessage } from 'formik';
|
|
||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import {
|
import {
|
||||||
FFormGroup,
|
FFormGroup,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
MoneyInputGroup,
|
|
||||||
InputPrependText,
|
InputPrependText,
|
||||||
CurrencySelectList,
|
CurrencySelectList,
|
||||||
BranchSelect,
|
BranchSelect,
|
||||||
@@ -17,10 +14,17 @@ import {
|
|||||||
FeatureCan,
|
FeatureCan,
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
|
FMoneyInputGroup,
|
||||||
|
ExchangeRateInputGroup,
|
||||||
|
FDateInput,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||||
import { useSetPrimaryBranchToForm } from './utils';
|
import {
|
||||||
import { momentFormatter, tansformDateValue, inputIntent } from '@/utils';
|
openingBalanceFieldShouldUpdate,
|
||||||
|
useIsCustomerForeignCurrency,
|
||||||
|
useSetPrimaryBranchToForm,
|
||||||
|
} from './utils';
|
||||||
|
import { useCurrentOrganization } from '@/hooks/state';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customer financial panel.
|
* Customer financial panel.
|
||||||
@@ -35,76 +39,6 @@ export default function CustomerFinancialPanel() {
|
|||||||
<div className={'tab-panel--financial'}>
|
<div className={'tab-panel--financial'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={6}>
|
<Col xs={6}>
|
||||||
{/*------------ Opening balance at -----------*/}
|
|
||||||
<FastField name={'opening_balance_at'}>
|
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'opening_balance_at'} />}
|
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
inline={true}
|
|
||||||
helperText={<ErrorMessage name="opening_balance_at" />}
|
|
||||||
>
|
|
||||||
<DateInput
|
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
|
||||||
onChange={(date) => {
|
|
||||||
form.setFieldValue(
|
|
||||||
'opening_balance_at',
|
|
||||||
moment(date).format('YYYY-MM-DD'),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
value={tansformDateValue(value)}
|
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
|
||||||
disabled={customerId}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
{/*------------ Opening balance -----------*/}
|
|
||||||
<FastField name={'opening_balance'}>
|
|
||||||
{({ form, field, field: { value }, meta: { error, touched } }) => (
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'opening_balance'} />}
|
|
||||||
className={classNames(
|
|
||||||
'form-group--opening-balance',
|
|
||||||
Classes.FILL,
|
|
||||||
)}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
inline={true}
|
|
||||||
>
|
|
||||||
<ControlGroup>
|
|
||||||
<InputPrependText text={form.values.currency_code} />
|
|
||||||
<MoneyInputGroup
|
|
||||||
value={value}
|
|
||||||
inputGroupProps={{ fill: true }}
|
|
||||||
disabled={customerId}
|
|
||||||
onChange={(balance) => {
|
|
||||||
form.setFieldValue('opening_balance', balance);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ControlGroup>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
{/*------------ Opening branch -----------*/}
|
|
||||||
<FeatureCan feature={Features.Branches}>
|
|
||||||
<FFormGroup
|
|
||||||
label={<T id={'customer.label.opening_branch'} />}
|
|
||||||
name={'opening_balance_branch_id'}
|
|
||||||
inline={true}
|
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
|
||||||
>
|
|
||||||
<BranchSelect
|
|
||||||
name={'opening_balance_branch_id'}
|
|
||||||
branches={branches}
|
|
||||||
input={BranchSelectButton}
|
|
||||||
popoverProps={{ minimal: true }}
|
|
||||||
/>
|
|
||||||
</FFormGroup>
|
|
||||||
</FeatureCan>
|
|
||||||
|
|
||||||
{/*------------ Currency -----------*/}
|
{/*------------ Currency -----------*/}
|
||||||
<FastField name={'currency_code'}>
|
<FastField name={'currency_code'}>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
@@ -123,12 +57,130 @@ export default function CustomerFinancialPanel() {
|
|||||||
onCurrencySelected={(currency) => {
|
onCurrencySelected={(currency) => {
|
||||||
form.setFieldValue('currency_code', currency.currency_code);
|
form.setFieldValue('currency_code', currency.currency_code);
|
||||||
}}
|
}}
|
||||||
|
disabled={customerId}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
|
{/*------------ Opening balance -----------*/}
|
||||||
|
<CustomerOpeningBalanceField />
|
||||||
|
|
||||||
|
{/*------ Opening Balance Exchange Rate -----*/}
|
||||||
|
<CustomerOpeningBalanceExchangeRateField />
|
||||||
|
|
||||||
|
{/*------------ Opening balance at -----------*/}
|
||||||
|
<CustomerOpeningBalanceAtField />
|
||||||
|
|
||||||
|
{/*------------ Opening branch -----------*/}
|
||||||
|
<FeatureCan feature={Features.Branches}>
|
||||||
|
<FFormGroup
|
||||||
|
label={<T id={'customer.label.opening_branch'} />}
|
||||||
|
name={'opening_balance_branch_id'}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
|
>
|
||||||
|
<BranchSelect
|
||||||
|
name={'opening_balance_branch_id'}
|
||||||
|
branches={branches}
|
||||||
|
input={BranchSelectButton}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
</FeatureCan>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer opening balance at date field.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
function CustomerOpeningBalanceAtField() {
|
||||||
|
const { customerId } = useCustomerFormContext();
|
||||||
|
|
||||||
|
// Cannot continue if the customer id is defined.
|
||||||
|
if (customerId) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormGroup
|
||||||
|
name={'opening_balance_at'}
|
||||||
|
label={<T id={'opening_balance_at'} />}
|
||||||
|
inline={true}
|
||||||
|
helperText={<ErrorMessage name="opening_balance_at" />}
|
||||||
|
>
|
||||||
|
<FDateInput
|
||||||
|
name={'opening_balance_at'}
|
||||||
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
disabled={customerId}
|
||||||
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
|
parseDate={(str) => new Date(str)}
|
||||||
|
fill={true}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer opening balance field.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
function CustomerOpeningBalanceField() {
|
||||||
|
const { customerId } = useCustomerFormContext();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
// Cannot continue if the customer id is defined.
|
||||||
|
if (customerId) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FFormGroup
|
||||||
|
label={<T id={'opening_balance'} />}
|
||||||
|
name={'opening_balance'}
|
||||||
|
inline={true}
|
||||||
|
shouldUpdate={openingBalanceFieldShouldUpdate}
|
||||||
|
shouldUpdateDeps={{ currencyCode: values.currency_code }}
|
||||||
|
fastField={true}
|
||||||
|
>
|
||||||
|
<ControlGroup>
|
||||||
|
<InputPrependText text={values.currency_code} />
|
||||||
|
<FMoneyInputGroup
|
||||||
|
name={'opening_balance'}
|
||||||
|
inputGroupProps={{ fill: true }}
|
||||||
|
/>
|
||||||
|
</ControlGroup>
|
||||||
|
</FFormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer opening balance exchange rate field if the customer has foreign
|
||||||
|
* currency.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
function CustomerOpeningBalanceExchangeRateField() {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
const { customerId } = useCustomerFormContext();
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
|
||||||
|
const isForeignJouranl = useIsCustomerForeignCurrency();
|
||||||
|
|
||||||
|
// Can't continue if the customer is not foreign.
|
||||||
|
if (!isForeignJouranl || customerId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<FFormGroup
|
||||||
|
label={' '}
|
||||||
|
name={'opening_balance_exchange_rate'}
|
||||||
|
inline={true}
|
||||||
|
>
|
||||||
|
<ExchangeRateInputGroup
|
||||||
|
fromCurrency={values.currency_code}
|
||||||
|
toCurrency={currentOrganization.base_currency}
|
||||||
|
name={'opening_balance_exchange_rate'}
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,11 +32,6 @@ export default function CustomersTabs() {
|
|||||||
title={intl.get('notes')}
|
title={intl.get('notes')}
|
||||||
panel={<CustomerNotePanel />}
|
panel={<CustomerNotePanel />}
|
||||||
/>
|
/>
|
||||||
<Tab
|
|
||||||
id={'attachement'}
|
|
||||||
title={intl.get('attachement')}
|
|
||||||
panel={<CustomerAttachmentTabs />}
|
|
||||||
/>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useFormikContext } from 'formik';
|
|||||||
import { first } from 'lodash';
|
import { first } from 'lodash';
|
||||||
|
|
||||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||||
|
import { useCurrentOrganization } from '@/hooks/state';
|
||||||
|
|
||||||
export const defaultInitialValues = {
|
export const defaultInitialValues = {
|
||||||
customer_type: 'business',
|
customer_type: 'business',
|
||||||
@@ -37,9 +38,11 @@ export const defaultInitialValues = {
|
|||||||
shipping_address_postcode: '',
|
shipping_address_postcode: '',
|
||||||
shipping_address_phone: '',
|
shipping_address_phone: '',
|
||||||
|
|
||||||
opening_balance: '',
|
|
||||||
currency_code: '',
|
currency_code: '',
|
||||||
|
|
||||||
|
opening_balance: '',
|
||||||
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
|
opening_balance_exchange_rate: '',
|
||||||
opening_balance_branch_id: '',
|
opening_balance_branch_id: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,3 +60,25 @@ export const useSetPrimaryBranchToForm = () => {
|
|||||||
}
|
}
|
||||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the current customer has foreign currency.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const useIsCustomerForeignCurrency = () => {
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
return currentOrganization.base_currency !== values.currency_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines the exchange opening balance field when should update.
|
||||||
|
*/
|
||||||
|
export const openingBalanceFieldShouldUpdate = (newProps, oldProps) => {
|
||||||
|
return (
|
||||||
|
newProps.shouldUpdateDeps.currencyCode !==
|
||||||
|
oldProps.shouldUpdateDeps.currencyCode ||
|
||||||
|
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ export function ActionsMenu({
|
|||||||
/>
|
/>
|
||||||
<Can I={CustomerAction.Edit} a={AbilitySubject.Customer}>
|
<Can I={CustomerAction.Edit} a={AbilitySubject.Customer}>
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<Icon icon="pen-18" />}
|
icon={<Icon icon="pen-18" />}
|
||||||
text={intl.get('edit_customer')}
|
text={intl.get('edit_customer')}
|
||||||
@@ -47,7 +46,7 @@ export function ActionsMenu({
|
|||||||
</Can>
|
</Can>
|
||||||
<Can I={CustomerAction.Create} a={AbilitySubject.Customer}>
|
<Can I={CustomerAction.Create} a={AbilitySubject.Customer}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<Icon icon="duplicate-16" />}
|
icon={<Icon icon="content-copy" iconSize={16} />}
|
||||||
text={intl.get('duplicate')}
|
text={intl.get('duplicate')}
|
||||||
onClick={safeCallback(onDuplicate, original)}
|
onClick={safeCallback(onDuplicate, original)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ export function Sidebar() {
|
|||||||
* @returns {React.JSX}
|
* @returns {React.JSX}
|
||||||
*/
|
*/
|
||||||
function SidebarFooterVersion() {
|
function SidebarFooterVersion() {
|
||||||
const { VERSION } = process.env;
|
const { MONOREPO_VERSION } = process.env;
|
||||||
|
|
||||||
if (!VERSION) {
|
if (!MONOREPO_VERSION) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return <div class="sidebar__version">v{VERSION}</div>;
|
return <div class="sidebar__version">v{MONOREPO_VERSION}</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { useCustomerOpeningBalanceContext } from './CustomerOpeningBalanceFormPr
|
|||||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||||
import { compose } from '@/utils';
|
import { compose } from '@/utils';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customer Opening balance floating actions.
|
* Customer Opening balance floating actions.
|
||||||
* @returns
|
* @returns
|
||||||
@@ -31,6 +30,9 @@ function CustomerOpeningBalanceFormFloatingActions({
|
|||||||
return (
|
return (
|
||||||
<div className={Classes.DIALOG_FOOTER}>
|
<div className={Classes.DIALOG_FOOTER}>
|
||||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||||
|
<Button onClick={handleCancelBtnClick} style={{ minWidth: '75px' }}>
|
||||||
|
<T id={'cancel'} />
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
@@ -39,9 +41,6 @@ function CustomerOpeningBalanceFormFloatingActions({
|
|||||||
>
|
>
|
||||||
{<T id={'edit'} />}
|
{<T id={'edit'} />}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleCancelBtnClick} style={{ minWidth: '75px' }}>
|
|
||||||
<T id={'cancel'} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ function InventoryAdjustmentFloatingActions({
|
|||||||
// Handle submit make adjustment button click.
|
// Handle submit make adjustment button click.
|
||||||
const handleSubmitMakeAdjustmentBtnClick = (event) => {
|
const handleSubmitMakeAdjustmentBtnClick = (event) => {
|
||||||
setSubmitPayload({ publish: true });
|
setSubmitPayload({ publish: true });
|
||||||
submitForm();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle close button click.
|
// Handle close button click.
|
||||||
@@ -53,7 +52,6 @@ function InventoryAdjustmentFloatingActions({
|
|||||||
<Button
|
<Button
|
||||||
loading={isSubmitting && !submitPayload.publish}
|
loading={isSubmitting && !submitPayload.publish}
|
||||||
style={{ minWidth: '75px' }}
|
style={{ minWidth: '75px' }}
|
||||||
type="submit"
|
|
||||||
onClick={handleSubmitDraftBtnClick}
|
onClick={handleSubmitDraftBtnClick}
|
||||||
>
|
>
|
||||||
{<T id={'save_as_draft'} />}
|
{<T id={'save_as_draft'} />}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default function ItemFormFloatingActions({ onCancel }) {
|
|||||||
const { setSubmitPayload, isNewMode } = useItemFormContext();
|
const { setSubmitPayload, isNewMode } = useItemFormContext();
|
||||||
|
|
||||||
// Formik context.
|
// Formik context.
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting, submitForm } = useFormikContext();
|
||||||
|
|
||||||
// Handle cancel button click.
|
// Handle cancel button click.
|
||||||
const handleCancelBtnClick = (event) => {
|
const handleCancelBtnClick = (event) => {
|
||||||
@@ -33,6 +33,7 @@ export default function ItemFormFloatingActions({ onCancel }) {
|
|||||||
// Handle submit & new button click.
|
// Handle submit & new button click.
|
||||||
const handleSubmitAndNewBtnClick = (event) => {
|
const handleSubmitAndNewBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false });
|
setSubmitPayload({ redirect: false });
|
||||||
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -52,7 +53,6 @@ export default function ItemFormFloatingActions({ onCancel }) {
|
|||||||
className={classNames('ml1', 'btn--submit-new')}
|
className={classNames('ml1', 'btn--submit-new')}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
onClick={handleSubmitAndNewBtnClick}
|
onClick={handleSubmitAndNewBtnClick}
|
||||||
type="submit"
|
|
||||||
>
|
>
|
||||||
<T id={'save_new'} />
|
<T id={'save_new'} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default function PaymentMadeFloatingActions() {
|
|||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
// Formik context.
|
// Formik context.
|
||||||
const { isSubmitting, resetForm } = useFormikContext();
|
const { isSubmitting, resetForm, submitForm } = useFormikContext();
|
||||||
|
|
||||||
// Payment made form context.
|
// Payment made form context.
|
||||||
const { setSubmitPayload, paymentMadeId } = usePaymentMadeFormContext();
|
const { setSubmitPayload, paymentMadeId } = usePaymentMadeFormContext();
|
||||||
@@ -50,12 +50,15 @@ export default function PaymentMadeFloatingActions() {
|
|||||||
// Handle submit & new button click.
|
// Handle submit & new button click.
|
||||||
const handleSubmitAndNewClick = (event) => {
|
const handleSubmitAndNewClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, resetForm: true });
|
setSubmitPayload({ redirect: false, resetForm: true });
|
||||||
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// handle submit & continue editing button click.
|
// Handle submit & continue editing button click.
|
||||||
const handleSubmitContinueEditingBtnClick = (event) => {
|
const handleSubmitContinueEditingBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, publish: true });
|
setSubmitPayload({ redirect: false, publish: true });
|
||||||
|
submitForm()
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||||
{/* ----------- Save and New ----------- */}
|
{/* ----------- Save and New ----------- */}
|
||||||
|
|||||||
@@ -87,18 +87,19 @@ function PaymentMadeForm({
|
|||||||
message: intl.get('you_cannot_make_payment_with_zero_total_amount'),
|
message: intl.get('you_cannot_make_payment_with_zero_total_amount'),
|
||||||
intent: Intent.DANGER,
|
intent: Intent.DANGER,
|
||||||
});
|
});
|
||||||
|
setSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Transformes the form values to request body.
|
// Transformes the form values to request body.
|
||||||
const form = transformFormToRequest(values);
|
const form = transformFormToRequest(values);
|
||||||
|
|
||||||
// Triggers once the save request success.
|
// Triggers once the save request success.
|
||||||
const onSaved = (response) => {
|
const onSaved = () => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: intl.get(
|
message: intl.get(
|
||||||
isNewMode
|
isNewMode
|
||||||
? 'the_payment_made_has_been_edited_successfully'
|
? 'the_payment_made_has_been_created_successfully'
|
||||||
: 'the_payment_made_has_been_created_successfully',
|
: 'the_payment_made_has_been_edited_successfully',
|
||||||
),
|
),
|
||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
@@ -116,7 +117,6 @@ function PaymentMadeForm({
|
|||||||
if (errors) {
|
if (errors) {
|
||||||
transformErrors(errors, { setFieldError });
|
transformErrors(errors, { setFieldError });
|
||||||
}
|
}
|
||||||
|
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -34,25 +34,20 @@ export default function PaymentReceiveFormFloatingActions() {
|
|||||||
// Handle submit button click.
|
// Handle submit button click.
|
||||||
const handleSubmitBtnClick = (event) => {
|
const handleSubmitBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: true });
|
setSubmitPayload({ redirect: true });
|
||||||
submitForm();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle clear button click.
|
// Handle clear button click.
|
||||||
const handleClearBtnClick = (event) => {
|
const handleClearBtnClick = (event) => {
|
||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle cancel button click.
|
// Handle cancel button click.
|
||||||
const handleCancelBtnClick = (event) => {
|
const handleCancelBtnClick = (event) => {
|
||||||
history.goBack();
|
history.goBack();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit & new button click.
|
// Handle submit & new button click.
|
||||||
const handleSubmitAndNewClick = (event) => {
|
const handleSubmitAndNewClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, resetForm: true });
|
setSubmitPayload({ redirect: false, resetForm: true });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit & continue editing button click.
|
// Handle submit & continue editing button click.
|
||||||
const handleSubmitContinueEditingBtnClick = (event) => {
|
const handleSubmitContinueEditingBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, publish: true });
|
setSubmitPayload({ redirect: false, publish: true });
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ function PaymentReceiveForm({
|
|||||||
message: intl.get('you_cannot_make_payment_with_zero_total_amount'),
|
message: intl.get('you_cannot_make_payment_with_zero_total_amount'),
|
||||||
intent: Intent.DANGER,
|
intent: Intent.DANGER,
|
||||||
});
|
});
|
||||||
|
setSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Transformes the form values to request body.
|
// Transformes the form values to request body.
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormGroup, ControlGroup, Position, Classes } from '@blueprintjs/core';
|
import { FormGroup, ControlGroup, Position, Classes } from '@blueprintjs/core';
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
||||||
import { FastField, ErrorMessage } from 'formik';
|
|
||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import {
|
import {
|
||||||
FFormGroup,
|
FFormGroup,
|
||||||
MoneyInputGroup,
|
|
||||||
InputPrependText,
|
InputPrependText,
|
||||||
CurrencySelectList,
|
CurrencySelectList,
|
||||||
BranchSelect,
|
BranchSelect,
|
||||||
@@ -17,16 +14,23 @@ import {
|
|||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
|
FMoneyInputGroup,
|
||||||
|
ExchangeRateInputGroup,
|
||||||
|
FDateInput,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useSetPrimaryBranchToForm } from './utils';
|
import {
|
||||||
import { momentFormatter, tansformDateValue, inputIntent } from '@/utils';
|
openingBalanceFieldShouldUpdate,
|
||||||
|
useIsVendorForeignCurrency,
|
||||||
|
useSetPrimaryBranchToForm,
|
||||||
|
} from './utils';
|
||||||
import { useVendorFormContext } from './VendorFormProvider';
|
import { useVendorFormContext } from './VendorFormProvider';
|
||||||
|
import { useCurrentOrganization } from '@/hooks/state';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vendor Finaniceal Panel Tab.
|
* Vendor Finaniceal Panel Tab.
|
||||||
*/
|
*/
|
||||||
export default function VendorFinanicalPanelTab() {
|
export default function VendorFinanicalPanelTab() {
|
||||||
const { vendorId, currencies, branches } = useVendorFormContext();
|
const { currencies, branches } = useVendorFormContext();
|
||||||
|
|
||||||
// Sets the primary branch to form.
|
// Sets the primary branch to form.
|
||||||
useSetPrimaryBranchToForm();
|
useSetPrimaryBranchToForm();
|
||||||
@@ -35,78 +39,6 @@ export default function VendorFinanicalPanelTab() {
|
|||||||
<div className={'tab-panel--financial'}>
|
<div className={'tab-panel--financial'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={6}>
|
<Col xs={6}>
|
||||||
{/*------------ Opening balance at -----------*/}
|
|
||||||
<FastField name={'opening_balance_at'}>
|
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'opening_balance_at'} />}
|
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
inline={true}
|
|
||||||
helperText={<ErrorMessage name="opening_balance_at" />}
|
|
||||||
>
|
|
||||||
<DateInput
|
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
|
||||||
onChange={(date) => {
|
|
||||||
form.setFieldValue(
|
|
||||||
'opening_balance_at',
|
|
||||||
moment(date).format('YYYY-MM-DD'),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
value={tansformDateValue(value)}
|
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
|
||||||
disabled={vendorId}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
{/*------------ Opening balance -----------*/}
|
|
||||||
<FastField name={'opening_balance'}>
|
|
||||||
{({ form, field, field: { value }, meta: { error, touched } }) => (
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'opening_balance'} />}
|
|
||||||
className={classNames(
|
|
||||||
'form-group--opening-balance',
|
|
||||||
Classes.FILL,
|
|
||||||
)}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
inline={true}
|
|
||||||
>
|
|
||||||
<ControlGroup>
|
|
||||||
<InputPrependText text={form.values.currency_code} />
|
|
||||||
<MoneyInputGroup
|
|
||||||
value={value}
|
|
||||||
inputGroupProps={{
|
|
||||||
fill: true,
|
|
||||||
// ...field,
|
|
||||||
}}
|
|
||||||
onChange={(balance) => {
|
|
||||||
form.setFieldValue('opening_balance', balance);
|
|
||||||
}}
|
|
||||||
disabled={vendorId}
|
|
||||||
/>
|
|
||||||
</ControlGroup>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
{/*------------ Opening branch -----------*/}
|
|
||||||
<FeatureCan feature={Features.Branches}>
|
|
||||||
<FFormGroup
|
|
||||||
label={<T id={'vendor.label.opening_branch'} />}
|
|
||||||
name={'opening_balance_branch_id'}
|
|
||||||
inline={true}
|
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
|
||||||
>
|
|
||||||
<BranchSelect
|
|
||||||
name={'opening_balance_branch_id'}
|
|
||||||
branches={branches}
|
|
||||||
input={BranchSelectButton}
|
|
||||||
popoverProps={{ minimal: true }}
|
|
||||||
/>
|
|
||||||
</FFormGroup>
|
|
||||||
</FeatureCan>
|
|
||||||
|
|
||||||
{/*------------ Currency -----------*/}
|
{/*------------ Currency -----------*/}
|
||||||
<FastField name={'currency_code'}>
|
<FastField name={'currency_code'}>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
@@ -129,8 +61,121 @@ export default function VendorFinanicalPanelTab() {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
|
{/*------------ Opening balance -----------*/}
|
||||||
|
<VendorOpeningBalanceField />
|
||||||
|
<VendorOpeningBalanceExchangeRateField />
|
||||||
|
|
||||||
|
{/*------------ Opening balance at -----------*/}
|
||||||
|
<VendorOpeningBalanceAtField />
|
||||||
|
|
||||||
|
{/*------------ Opening branch -----------*/}
|
||||||
|
<FeatureCan feature={Features.Branches}>
|
||||||
|
<FFormGroup
|
||||||
|
label={<T id={'vendor.label.opening_branch'} />}
|
||||||
|
name={'opening_balance_branch_id'}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
|
>
|
||||||
|
<BranchSelect
|
||||||
|
name={'opening_balance_branch_id'}
|
||||||
|
branches={branches}
|
||||||
|
input={BranchSelectButton}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
</FeatureCan>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vendor opening balance field.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
function VendorOpeningBalanceField() {
|
||||||
|
const { vendorId } = useVendorFormContext();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
// Cannot continue if the vendor id is defined.
|
||||||
|
if (vendorId) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FFormGroup
|
||||||
|
name={'opening_balance'}
|
||||||
|
label={<T id={'opening_balance'} />}
|
||||||
|
inline={true}
|
||||||
|
shouldUpdate={openingBalanceFieldShouldUpdate}
|
||||||
|
shouldUpdateDeps={{ currencyCode: values.currency_code }}
|
||||||
|
fastField={true}
|
||||||
|
>
|
||||||
|
<ControlGroup>
|
||||||
|
<InputPrependText text={values.currency_code} />
|
||||||
|
<FMoneyInputGroup
|
||||||
|
name={'opening_balance'}
|
||||||
|
inputGroupProps={{ fill: true }}
|
||||||
|
/>
|
||||||
|
</ControlGroup>
|
||||||
|
</FFormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vendor opening balance at date field.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
function VendorOpeningBalanceAtField() {
|
||||||
|
const { vendorId } = useVendorFormContext();
|
||||||
|
|
||||||
|
// Cannot continue if the vendor id is defined.
|
||||||
|
if (vendorId) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FFormGroup
|
||||||
|
name={'opening_balance_at'}
|
||||||
|
label={<T id={'opening_balance_at'} />}
|
||||||
|
inline={true}
|
||||||
|
helperText={<ErrorMessage name="opening_balance_at" />}
|
||||||
|
>
|
||||||
|
<FDateInput
|
||||||
|
name={'opening_balance_at'}
|
||||||
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
disabled={vendorId}
|
||||||
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
|
parseDate={(str) => new Date(str)}
|
||||||
|
fill={true}
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vendor opening balance exchange rate field if the vendor has foreign currency.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
function VendorOpeningBalanceExchangeRateField() {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
const { vendorId } = useVendorFormContext();
|
||||||
|
const isForeignVendor = useIsVendorForeignCurrency();
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
|
||||||
|
// Cannot continue if the current vendor does not have foreign currency.
|
||||||
|
if (!isForeignVendor || vendorId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<FFormGroup
|
||||||
|
label={' '}
|
||||||
|
name={'opening_balance_exchange_rate'}
|
||||||
|
inline={true}
|
||||||
|
>
|
||||||
|
<ExchangeRateInputGroup
|
||||||
|
fromCurrency={values.currency_code}
|
||||||
|
toCurrency={currentOrganization.base_currency}
|
||||||
|
name={'opening_balance_exchange_rate'}
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ export default function VendorFloatingActions({ onCancel }) {
|
|||||||
// Handle the submit button.
|
// Handle the submit button.
|
||||||
const handleSubmitBtnClick = (event) => {
|
const handleSubmitBtnClick = (event) => {
|
||||||
setSubmitPayload({ noRedirect: false });
|
setSubmitPayload({ noRedirect: false });
|
||||||
submitForm();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle the submit & new button click.
|
// Handle the submit & new button click.
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { Tabs, Tab } from '@blueprintjs/core';
|
|||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
|
|
||||||
import VendorFinanicalPanelTab from './VendorFinanicalPanelTab';
|
import VendorFinanicalPanelTab from './VendorFinanicalPanelTab';
|
||||||
import VendorAttahmentTab from './VendorAttahmentTab';
|
|
||||||
|
|
||||||
import CustomerAddressTabs from '@/containers/Customers/CustomerForm/CustomerAddressTabs';
|
import CustomerAddressTabs from '@/containers/Customers/CustomerForm/CustomerAddressTabs';
|
||||||
import CustomerNotePanel from '@/containers/Customers/CustomerForm/CustomerNotePanel';
|
import CustomerNotePanel from '@/containers/Customers/CustomerForm/CustomerNotePanel';
|
||||||
@@ -15,7 +14,6 @@ import CustomerNotePanel from '@/containers/Customers/CustomerForm/CustomerNoteP
|
|||||||
* Vendor form tabs.
|
* Vendor form tabs.
|
||||||
*/
|
*/
|
||||||
export default function VendorTabs() {
|
export default function VendorTabs() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
|
||||||
<Tabs
|
<Tabs
|
||||||
@@ -27,7 +25,7 @@ export default function VendorTabs() {
|
|||||||
<Tab
|
<Tab
|
||||||
id={'financial'}
|
id={'financial'}
|
||||||
title={intl.get('financial_details')}
|
title={intl.get('financial_details')}
|
||||||
panel={<VendorFinanicalPanelTab />}
|
panel={<VendorFinanicalPanelTab />}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
id={'address'}
|
id={'address'}
|
||||||
@@ -39,11 +37,6 @@ export default function VendorTabs() {
|
|||||||
title={intl.get('notes')}
|
title={intl.get('notes')}
|
||||||
panel={<CustomerNotePanel />}
|
panel={<CustomerNotePanel />}
|
||||||
/>
|
/>
|
||||||
<Tab
|
|
||||||
id={'attachement'}
|
|
||||||
title={intl.get('attachement')}
|
|
||||||
panel={<VendorAttahmentTab />}
|
|
||||||
/>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useFormikContext } from 'formik';
|
|||||||
import { first } from 'lodash';
|
import { first } from 'lodash';
|
||||||
|
|
||||||
import { useVendorFormContext } from './VendorFormProvider';
|
import { useVendorFormContext } from './VendorFormProvider';
|
||||||
|
import { useCurrentOrganization } from '@/hooks/state';
|
||||||
|
|
||||||
export const defaultInitialValues = {
|
export const defaultInitialValues = {
|
||||||
salutation: '',
|
salutation: '',
|
||||||
@@ -36,10 +37,12 @@ export const defaultInitialValues = {
|
|||||||
shipping_address_postcode: '',
|
shipping_address_postcode: '',
|
||||||
shipping_address_phone: '',
|
shipping_address_phone: '',
|
||||||
|
|
||||||
opening_balance: '',
|
|
||||||
currency_code: '',
|
currency_code: '',
|
||||||
|
|
||||||
|
opening_balance: '',
|
||||||
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
opening_balance_branch_id: '',
|
opening_balance_branch_id: '',
|
||||||
|
opening_balance_exchange_rate: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSetPrimaryBranchToForm = () => {
|
export const useSetPrimaryBranchToForm = () => {
|
||||||
@@ -56,3 +59,25 @@ export const useSetPrimaryBranchToForm = () => {
|
|||||||
}
|
}
|
||||||
}, [isBranchesSuccess, setFieldValue, branches]);
|
}, [isBranchesSuccess, setFieldValue, branches]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the current vendor has foreign currency.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const useIsVendorForeignCurrency = () => {
|
||||||
|
const currentOrganization = useCurrentOrganization();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
return currentOrganization.base_currency !== values.currency_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines the exchange opening balance field when should update.
|
||||||
|
*/
|
||||||
|
export const openingBalanceFieldShouldUpdate = (newProps, oldProps) => {
|
||||||
|
return (
|
||||||
|
newProps.shouldUpdateDeps.currencyCode !==
|
||||||
|
oldProps.shouldUpdateDeps.currencyCode ||
|
||||||
|
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function ActionsMenu({
|
|||||||
</Can>
|
</Can>
|
||||||
<Can I={VendorAction.Create} a={AbilitySubject.Customer}>
|
<Can I={VendorAction.Create} a={AbilitySubject.Customer}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<Icon icon="duplicate-16" />}
|
icon={<Icon icon="content-copy" iconSize={16} />}
|
||||||
text={intl.get('duplicate')}
|
text={intl.get('duplicate')}
|
||||||
onClick={safeCallback(onDuplicate, original)}
|
onClick={safeCallback(onDuplicate, original)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export default function WarehouseTransferFloatingActions() {
|
|||||||
// Handle submit initiate button click.
|
// Handle submit initiate button click.
|
||||||
const handleSubmitInitiateBtnClick = (event) => {
|
const handleSubmitInitiateBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: true, initiate: true, deliver: false });
|
setSubmitPayload({ redirect: true, initiate: true, deliver: false });
|
||||||
submitForm();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit transferred button click.
|
// Handle submit transferred button click.
|
||||||
|
|||||||
@@ -658,7 +658,7 @@
|
|||||||
"opening_balance_at": "Opening balance at",
|
"opening_balance_at": "Opening balance at",
|
||||||
"opening_balance": "Opening balance",
|
"opening_balance": "Opening balance",
|
||||||
"balance_currency": "Balance currency",
|
"balance_currency": "Balance currency",
|
||||||
"financial_details": "Financial details",
|
"financial_details": "Financial",
|
||||||
"are_you_sure_you_want_to_clear_this_transaction": "Are you sure you want to clear this transaction?",
|
"are_you_sure_you_want_to_clear_this_transaction": "Are you sure you want to clear this transaction?",
|
||||||
"clearing_the_table_lines_will_delete_all_credits": "Clearing the table lines will delete all credits and payment were applied, Is this okay?",
|
"clearing_the_table_lines_will_delete_all_credits": "Clearing the table lines will delete all credits and payment were applied, Is this okay?",
|
||||||
"changing_full_amount_will_change_all_credits_and_payment": " Changing full amount will change all credits and payment were applied, Is this okay?",
|
"changing_full_amount_will_change_all_credits_and_payment": " Changing full amount will change all credits and payment were applied, Is this okay?",
|
||||||
@@ -703,6 +703,7 @@
|
|||||||
"email_is_already_used": "The email is already used.",
|
"email_is_already_used": "The email is already used.",
|
||||||
"the_item_categories_has_been_deleted_successfully": "The item categories has been deleted successfully .",
|
"the_item_categories_has_been_deleted_successfully": "The item categories has been deleted successfully .",
|
||||||
"receivable_accounts_should_assign_with_customers": "Receivable accounts should assign with customers.",
|
"receivable_accounts_should_assign_with_customers": "Receivable accounts should assign with customers.",
|
||||||
|
"make_journal.errors.should_add_accounts_in_same_currency_or_base_currency": "You can only add accounts that have the same selected currency or base currency.",
|
||||||
"delivered": "Delivered",
|
"delivered": "Delivered",
|
||||||
"save_and_deliver": "Save & Deliver",
|
"save_and_deliver": "Save & Deliver",
|
||||||
"deliver_and_new": "Deliver and new",
|
"deliver_and_new": "Deliver and new",
|
||||||
|
|||||||
@@ -142,15 +142,8 @@
|
|||||||
> *:not(:last-child) {
|
> *:not(:last-child) {
|
||||||
margin-right: 25px;
|
margin-right: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.bp3-large > .bp3-tab {
|
&.bp3-large > .bp3-tab {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: #555;
|
|
||||||
|
|
||||||
&[aria-selected='true'],
|
|
||||||
&:not([aria-disabled='true']):hover {
|
|
||||||
color: $pt-link-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user