mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
Compare commits
12 Commits
BIG-409-so
...
v0.8.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36611652da | ||
|
|
06c7ee71b4 | ||
|
|
54d3188666 | ||
|
|
3ceb9adda2 | ||
|
|
1249415054 | ||
|
|
6c96c371c5 | ||
|
|
6c61a69f10 | ||
|
|
981b65349d | ||
|
|
a7d29a31c8 | ||
|
|
c1d92b74f0 | ||
|
|
6f0f47f38a | ||
|
|
83510cfa70 |
@@ -3,7 +3,12 @@ import { check, param, query } from 'express-validator';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import { AbilitySubject, AccountAction, IAccountDTO } from '@/interfaces';
|
||||
import {
|
||||
AbilitySubject,
|
||||
AccountAction,
|
||||
IAccountDTO,
|
||||
IAccountsStructureType,
|
||||
} from '@/interfaces';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import { DATATYPES_LENGTH } from '@/data/DataTypes';
|
||||
@@ -172,6 +177,11 @@ export default class AccountsController extends BaseController {
|
||||
|
||||
query('inactive_mode').optional().isBoolean().toBoolean(),
|
||||
query('search_keyword').optional({ nullable: true }).isString().trim(),
|
||||
|
||||
query('structure')
|
||||
.optional()
|
||||
.isString()
|
||||
.isIn([IAccountsStructureType.Tree, IAccountsStructureType.Flat]),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -341,6 +351,7 @@ export default class AccountsController extends BaseController {
|
||||
sortOrder: 'desc',
|
||||
columnSortBy: 'created_at',
|
||||
inactiveMode: false,
|
||||
structure: IAccountsStructureType.Tree,
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ export default class GeneralLedgerReportController extends BaseFinancialReportCo
|
||||
try {
|
||||
const { data, query, meta } =
|
||||
await this.generalLedgetService.generalLedger(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
meta: this.transfromToResponse(meta),
|
||||
data: this.transfromToResponse(data),
|
||||
|
||||
@@ -4,6 +4,7 @@ import moment from 'moment';
|
||||
global.__root_dir = path.join(__dirname, '..');
|
||||
global.__resources_dir = path.join(global.__root_dir, 'resources');
|
||||
global.__locales_dir = path.join(global.__resources_dir, 'locales');
|
||||
global.__views_dir = path.join(global.__root_dir, 'views');
|
||||
|
||||
moment.prototype.toMySqlDateTime = function () {
|
||||
return this.format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
@@ -79,9 +79,15 @@ export interface IAccountTransaction {
|
||||
}
|
||||
export interface IAccountResponse extends IAccount {}
|
||||
|
||||
export enum IAccountsStructureType {
|
||||
Tree = 'tree',
|
||||
Flat = 'flat',
|
||||
}
|
||||
|
||||
export interface IAccountsFilter extends IDynamicListFilterDTO {
|
||||
stringifiedFilterRoles?: string;
|
||||
onlyInactive: boolean;
|
||||
structure?: IAccountsStructureType;
|
||||
}
|
||||
|
||||
export interface IAccountType {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AnyObject } from '@casl/ability/dist/types/types';
|
||||
import { ITenant } from '@/interfaces';
|
||||
import { Model } from 'objection';
|
||||
import { Tenant } from '@/system/models';
|
||||
|
||||
export interface ISystemUser extends Model {
|
||||
id: number;
|
||||
@@ -54,20 +55,52 @@ export interface IUserInvite {
|
||||
|
||||
export interface IInviteUserService {
|
||||
acceptInvite(token: string, inviteUserInput: IInviteUserInput): Promise<void>;
|
||||
|
||||
/**
|
||||
* Re-send user invite.
|
||||
* @param {number} tenantId -
|
||||
* @param {string} email -
|
||||
* @return {Promise<{ invite: IUserInvite }>}
|
||||
*/
|
||||
resendInvite(
|
||||
tenantId: number,
|
||||
userId: number,
|
||||
authorizedUser: ISystemUser
|
||||
): Promise<{
|
||||
invite: IUserInvite;
|
||||
user: ITenantUser;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Sends invite mail to the given email from the given tenant and user.
|
||||
* @param {number} tenantId -
|
||||
* @param {string} email -
|
||||
* @param {IUser} authorizedUser -
|
||||
* @return {Promise<IUserInvite>}
|
||||
*/
|
||||
sendInvite(
|
||||
tenantId: number,
|
||||
email: string,
|
||||
sendInviteDTO: IUserSendInviteDTO,
|
||||
authorizedUser: ISystemUser
|
||||
): Promise<{
|
||||
invite: IUserInvite;
|
||||
invitedUser: ITenantUser;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface IAcceptInviteUserService {
|
||||
/**
|
||||
* Accept the received invite.
|
||||
* @param {string} token
|
||||
* @param {IInviteUserInput} inviteUserInput
|
||||
* @throws {ServiceErrors}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
acceptInvite(token: string, inviteUserDTO: IInviteUserInput): Promise<void>;
|
||||
|
||||
/**
|
||||
* Validate the given invite token.
|
||||
* @param {string} token - the given token string.
|
||||
* @throws {ServiceError}
|
||||
*/
|
||||
checkInvite(
|
||||
token: string
|
||||
): Promise<{ inviteToken: IUserInvite; orgName: object }>;
|
||||
@@ -121,7 +154,7 @@ export interface IUserInvitedEventPayload {
|
||||
tenantId: number;
|
||||
user: ITenantUser;
|
||||
}
|
||||
export interface IUserInviteTenantSyncedEventPayload{
|
||||
export interface IUserInviteTenantSyncedEventPayload {
|
||||
invite: IUserInvite;
|
||||
authorizedUser: ISystemUser;
|
||||
tenantId: number;
|
||||
@@ -143,10 +176,10 @@ export interface IAcceptInviteEventPayload {
|
||||
|
||||
export interface ICheckInviteEventPayload {
|
||||
inviteToken: IUserInvite;
|
||||
tenant: ITenant
|
||||
tenant: Tenant;
|
||||
}
|
||||
|
||||
export interface IUserSendInviteDTO {
|
||||
email: string;
|
||||
roleId: number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Container, Inject } from 'typedi';
|
||||
import InviteUserService from '@/services/InviteUsers/AcceptInviteUser';
|
||||
import SendInviteUsersMailMessage from '@/services/InviteUsers/SendInviteUsersMailMessage';
|
||||
|
||||
export default class UserInviteMailJob {
|
||||
/**
|
||||
@@ -21,24 +22,17 @@ export default class UserInviteMailJob {
|
||||
*/
|
||||
public async handler(job, done: Function): Promise<void> {
|
||||
const { invite, authorizedUser, tenantId } = job.attrs.data;
|
||||
|
||||
const Logger = Container.get('logger');
|
||||
const inviteUsersService = Container.get(InviteUserService);
|
||||
|
||||
Logger.info(`Send invite user mail - started: ${job.attrs.data}`);
|
||||
const sendInviteMailMessage = Container.get(SendInviteUsersMailMessage);
|
||||
|
||||
try {
|
||||
await inviteUsersService.mailMessages.sendInviteMail(
|
||||
await sendInviteMailMessage.sendInviteMail(
|
||||
tenantId,
|
||||
authorizedUser,
|
||||
invite
|
||||
);
|
||||
Logger.info(`Send invite user mail - finished: ${job.attrs.data}`);
|
||||
done();
|
||||
} catch (error) {
|
||||
Logger.info(
|
||||
`Send invite user mail - error: ${job.attrs.data}, error: ${error}`
|
||||
);
|
||||
console.log(error);
|
||||
done(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ export default class Mail {
|
||||
* Retrieve view content from the view directory.
|
||||
*/
|
||||
private getViewContent(): string {
|
||||
const filePath = path.join(global.__root_dir, `../views/${this.view}`);
|
||||
const filePath = path.join(global.__views_dir, `/${this.view}`);
|
||||
return fs.readFileSync(filePath, 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import moment from 'moment';
|
||||
import * as R from 'ramda';
|
||||
import { includes, isFunction, isObject, isUndefined, omit } from 'lodash';
|
||||
import { formatNumber } from 'utils';
|
||||
import { isArrayLikeObject } from 'lodash/fp';
|
||||
|
||||
export class Transformer {
|
||||
public context: any;
|
||||
@@ -39,12 +40,33 @@ export class Transformer {
|
||||
return object;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @returns
|
||||
*/
|
||||
protected preCollectionTransform = (object: any) => {
|
||||
return object;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @returns
|
||||
*/
|
||||
protected postCollectionTransform = (object: any) => {
|
||||
return object;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public work = (object: any) => {
|
||||
if (Array.isArray(object)) {
|
||||
return object.map(this.getTransformation);
|
||||
const preTransformed = this.preCollectionTransform(object);
|
||||
const transformed = preTransformed.map(this.getTransformation);
|
||||
|
||||
return this.postCollectionTransform(transformed);
|
||||
} else if (isObject(object)) {
|
||||
return this.getTransformation(object);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import SaleInvoiceAutoIncrementSubscriber from '@/subscribers/SaleInvoices/AutoI
|
||||
import SaleInvoiceConvertFromEstimateSubscriber from '@/subscribers/SaleInvoices/ConvertFromEstimate';
|
||||
import PaymentReceiveAutoSerialSubscriber from '@/subscribers/PaymentReceive/AutoSerialIncrement';
|
||||
import SyncSystemSendInvite from '@/services/InviteUsers/SyncSystemSendInvite';
|
||||
import InviteSendMainNotification from '@/services/InviteUsers/InviteSendMailNotification';
|
||||
import InviteSendMainNotification from '@/services/InviteUsers/InviteSendMailNotificationSubscribe';
|
||||
import SyncTenantAcceptInvite from '@/services/InviteUsers/SyncTenantAcceptInvite';
|
||||
import SyncTenantUserMutate from '@/services/Users/SyncTenantUserSaved';
|
||||
import { SyncTenantUserDelete } from '@/services/Users/SyncTenantUserDeleted';
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { IAccount } from '@/interfaces';
|
||||
import { IAccount, IAccountsStructureType } from '@/interfaces';
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { formatNumber } from 'utils';
|
||||
import {
|
||||
assocDepthLevelToObjectTree,
|
||||
flatToNestedArray,
|
||||
formatNumber,
|
||||
nestedArrayToFlatten,
|
||||
} from 'utils';
|
||||
|
||||
export class AccountTransformer extends Transformer {
|
||||
/**
|
||||
@@ -8,7 +13,23 @@ export class AccountTransformer extends Transformer {
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['formattedAmount'];
|
||||
return ['formattedAmount', 'flattenName'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the flatten name with all dependants accounts names.
|
||||
* @param {IAccount} account -
|
||||
* @returns {string}
|
||||
*/
|
||||
public flattenName = (account: IAccount): string => {
|
||||
const parentDependantsIds = this.options.accountsGraph.dependantsOf(
|
||||
account.id
|
||||
);
|
||||
const prefixAccounts = parentDependantsIds.map((dependId) => {
|
||||
const node = this.options.accountsGraph.getNodeData(dependId);
|
||||
return `${node.name}: `;
|
||||
});
|
||||
return `${prefixAccounts}${account.name}`;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -17,8 +38,28 @@ export class AccountTransformer extends Transformer {
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedAmount = (account: IAccount): string => {
|
||||
return formatNumber(account.amount, {
|
||||
currencyCode: account.currencyCode,
|
||||
return formatNumber(account.amount, { currencyCode: account.currencyCode });
|
||||
};
|
||||
|
||||
/**
|
||||
* Transformes the accounts collection to flat or nested array.
|
||||
* @param {IAccount[]}
|
||||
* @returns {IAccount[]}
|
||||
*/
|
||||
protected postCollectionTransform = (accounts: IAccount[]) => {
|
||||
// Transfom the flatten to accounts tree.
|
||||
const transformed = flatToNestedArray(accounts, {
|
||||
id: 'id',
|
||||
parentId: 'parentAccountId',
|
||||
});
|
||||
// Associate `accountLevel` attr to indicate object depth.
|
||||
const transformed2 = assocDepthLevelToObjectTree(
|
||||
transformed,
|
||||
1,
|
||||
'accountLevel'
|
||||
);
|
||||
return this.options.structure === IAccountsStructureType.Flat
|
||||
? nestedArrayToFlatten(transformed2)
|
||||
: transformed2;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,15 +22,19 @@ export class GetAccount {
|
||||
*/
|
||||
public getAccount = async (tenantId: number, accountId: number) => {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Find the given account or throw not found error.
|
||||
const account = await Account.query().findById(accountId).throwIfNotFound();
|
||||
|
||||
const accountsGraph = await accountRepository.getDependencyGraph();
|
||||
|
||||
// Transformes the account model to POJO.
|
||||
const transformed = await this.transformer.transform(
|
||||
tenantId,
|
||||
account,
|
||||
new AccountTransformer()
|
||||
new AccountTransformer(),
|
||||
{ accountsGraph }
|
||||
);
|
||||
return this.i18nService.i18nApply(
|
||||
[['accountTypeLabel'], ['accountNormalFormatted']],
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import { IAccountsFilter, IAccountResponse, IFilterMeta } from '@/interfaces';
|
||||
import {
|
||||
IAccountsFilter,
|
||||
IAccountResponse,
|
||||
IFilterMeta,
|
||||
IAccountsStructureType,
|
||||
} from '@/interfaces';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import { AccountTransformer } from './AccountTransform';
|
||||
@@ -38,6 +43,7 @@ export class GetAccounts {
|
||||
filterDTO: IAccountsFilter
|
||||
): Promise<{ accounts: IAccountResponse[]; filterMeta: IFilterMeta }> => {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Parses the stringified filter roles.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
@@ -53,17 +59,16 @@ export class GetAccounts {
|
||||
dynamicList.buildQuery()(builder);
|
||||
builder.modify('inactiveMode', filter.inactiveMode);
|
||||
});
|
||||
// Retrievs the formatted accounts collection.
|
||||
const preTransformedAccounts = await this.transformer.transform(
|
||||
|
||||
const accountsGraph = await accountRepository.getDependencyGraph();
|
||||
|
||||
// Retrieves the transformed accounts collection.
|
||||
const transformedAccounts = await this.transformer.transform(
|
||||
tenantId,
|
||||
accounts,
|
||||
new AccountTransformer()
|
||||
new AccountTransformer(),
|
||||
{ accountsGraph, structure: filterDTO.structure }
|
||||
);
|
||||
// Transform accounts to nested array.
|
||||
const transformedAccounts = flatToNestedArray(preTransformedAccounts, {
|
||||
id: 'id',
|
||||
parentId: 'parentAccountId',
|
||||
});
|
||||
|
||||
return {
|
||||
accounts: transformedAccounts,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ICashflowAccountTransactionsQuery, IPaginationMeta } from '@/interfaces
|
||||
@Service()
|
||||
export default class CashflowAccountTransactionsRepo {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow account transactions.
|
||||
|
||||
@@ -12,9 +12,12 @@ import {
|
||||
} from '@/interfaces';
|
||||
import { ERRORS } from './constants';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import { IAcceptInviteUserService } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class AcceptInviteUserService {
|
||||
export default class AcceptInviteUserService
|
||||
implements IAcceptInviteUserService
|
||||
{
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
IUserInvitedEventPayload,
|
||||
IUserInviteTenantSyncedEventPayload,
|
||||
} from '@/interfaces';
|
||||
import { IUserInviteTenantSyncedEventPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { Inject, Service } from 'typedi';
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import path from 'path';
|
||||
import { ISystemUser } from '@/interfaces';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import Mail from '@/lib/Mail';
|
||||
import { Service, Container } from 'typedi';
|
||||
import config from '@/config';
|
||||
import { Service } from 'typedi';
|
||||
import { Tenant } from '@/system/models';
|
||||
import config from '@/config';
|
||||
|
||||
@Service()
|
||||
export default class InviteUsersMailMessages {
|
||||
export default class SendInviteUsersMailMessage {
|
||||
/**
|
||||
* Sends invite mail to the given email.
|
||||
* @param user
|
||||
@@ -18,7 +18,7 @@ export default class InviteUsersMailMessages {
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
const root = __dirname + '/../../../views/images/bigcapital.png';
|
||||
const root = path.join(global.__views_dir, '/images/bigcapital.png');
|
||||
|
||||
const mail = new Mail()
|
||||
.setSubject(`${fromUser.firstName} has invited you to join a Bigcapital`)
|
||||
@@ -8,7 +8,7 @@ import { IAcceptInviteEventPayload } from '@/interfaces';
|
||||
@Service()
|
||||
export default class SyncTenantAcceptInvite {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
|
||||
@@ -74,17 +74,15 @@ export default class InviteTenantUserService implements IInviteUserService {
|
||||
|
||||
/**
|
||||
* Re-send user invite.
|
||||
* @param {number} tenantId -
|
||||
* @param {string} email -
|
||||
* @param {number} tenantId -
|
||||
* @param {string} email -
|
||||
* @return {Promise<{ invite: IUserInvite }>}
|
||||
*/
|
||||
public async resendInvite(
|
||||
tenantId: number,
|
||||
userId: number,
|
||||
authorizedUser: ISystemUser
|
||||
): Promise<{
|
||||
user: ITenantUser;
|
||||
}> {
|
||||
): Promise<{ user: ITenantUser }> {
|
||||
// Retrieve the user by id or throw not found service error.
|
||||
const user = await this.getUserByIdOrThrowError(tenantId, userId);
|
||||
|
||||
|
||||
@@ -419,6 +419,54 @@ export const parseDate = (date: string) => {
|
||||
return date ? moment(date).utcOffset(0).format('YYYY-MM-DD') : '';
|
||||
};
|
||||
|
||||
const nestedArrayToFlatten = (
|
||||
collection,
|
||||
property = 'children',
|
||||
parseItem = (a, level) => a,
|
||||
level = 1
|
||||
) => {
|
||||
const parseObject = (obj) =>
|
||||
parseItem(
|
||||
{
|
||||
..._.omit(obj, [property]),
|
||||
},
|
||||
level
|
||||
);
|
||||
|
||||
return collection.reduce((items, currentValue, index) => {
|
||||
let localItems = [...items];
|
||||
const parsedItem = parseObject(currentValue, level);
|
||||
localItems.push(parsedItem);
|
||||
|
||||
if (Array.isArray(currentValue[property])) {
|
||||
const flattenArray = nestedArrayToFlatten(
|
||||
currentValue[property],
|
||||
property,
|
||||
parseItem,
|
||||
level + 1
|
||||
);
|
||||
localItems = _.concat(localItems, flattenArray);
|
||||
}
|
||||
return localItems;
|
||||
}, []);
|
||||
};
|
||||
|
||||
const assocDepthLevelToObjectTree = (
|
||||
objects,
|
||||
level = 1,
|
||||
propertyName = 'level'
|
||||
) => {
|
||||
for (let i = 0; i < objects.length; i++) {
|
||||
const object = objects[i];
|
||||
object[propertyName] = level;
|
||||
|
||||
if (object.children) {
|
||||
assocDepthLevelToObjectTree(object.children, level + 1, propertyName);
|
||||
}
|
||||
}
|
||||
return objects;
|
||||
};
|
||||
|
||||
export {
|
||||
templateRender,
|
||||
accumSum,
|
||||
@@ -449,4 +497,6 @@ export {
|
||||
dateRangeFromToCollection,
|
||||
transformToMapKeyValue,
|
||||
mergeObjectsBykey,
|
||||
nestedArrayToFlatten,
|
||||
assocDepthLevelToObjectTree,
|
||||
};
|
||||
|
||||
6
packages/webapp/package-lock.json
generated
6
packages/webapp/package-lock.json
generated
@@ -1227,9 +1227,9 @@
|
||||
}
|
||||
},
|
||||
"@blueprintjs-formik/select": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@blueprintjs-formik/select/-/select-0.1.5.tgz",
|
||||
"integrity": "sha512-EqGbuoiS1VrWpzjd39uVhBAmfVobdpgqalGcpODyGA+XAYoft1UU12yzTzrEOwBZpQKiC12UQwekUPspYBsVKA==",
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@blueprintjs-formik/select/-/select-0.2.3.tgz",
|
||||
"integrity": "sha512-j/zkX0B9wgtoHgK6Z/rlowB7F7zemrAajBU+d3caCoEYMMqwAI0XA++GytqrIhv5fEGjkZ1hkxS9j8eqX8vtjA==",
|
||||
"requires": {
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.keyby": "^4.6.0",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"dependencies": {
|
||||
"@blueprintjs-formik/core": "^0.2.1",
|
||||
"@blueprintjs-formik/datetime": "^0.3.4",
|
||||
"@blueprintjs-formik/select": "^0.1.4",
|
||||
"@blueprintjs-formik/select": "^0.2.3",
|
||||
"@blueprintjs/core": "^3.50.2",
|
||||
"@blueprintjs/datetime": "^3.23.12",
|
||||
"@blueprintjs/popover2": "^0.11.1",
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { MenuItem } from '@blueprintjs/core';
|
||||
import { FMultiSelect } from '../Forms';
|
||||
import classNames from 'classnames';
|
||||
import { Classes } from '@blueprintjs/popover2';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} query
|
||||
* @param {*} account
|
||||
* @param {*} _index
|
||||
* @param {*} exactMatch
|
||||
* @returns
|
||||
*/
|
||||
const accountItemPredicate = (query, account, _index, exactMatch) => {
|
||||
const normalizedTitle = account.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return `${account.code}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} account
|
||||
* @param {*} param1
|
||||
* @returns
|
||||
*/
|
||||
const accountItemRenderer = (
|
||||
account,
|
||||
{ handleClick, modifiers, query },
|
||||
{ isSelected },
|
||||
) => {
|
||||
return (
|
||||
<MenuItem
|
||||
icon={isSelected ? 'tick' : 'blank'}
|
||||
text={account.name}
|
||||
label={account.code}
|
||||
key={account.id}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const accountSelectProps = {
|
||||
itemPredicate: accountItemPredicate,
|
||||
itemRenderer: accountItemRenderer,
|
||||
valueAccessor: (item) => item.id,
|
||||
labelAccessor: (item) => item.code,
|
||||
tagRenderer: (item) => item.name,
|
||||
};
|
||||
|
||||
/**
|
||||
* branches mulit select.
|
||||
* @param {*} param0
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export function AccountMultiSelect({ accounts, ...rest }) {
|
||||
return (
|
||||
<FMultiSelect
|
||||
items={accounts}
|
||||
popoverProps={{
|
||||
minimal: true,
|
||||
}}
|
||||
{...accountSelectProps}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,31 +1,97 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { MenuItem } from '@blueprintjs/core';
|
||||
import { MultiSelect } from '../MultiSelectTaggable';
|
||||
import { FMultiSelect } from '../Forms';
|
||||
import { accountPredicate } from './_components';
|
||||
import { MenuItemNestedText } from '../Menu';
|
||||
import { usePreprocessingAccounts } from './_hooks';
|
||||
|
||||
export function AccountsMultiSelect({ ...multiSelectProps }) {
|
||||
// Create new account renderer.
|
||||
const createNewItemRenderer = (query, active, handleClick) => {
|
||||
return (
|
||||
<MultiSelect
|
||||
itemRenderer={(
|
||||
item,
|
||||
{ active, selected, handleClick, modifiers, query },
|
||||
) => {
|
||||
return (
|
||||
<MenuItem
|
||||
active={active}
|
||||
icon={selected ? 'tick' : 'blank'}
|
||||
text={item.name}
|
||||
label={item.code}
|
||||
key={item.id}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
<MenuItem
|
||||
icon="add"
|
||||
text={intl.get('list.create', { value: `"${query}"` })}
|
||||
active={active}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Default account item renderer of the list.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
const accountRenderer = (
|
||||
item,
|
||||
{ handleClick, modifiers, query },
|
||||
{ isSelected },
|
||||
) => {
|
||||
if (!modifiers.matchesPredicate) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<MenuItem
|
||||
active={modifiers.active}
|
||||
disabled={modifiers.disabled}
|
||||
text={<MenuItemNestedText level={item.account_level} text={item.name} />}
|
||||
key={item.id}
|
||||
onClick={handleClick}
|
||||
icon={isSelected ? 'tick' : 'blank'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// Create new item from the given query string.
|
||||
const createNewItemFromQuery = (name) => ({ name });
|
||||
|
||||
/**
|
||||
* Accounts multi-select field binded with Formik form.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export function AccountsMultiSelect({
|
||||
items,
|
||||
allowCreate,
|
||||
|
||||
filterByRootTypes,
|
||||
filterByParentTypes,
|
||||
filterByTypes,
|
||||
filterByNormal,
|
||||
|
||||
...rest
|
||||
}) {
|
||||
// Filters accounts based on filter props.
|
||||
const filteredAccounts = usePreprocessingAccounts(items, {
|
||||
filterByParentTypes,
|
||||
filterByTypes,
|
||||
filterByNormal,
|
||||
filterByRootTypes,
|
||||
});
|
||||
// Maybe inject new item props to select component.
|
||||
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
|
||||
const maybeCreateNewItemFromQuery = allowCreate
|
||||
? createNewItemFromQuery
|
||||
: null;
|
||||
|
||||
// Handles the create item click.
|
||||
const handleCreateItemClick = () => {
|
||||
openDialog(DialogsName.AccountForm);
|
||||
};
|
||||
|
||||
return (
|
||||
<FMultiSelect
|
||||
items={filteredAccounts}
|
||||
valueAccessor={'id'}
|
||||
textAccessor={'name'}
|
||||
labelAccessor={'code'}
|
||||
tagAccessor={'name'}
|
||||
popoverProps={{ minimal: true }}
|
||||
fill={true}
|
||||
tagRenderer={(item) => item.name}
|
||||
resetOnSelect={true}
|
||||
{...multiSelectProps}
|
||||
itemPredicate={accountPredicate}
|
||||
itemRenderer={accountRenderer}
|
||||
createNewItemRenderer={maybeCreateNewItemRenderer}
|
||||
createNewItemFromQuery={maybeCreateNewItemFromQuery}
|
||||
onCreateItemSelect={handleCreateItemClick}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
101
packages/webapp/src/components/Accounts/AccountsSelect.tsx
Normal file
101
packages/webapp/src/components/Accounts/AccountsSelect.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
import intl from 'react-intl-universal';
|
||||
import { MenuItem } from '@blueprintjs/core';
|
||||
import { MenuItemNestedText, FSelect } from '@/components';
|
||||
import { accountPredicate } from './_components';
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import { usePreprocessingAccounts } from './_hooks';
|
||||
|
||||
// Create new account renderer.
|
||||
const createNewItemRenderer = (query, active, handleClick) => {
|
||||
return (
|
||||
<MenuItem
|
||||
icon="add"
|
||||
text={intl.get('list.create', { value: `"${query}"` })}
|
||||
active={active}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// Create new item from the given query string.
|
||||
const createNewItemFromQuery = (name) => ({ name });
|
||||
|
||||
/**
|
||||
* Default account item renderer.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
const accountRenderer = (item, { handleClick, modifiers, query }) => {
|
||||
if (!modifiers.matchesPredicate) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<MenuItem
|
||||
active={modifiers.active}
|
||||
disabled={modifiers.disabled}
|
||||
label={item.code}
|
||||
key={item.id}
|
||||
text={<MenuItemNestedText level={item.account_level} text={item.name} />}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Accounts select field binded with Formik form.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
function AccountsSelectRoot({
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #ownProps
|
||||
items,
|
||||
allowCreate,
|
||||
|
||||
filterByParentTypes,
|
||||
filterByTypes,
|
||||
filterByNormal,
|
||||
filterByRootTypes,
|
||||
|
||||
...restProps
|
||||
}) {
|
||||
// Filters accounts based on filter props.
|
||||
const filteredAccounts = usePreprocessingAccounts(items, {
|
||||
filterByParentTypes,
|
||||
filterByTypes,
|
||||
filterByNormal,
|
||||
filterByRootTypes,
|
||||
});
|
||||
// Maybe inject new item props to select component.
|
||||
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
|
||||
const maybeCreateNewItemFromQuery = allowCreate
|
||||
? createNewItemFromQuery
|
||||
: null;
|
||||
|
||||
// Handles the create item click.
|
||||
const handleCreateItemClick = () => {
|
||||
openDialog(DialogsName.AccountForm);
|
||||
};
|
||||
|
||||
return (
|
||||
<FSelect
|
||||
items={filteredAccounts}
|
||||
textAccessor={'name'}
|
||||
labelAccessor={'code'}
|
||||
valueAccessor={'id'}
|
||||
popoverProps={{ minimal: true, usePortal: true, inline: false }}
|
||||
itemPredicate={accountPredicate}
|
||||
itemRenderer={accountRenderer}
|
||||
createNewItemRenderer={maybeCreateNewItemRenderer}
|
||||
createNewItemFromQuery={maybeCreateNewItemFromQuery}
|
||||
onCreateItemSelect={handleCreateItemClick}
|
||||
{...restProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const AccountsSelect = R.compose(withDialogActions)(AccountsSelectRoot);
|
||||
@@ -1,177 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import React, { useCallback, useState, useEffect, useMemo } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import classNames from 'classnames';
|
||||
import { MenuItem, Button } from '@blueprintjs/core';
|
||||
import { Select } from '@blueprintjs/select';
|
||||
import * as R from 'ramda';
|
||||
|
||||
import { MenuItemNestedText, FormattedMessage as T } from '@/components';
|
||||
import { nestedArrayToflatten, filterAccountsByQuery } from '@/utils';
|
||||
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
|
||||
// Create new account renderer.
|
||||
const createNewItemRenderer = (query, active, handleClick) => {
|
||||
return (
|
||||
<MenuItem
|
||||
icon="add"
|
||||
text={intl.get('list.create', { value: `"${query}"` })}
|
||||
active={active}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// Create new item from the given query string.
|
||||
const createNewItemFromQuery = (name) => {
|
||||
return {
|
||||
name,
|
||||
};
|
||||
};
|
||||
|
||||
// Filters accounts items.
|
||||
const filterAccountsPredicater = (query, account, _index, exactMatch) => {
|
||||
const normalizedTitle = account.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return `${account.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Accounts select list.
|
||||
*/
|
||||
function AccountsSelectListRoot({
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #ownProps
|
||||
accounts,
|
||||
initialAccountId,
|
||||
selectedAccountId,
|
||||
defaultSelectText = 'Select account',
|
||||
onAccountSelected,
|
||||
disabled = false,
|
||||
popoverFill = false,
|
||||
|
||||
filterByParentTypes,
|
||||
filterByTypes,
|
||||
filterByNormal,
|
||||
filterByRootTypes,
|
||||
|
||||
allowCreate,
|
||||
|
||||
buttonProps = {},
|
||||
}) {
|
||||
const flattenAccounts = useMemo(
|
||||
() => nestedArrayToflatten(accounts),
|
||||
[accounts],
|
||||
);
|
||||
|
||||
// Filters accounts based on filter props.
|
||||
const filteredAccounts = useMemo(() => {
|
||||
let filteredAccounts = filterAccountsByQuery(flattenAccounts, {
|
||||
filterByRootTypes,
|
||||
filterByParentTypes,
|
||||
filterByTypes,
|
||||
filterByNormal,
|
||||
});
|
||||
return filteredAccounts;
|
||||
}, [
|
||||
flattenAccounts,
|
||||
filterByRootTypes,
|
||||
filterByParentTypes,
|
||||
filterByTypes,
|
||||
filterByNormal,
|
||||
]);
|
||||
|
||||
// Find initial account object to set it as default account in initial render.
|
||||
const initialAccount = useMemo(
|
||||
() => filteredAccounts.find((a) => a.id === initialAccountId),
|
||||
[initialAccountId, filteredAccounts],
|
||||
);
|
||||
|
||||
// Select account item.
|
||||
const [selectedAccount, setSelectedAccount] = useState(
|
||||
initialAccount || null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof selectedAccountId !== 'undefined') {
|
||||
const account = selectedAccountId
|
||||
? filteredAccounts.find((a) => a.id === selectedAccountId)
|
||||
: null;
|
||||
setSelectedAccount(account);
|
||||
}
|
||||
}, [selectedAccountId, filteredAccounts, setSelectedAccount]);
|
||||
|
||||
// Account item of select accounts field.
|
||||
const accountItem = useCallback((item, { handleClick, modifiers, query }) => {
|
||||
return (
|
||||
<MenuItem
|
||||
text={<MenuItemNestedText level={item.level} text={item.name} />}
|
||||
label={item.code}
|
||||
key={item.id}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
}, []);
|
||||
|
||||
// Handle the account item select.
|
||||
const handleAccountSelect = useCallback(
|
||||
(account) => {
|
||||
if (account.id) {
|
||||
setSelectedAccount({ ...account });
|
||||
onAccountSelected && onAccountSelected(account);
|
||||
} else {
|
||||
openDialog(DialogsName.AccountForm);
|
||||
}
|
||||
},
|
||||
[setSelectedAccount, onAccountSelected, openDialog],
|
||||
);
|
||||
|
||||
// Maybe inject new item props to select component.
|
||||
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
|
||||
const maybeCreateNewItemFromQuery = allowCreate
|
||||
? createNewItemFromQuery
|
||||
: null;
|
||||
|
||||
return (
|
||||
<Select
|
||||
items={filteredAccounts}
|
||||
noResults={<MenuItem disabled={true} text={<T id={'no_accounts'} />} />}
|
||||
itemRenderer={accountItem}
|
||||
itemPredicate={filterAccountsPredicater}
|
||||
popoverProps={{
|
||||
minimal: true,
|
||||
usePortal: !popoverFill,
|
||||
inline: popoverFill,
|
||||
}}
|
||||
filterable={true}
|
||||
onItemSelect={handleAccountSelect}
|
||||
disabled={disabled}
|
||||
className={classNames('form-group--select-list', {
|
||||
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
|
||||
})}
|
||||
createNewItemRenderer={maybeCreateNewItemRenderer}
|
||||
createNewItemFromQuery={maybeCreateNewItemFromQuery}
|
||||
>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
text={selectedAccount ? selectedAccount.name : defaultSelectText}
|
||||
{...buttonProps}
|
||||
/>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
export const AccountsSelectList = R.compose(withDialogActions)(
|
||||
AccountsSelectListRoot,
|
||||
);
|
||||
@@ -1,49 +1,15 @@
|
||||
// @ts-nocheck
|
||||
import React, { useCallback } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { ListSelect } from '@/components';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
|
||||
export function AccountsTypesSelect({
|
||||
accountsTypes,
|
||||
selectedTypeId,
|
||||
defaultSelectText = 'Select account type',
|
||||
onTypeSelected,
|
||||
disabled = false,
|
||||
popoverFill = false,
|
||||
...restProps
|
||||
}) {
|
||||
// Filters accounts types items.
|
||||
const filterAccountTypeItems = (query, accountType, _index, exactMatch) => {
|
||||
const normalizedTitle = accountType.label.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return normalizedTitle.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Handle item selected.
|
||||
const handleItemSelected = (accountType) => {
|
||||
onTypeSelected && onTypeSelected(accountType);
|
||||
};
|
||||
import React from 'react';
|
||||
import { FSelect } from '@/components/Forms';
|
||||
|
||||
export function AccountsTypesSelect({ ...props }) {
|
||||
return (
|
||||
<ListSelect
|
||||
items={accountsTypes}
|
||||
selectedItemProp={'key'}
|
||||
selectedItem={selectedTypeId}
|
||||
textProp={'label'}
|
||||
defaultText={defaultSelectText}
|
||||
onItemSelect={handleItemSelected}
|
||||
itemPredicate={filterAccountTypeItems}
|
||||
disabled={disabled}
|
||||
className={classNames('form-group--select-list', {
|
||||
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
|
||||
})}
|
||||
{...restProps}
|
||||
<FSelect
|
||||
valueAccessor={'key'}
|
||||
labelAccessor={'label'}
|
||||
textAccessor={'label'}
|
||||
placeholder={'Select an account...'}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
14
packages/webapp/src/components/Accounts/_components.tsx
Normal file
14
packages/webapp/src/components/Accounts/_components.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
// Filters accounts items.
|
||||
export const accountPredicate = (query, account, _index, exactMatch) => {
|
||||
const normalizedTitle = account.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return `${account.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
};
|
||||
36
packages/webapp/src/components/Accounts/_hooks.ts
Normal file
36
packages/webapp/src/components/Accounts/_hooks.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useMemo } from 'react';
|
||||
import { filterAccountsByQuery, nestedArrayToflatten } from '@/utils';
|
||||
|
||||
interface PreprocessingAccountsOptions {
|
||||
filterByRootTypes: string[];
|
||||
filterByParentTypes: string[];
|
||||
filterByTypes: string[];
|
||||
filterByNormal: string[];
|
||||
}
|
||||
|
||||
export const usePreprocessingAccounts = (
|
||||
items: any,
|
||||
{
|
||||
filterByRootTypes,
|
||||
filterByParentTypes,
|
||||
filterByTypes,
|
||||
filterByNormal,
|
||||
}: PreprocessingAccountsOptions,
|
||||
) => {
|
||||
return useMemo(() => {
|
||||
const flattenAccounts = nestedArrayToflatten(items);
|
||||
const filteredAccounts = filterAccountsByQuery(flattenAccounts, {
|
||||
filterByRootTypes,
|
||||
filterByParentTypes,
|
||||
filterByTypes,
|
||||
filterByNormal,
|
||||
});
|
||||
return filteredAccounts;
|
||||
}, [
|
||||
items,
|
||||
filterByRootTypes,
|
||||
filterByParentTypes,
|
||||
filterByTypes,
|
||||
filterByNormal,
|
||||
]);
|
||||
};
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from './AccountMultiSelect';
|
||||
export * from './AccountsSelect';
|
||||
export * from './AccountsMultiSelect';
|
||||
export * from './AccountsSelectList';
|
||||
export * from './AccountsSuggestField';
|
||||
export * from './AccountsTypesSelect';
|
||||
|
||||
@@ -77,11 +77,13 @@ export function BankAccount({
|
||||
</BankAccountHeader>
|
||||
|
||||
<BankAccountMeta>
|
||||
<BankAccountMetaLine
|
||||
title={intl.get('cash_flow.label_account_transcations')}
|
||||
value={2}
|
||||
className={clsx({ [Classes.SKELETON]: loading })}
|
||||
/>
|
||||
{false && (
|
||||
<BankAccountMetaLine
|
||||
title={intl.get('cash_flow.transactions_for_review')}
|
||||
value={'0'}
|
||||
className={clsx({ [Classes.SKELETON]: loading })}
|
||||
/>
|
||||
)}
|
||||
<BankAccountMetaLine
|
||||
title={updatedBeforeText}
|
||||
className={clsx({ [Classes.SKELETON]: loading })}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { MenuItem, Button } from '@blueprintjs/core';
|
||||
import { FSelect } from '../Forms';
|
||||
|
||||
/**
|
||||
@@ -28,30 +27,6 @@ const currencyItemPredicate = (query, currency, _index, exactMatch) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {*} currency
|
||||
* @returns
|
||||
*/
|
||||
const currencyItemRenderer = (currency, { handleClick, modifiers, query }) => {
|
||||
return (
|
||||
<MenuItem
|
||||
active={modifiers.active}
|
||||
disabled={modifiers.disabled}
|
||||
text={currency.currency_name}
|
||||
label={currency.currency_code.toString()}
|
||||
key={currency.id}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const currencySelectProps = {
|
||||
itemPredicate: currencyItemPredicate,
|
||||
itemRenderer: currencyItemRenderer,
|
||||
valueAccessor: 'currency_code',
|
||||
labelAccessor: 'currency_code',
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} currencies
|
||||
@@ -60,18 +35,13 @@ const currencySelectProps = {
|
||||
export function CurrencySelect({ currencies, ...rest }) {
|
||||
return (
|
||||
<FSelect
|
||||
{...currencySelectProps}
|
||||
itemPredicate={currencyItemPredicate}
|
||||
valueAccessor={'currency_code'}
|
||||
textAccessor={'currency_name'}
|
||||
labelAccessor={'currency_code'}
|
||||
{...rest}
|
||||
items={currencies}
|
||||
input={CurrnecySelectButton}
|
||||
placeholder={intl.get('select_currency_code')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} label
|
||||
* @returns
|
||||
*/
|
||||
function CurrnecySelectButton({ label }) {
|
||||
return <Button text={label ? label : intl.get('select_currency_code')} />;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import {
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
@@ -9,8 +10,9 @@ import {
|
||||
EditableText,
|
||||
TextArea,
|
||||
} from '@blueprintjs-formik/core';
|
||||
import { Select, MultiSelect } from '@blueprintjs-formik/select';
|
||||
import { MultiSelect } from '@blueprintjs-formik/select';
|
||||
import { DateInput } from '@blueprintjs-formik/datetime';
|
||||
import { FSelect } from './Select';
|
||||
|
||||
export {
|
||||
FormGroup as FFormGroup,
|
||||
@@ -19,7 +21,7 @@ export {
|
||||
Checkbox as FCheckbox,
|
||||
RadioGroup as FRadioGroup,
|
||||
Switch as FSwitch,
|
||||
Select as FSelect,
|
||||
FSelect,
|
||||
MultiSelect as FMultiSelect,
|
||||
EditableText as FEditableText,
|
||||
TextArea as FTextArea,
|
||||
|
||||
58
packages/webapp/src/components/Forms/Select.tsx
Normal file
58
packages/webapp/src/components/Forms/Select.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Button } from '@blueprintjs/core';
|
||||
import { Select } from '@blueprintjs-formik/select';
|
||||
import styled from 'styled-components';
|
||||
import clsx from 'classnames';
|
||||
|
||||
export function FSelect({ ...props }) {
|
||||
const input = ({ activeItem, text, label, value }) => {
|
||||
return (
|
||||
<SelectButton
|
||||
text={text || props.placeholder || 'Select an item ...'}
|
||||
disabled={props.disabled || false}
|
||||
{...props.buttonProps}
|
||||
className={clsx({ 'is-selected': !!text }, props.className)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
return <Select input={input} {...props} fill={true} />;
|
||||
}
|
||||
|
||||
const SelectButton = styled(Button)`
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 transparent;
|
||||
border: 1px solid #ced4da;
|
||||
position: relative;
|
||||
padding-right: 30px;
|
||||
|
||||
&:not(.is-selected):not([class*='bp3-intent-']):not(.bp3-minimal) {
|
||||
color: #5c7080;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 5px solid #8d8d8d;
|
||||
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
margin-top: -2px;
|
||||
margin-right: 12px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
&:not([class*='bp3-intent-']) {
|
||||
&,
|
||||
&:hover {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
.bp3-intent-danger & {
|
||||
border-color: #db3737;
|
||||
}
|
||||
`;
|
||||
@@ -1,32 +1,29 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Form, FastField, Field, ErrorMessage, useFormikContext } from 'formik';
|
||||
import {
|
||||
Button,
|
||||
Classes,
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Intent,
|
||||
TextArea,
|
||||
Checkbox,
|
||||
} from '@blueprintjs/core';
|
||||
import { Form, useFormikContext } from 'formik';
|
||||
import { Button, Classes, FormGroup, Intent } from '@blueprintjs/core';
|
||||
import {
|
||||
If,
|
||||
FieldRequiredHint,
|
||||
Hint,
|
||||
AccountsSelectList,
|
||||
AccountsSelect,
|
||||
AccountsTypesSelect,
|
||||
CurrencySelect,
|
||||
FormattedMessage as T,
|
||||
FFormGroup,
|
||||
FInputGroup,
|
||||
FCheckbox,
|
||||
FTextArea,
|
||||
} from '@/components';
|
||||
import withAccounts from '@/containers/Accounts/withAccounts';
|
||||
|
||||
import { inputIntent, compose } from '@/utils';
|
||||
import { useAutofocus } from '@/hooks';
|
||||
import { FOREIGN_CURRENCY_ACCOUNTS } from '@/constants/accountTypes';
|
||||
|
||||
import { useAutofocus } from '@/hooks';
|
||||
import { useAccountDialogContext } from './AccountDialogProvider';
|
||||
|
||||
import { parentAccountShouldUpdate } from './utils';
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Account form dialogs fields.
|
||||
@@ -36,7 +33,7 @@ function AccountFormDialogFields({
|
||||
onClose,
|
||||
action,
|
||||
}) {
|
||||
const { values, isSubmitting } = useFormikContext();
|
||||
const { values, isSubmitting, setFieldValue } = useFormikContext();
|
||||
const accountNameFieldRef = useAutofocus();
|
||||
|
||||
// Account form context.
|
||||
@@ -46,146 +43,120 @@ function AccountFormDialogFields({
|
||||
return (
|
||||
<Form>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Field name={'account_type'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'account_type'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--account-type', Classes.FILL)}
|
||||
inline={true}
|
||||
helperText={<ErrorMessage name="account_type" />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<AccountsTypesSelect
|
||||
accountsTypes={accountsTypes}
|
||||
selectedTypeId={value}
|
||||
defaultSelectText={<T id={'select_account_type'} />}
|
||||
onTypeSelected={(accountType) => {
|
||||
form.setFieldValue('account_type', accountType.key);
|
||||
form.setFieldValue('currency_code', '');
|
||||
}}
|
||||
disabled={fieldsDisabled.accountType}
|
||||
popoverProps={{ minimal: true }}
|
||||
popoverFill={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<FastField name={'name'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'account_name'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={'form-group--account-name'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="name" />}
|
||||
inline={true}
|
||||
>
|
||||
<InputGroup
|
||||
medium={true}
|
||||
inputRef={(ref) => (accountNameFieldRef.current = ref)}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'code'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'account_code'} />}
|
||||
className={'form-group--account-code'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="code" />}
|
||||
inline={true}
|
||||
labelInfo={<Hint content={<T id="account_code_hint" />} />}
|
||||
>
|
||||
<InputGroup medium={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<Field name={'subaccount'} type={'checkbox'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={' '}
|
||||
className={classNames('form-group--subaccount')}
|
||||
intent={inputIntent({ error, touched })}
|
||||
inline={true}
|
||||
>
|
||||
<Checkbox
|
||||
inline={true}
|
||||
label={<T id={'sub_account'} />}
|
||||
name={'subaccount'}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<FastField
|
||||
name={'parent_account_id'}
|
||||
shouldUpdate={parentAccountShouldUpdate}
|
||||
<FFormGroup
|
||||
inline={true}
|
||||
label={<T id={'account_type'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
name={'account_type'}
|
||||
fastField={true}
|
||||
>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={<T id={'parent_account'} />}
|
||||
className={classNames('form-group--parent-account', Classes.FILL)}
|
||||
inline={true}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="parent_account_id" />}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={(account) => {
|
||||
setFieldValue('parent_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_parent_account'} />}
|
||||
selectedAccountId={value}
|
||||
popoverFill={true}
|
||||
filterByTypes={values.account_type}
|
||||
disabled={!values.subaccount}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<AccountsTypesSelect
|
||||
name={'account_type'}
|
||||
items={accountsTypes}
|
||||
onItemSelect={(accountType) => {
|
||||
setFieldValue('account_type', accountType.key);
|
||||
setFieldValue('currency_code', '');
|
||||
}}
|
||||
disabled={fieldsDisabled.accountType}
|
||||
popoverProps={{ minimal: true }}
|
||||
fastField={true}
|
||||
fill={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'name'}
|
||||
label={<T id={'account_name'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
inline={true}
|
||||
fastField={true}
|
||||
>
|
||||
<FInputGroup
|
||||
medium={true}
|
||||
inputRef={(ref) => (accountNameFieldRef.current = ref)}
|
||||
name={'name'}
|
||||
fastField={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
label={<T id={'account_code'} />}
|
||||
name={'code'}
|
||||
labelInfo={<Hint content={<T id="account_code_hint" />} />}
|
||||
inline={true}
|
||||
fastField={true}
|
||||
>
|
||||
<FInputGroup medium={true} name={'code'} fastField={true} />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
label={' '}
|
||||
name={'subaccount'}
|
||||
inline={true}
|
||||
fastField={true}
|
||||
>
|
||||
<FCheckbox
|
||||
inline={true}
|
||||
label={<T id={'sub_account'} />}
|
||||
name={'subaccount'}
|
||||
fastField={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{values.subaccount && (
|
||||
<FFormGroup
|
||||
name={'parent_account_id'}
|
||||
shouldUpdate={parentAccountShouldUpdate}
|
||||
label={<T id={'parent_account'} />}
|
||||
inline={true}
|
||||
fastField={true}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'parent_account_id'}
|
||||
items={accounts}
|
||||
shouldUpdate={parentAccountShouldUpdate}
|
||||
placeholder={<T id={'select_parent_account'} />}
|
||||
filterByTypes={values.account_type}
|
||||
buttonProps={{ disabled: !values.subaccount }}
|
||||
fastField={true}
|
||||
fill={true}
|
||||
allowCreate={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
)}
|
||||
|
||||
<If condition={FOREIGN_CURRENCY_ACCOUNTS.includes(values.account_type)}>
|
||||
{/*------------ Currency -----------*/}
|
||||
<FastField name={'currency_code'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'currency'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
inline={true}
|
||||
>
|
||||
<CurrencySelect
|
||||
name={'currency_code'}
|
||||
currencies={currencies}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FFormGroup
|
||||
label={<T id={'currency'} />}
|
||||
name={'currency_code'}
|
||||
inline={true}
|
||||
fastField={true}
|
||||
>
|
||||
<CurrencySelect
|
||||
name={'currency_code'}
|
||||
currencies={currencies}
|
||||
popoverProps={{ minimal: true }}
|
||||
fastField={true}
|
||||
fill={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</If>
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
className={'form-group--description'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'description'} />}
|
||||
inline={true}
|
||||
>
|
||||
<TextArea growVertically={true} height={280} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FFormGroup
|
||||
label={<T id={'description'} />}
|
||||
name={'description'}
|
||||
inline={true}
|
||||
fastField={true}
|
||||
>
|
||||
<FTextArea
|
||||
name={'description'}
|
||||
growVertically={true}
|
||||
height={280}
|
||||
fill={true}
|
||||
fastField={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</div>
|
||||
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
|
||||
@@ -16,7 +16,8 @@ import { customersFieldShouldUpdate, accountsFieldShouldUpdate } from './utils';
|
||||
import {
|
||||
CurrencySelectList,
|
||||
CustomerSelectField,
|
||||
AccountsSelectList,
|
||||
FFormGroup,
|
||||
AccountsSelect,
|
||||
FieldRequiredHint,
|
||||
Hint,
|
||||
} from '@/components';
|
||||
@@ -54,37 +55,26 @@ export default function ExpenseFormHeader() {
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField
|
||||
<FFormGroup
|
||||
name={'payment_account_id'}
|
||||
accounts={accounts}
|
||||
items={accounts}
|
||||
label={<T id={'payment_account'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
inline={true}
|
||||
fastField={true}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'payment_account'} />}
|
||||
className={classNames(
|
||||
'form-group--payment_account',
|
||||
'form-group--select-list',
|
||||
Classes.FILL,
|
||||
)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'payment_account_id'} />}
|
||||
inline={true}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('payment_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
selectedAccountId={value}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.CURRENT_ASSET]}
|
||||
allowCreate={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<AccountsSelect
|
||||
name={'payment_account_id'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_payment_account'} />}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.CURRENT_ASSET]}
|
||||
allowCreate={true}
|
||||
fastField={true}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
fill={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
<FastField name={'currency_code'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
|
||||
@@ -114,7 +114,7 @@ export const customersFieldShouldUpdate = (newProps, oldProps) => {
|
||||
*/
|
||||
export const accountsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.items !== oldProps.items ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
import { Classes } from '@blueprintjs/core';
|
||||
|
||||
import {
|
||||
AccountMultiSelect,
|
||||
AccountsMultiSelect,
|
||||
Row,
|
||||
Col,
|
||||
FormattedMessage as T,
|
||||
@@ -54,7 +54,7 @@ function GLHeaderGeneralPaneContent() {
|
||||
name={'accountsIds'}
|
||||
className={Classes.FILL}
|
||||
>
|
||||
<AccountMultiSelect name="accountsIds" accounts={accounts} />
|
||||
<AccountsMultiSelect name="accountsIds" items={accounts} />
|
||||
</FFormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -9,15 +9,15 @@ import {
|
||||
ControlGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import {
|
||||
AccountsSelectList,
|
||||
AccountsSelect,
|
||||
MoneyInputGroup,
|
||||
Col,
|
||||
Row,
|
||||
Hint,
|
||||
InputPrependText,
|
||||
FFormGroup,
|
||||
} from '@/components';
|
||||
import { FormattedMessage as T } from '@/components';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { useItemFormContext } from './ItemFormProvider';
|
||||
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
||||
@@ -91,42 +91,29 @@ function ItemFormBody({ organization: { base_currency } }) {
|
||||
</FastField>
|
||||
|
||||
{/*------------- Selling account ------------- */}
|
||||
<FastField
|
||||
<FFormGroup
|
||||
label={<T id={'account'} />}
|
||||
name={'sell_account_id'}
|
||||
labelInfo={
|
||||
<Hint content={<T id={'item.field.sell_account.hint'} />} />
|
||||
}
|
||||
inline={true}
|
||||
items={accounts}
|
||||
sellable={values.sellable}
|
||||
accounts={accounts}
|
||||
shouldUpdate={sellAccountFieldShouldUpdate}
|
||||
fastField={true}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'account'} />}
|
||||
labelInfo={
|
||||
<Hint content={<T id={'item.field.sell_account.hint'} />} />
|
||||
}
|
||||
inline={true}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="sell_account_id" />}
|
||||
className={classNames(
|
||||
'form-group--sell-account',
|
||||
'form-group--select-list',
|
||||
Classes.FILL,
|
||||
)}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('sell_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_account'} />}
|
||||
selectedAccountId={value}
|
||||
disabled={!form.values.sellable}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]}
|
||||
popoverFill={true}
|
||||
allowCreate={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<AccountsSelect
|
||||
name={'sell_account_id'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_account'} />}
|
||||
disabled={!values.sellable}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]}
|
||||
fill={true}
|
||||
allowCreate={true}
|
||||
fastField={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
<FastField
|
||||
name={'sell_description'}
|
||||
@@ -200,42 +187,31 @@ function ItemFormBody({ organization: { base_currency } }) {
|
||||
</FastField>
|
||||
|
||||
{/*------------- Cost account ------------- */}
|
||||
<FastField
|
||||
<FFormGroup
|
||||
name={'cost_account_id'}
|
||||
purchasable={values.purchasable}
|
||||
accounts={accounts}
|
||||
items={accounts}
|
||||
shouldUpdate={costAccountFieldShouldUpdate}
|
||||
label={<T id={'account'} />}
|
||||
labelInfo={
|
||||
<Hint content={<T id={'item.field.cost_account.hint'} />} />
|
||||
}
|
||||
inline={true}
|
||||
fastField={true}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'account'} />}
|
||||
labelInfo={
|
||||
<Hint content={<T id={'item.field.cost_account.hint'} />} />
|
||||
}
|
||||
inline={true}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="cost_account_id" />}
|
||||
className={classNames(
|
||||
'form-group--cost-account',
|
||||
'form-group--select-list',
|
||||
Classes.FILL,
|
||||
)}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('cost_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_account'} />}
|
||||
selectedAccountId={value}
|
||||
disabled={!form.values.purchasable}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.EXPENSE]}
|
||||
popoverFill={true}
|
||||
allowCreate={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<AccountsSelect
|
||||
name={'cost_account_id'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_account'} />}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.EXPENSE]}
|
||||
popoverFill={true}
|
||||
allowCreate={true}
|
||||
fastField={true}
|
||||
disabled={!values.purchasable}
|
||||
purchasable={values.purchasable}
|
||||
shouldUpdate={costAccountFieldShouldUpdate}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
<FastField
|
||||
name={'purchase_description'}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import { FormGroup } from '@blueprintjs/core';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import {
|
||||
AccountsSelectList,
|
||||
AccountsSelect,
|
||||
FFormGroup,
|
||||
FormattedMessage as T,
|
||||
Col,
|
||||
Row,
|
||||
} from '@/components';
|
||||
import classNames from 'classnames';
|
||||
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
||||
|
||||
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
||||
import { accountsFieldShouldUpdate } from './utils';
|
||||
import { compose, inputIntent } from '@/utils';
|
||||
import { ACCOUNT_TYPE } from '@/constants/accountTypes';
|
||||
import { useItemFormContext } from './ItemFormProvider';
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Item form inventory sections.
|
||||
@@ -31,36 +28,24 @@ function ItemFormInventorySection({ organization: { base_currency } }) {
|
||||
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
{/*------------- Inventory account ------------- */}
|
||||
<FastField
|
||||
{/*------------- Inventory Account ------------- */}
|
||||
<FFormGroup
|
||||
label={<T id={'inventory_account'} />}
|
||||
name={'inventory_account_id'}
|
||||
accounts={accounts}
|
||||
items={accounts}
|
||||
fastField={true}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
inline={true}
|
||||
>
|
||||
{({ form, field: { value }, meta: { touched, error } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'inventory_account'} />}
|
||||
inline={true}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="inventory_account_id" />}
|
||||
className={classNames(
|
||||
'form-group--item-inventory_account',
|
||||
'form-group--select-list',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('inventory_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_account'} />}
|
||||
selectedAccountId={value}
|
||||
filterByTypes={[ACCOUNT_TYPE.INVENTORY]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<AccountsSelect
|
||||
name={'inventory_account_id'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_account'} />}
|
||||
filterByTypes={[ACCOUNT_TYPE.INVENTORY]}
|
||||
fastField={true}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
||||
@@ -119,7 +119,7 @@ export const handleDeleteErrors = (errors) => {
|
||||
*/
|
||||
export const accountsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.items !== oldProps.items ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
@@ -149,7 +149,7 @@ export const sellPriceFieldShouldUpdate = (newProps, oldProps) => {
|
||||
*/
|
||||
export const sellAccountFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.items !== oldProps.items ||
|
||||
newProps.sellable !== oldProps.sellable ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Form, FastField, useFormikContext } from 'formik';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
FormGroup,
|
||||
RadioGroup,
|
||||
@@ -14,13 +15,13 @@ import { useHistory } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
FormattedMessage as T,
|
||||
AccountsSelectList,
|
||||
AccountsSelect,
|
||||
FieldRequiredHint,
|
||||
CardFooterActions,
|
||||
FFormGroup,
|
||||
} from '@/components';
|
||||
import { handleStringChange, inputIntent } from '@/utils';
|
||||
import { ACCOUNT_TYPE } from '@/constants/accountTypes';
|
||||
|
||||
import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from '@/constants/accountTypes';
|
||||
import { useAccountantFormContext } from './AccountantFormProvider';
|
||||
|
||||
/**
|
||||
@@ -28,6 +29,7 @@ import { useAccountantFormContext } from './AccountantFormProvider';
|
||||
*/
|
||||
export default function AccountantForm() {
|
||||
const history = useHistory();
|
||||
const { accounts } = useAccountantFormContext();
|
||||
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
@@ -35,8 +37,6 @@ export default function AccountantForm() {
|
||||
history.go(-1);
|
||||
};
|
||||
|
||||
const { accounts } = useAccountantFormContext();
|
||||
|
||||
return (
|
||||
<Form>
|
||||
{/* ----------- Accounts ----------- */}
|
||||
@@ -48,7 +48,7 @@ export default function AccountantForm() {
|
||||
}
|
||||
className={'accounts-checkbox'}
|
||||
>
|
||||
{/*------------ account code required -----------*/}
|
||||
{/*------------ Account code (required) -----------*/}
|
||||
<FastField name={'account_code_required'} type={'checkbox'}>
|
||||
{({ field }) => (
|
||||
<FormGroup inline={true}>
|
||||
@@ -65,7 +65,8 @@ export default function AccountantForm() {
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/*------------ account code unique -----------*/}
|
||||
|
||||
{/*------------ Account code (unique) -----------*/}
|
||||
<FastField name={'account_code_unique'} type={'checkbox'}>
|
||||
{({ field }) => (
|
||||
<FormGroup inline={true}>
|
||||
@@ -85,6 +86,7 @@ export default function AccountantForm() {
|
||||
)}
|
||||
</FastField>
|
||||
</FormGroup>
|
||||
|
||||
{/* ----------- Accounting basis ----------- */}
|
||||
<FastField name={'accounting_basis'}>
|
||||
{({
|
||||
@@ -116,120 +118,93 @@ export default function AccountantForm() {
|
||||
</FastField>
|
||||
|
||||
{/* ----------- Deposit customer account ----------- */}
|
||||
<FastField name={'preferred_deposit_account'}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={
|
||||
<strong>
|
||||
<T id={'deposit_customer_account'} />
|
||||
</strong>
|
||||
<AccountantFormGroup
|
||||
name={'preferred_deposit_account'}
|
||||
label={
|
||||
<strong>
|
||||
<T id={'deposit_customer_account'} />
|
||||
</strong>
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_after_customer_make_payment'
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_after_customer_make_payment'
|
||||
}
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) => {
|
||||
setFieldValue('preferred_deposit_account', id);
|
||||
}}
|
||||
selectedAccountId={value}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
fastField={true}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'preferred_deposit_account'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_payment_account'} />}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
]}
|
||||
fastField={true}
|
||||
/>
|
||||
</AccountantFormGroup>
|
||||
|
||||
{/* ----------- Withdrawal vendor account ----------- */}
|
||||
<FastField name={'withdrawal_account'}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={
|
||||
<strong>
|
||||
<T id={'withdrawal_vendor_account'} />
|
||||
</strong>
|
||||
<AccountantFormGroup
|
||||
name={'withdrawal_account'}
|
||||
label={
|
||||
<strong>
|
||||
<T id={'withdrawal_vendor_account'} />
|
||||
</strong>
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_after_customer_make_payment'
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_after_customer_make_payment'
|
||||
}
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) => {
|
||||
setFieldValue('withdrawal_account', id);
|
||||
}}
|
||||
selectedAccountId={value}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
fastField={true}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'withdrawal_account'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_payment_account'} />}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
]}
|
||||
fastField={true}
|
||||
/>
|
||||
</AccountantFormGroup>
|
||||
|
||||
{/* ----------- Withdrawal customer account ----------- */}
|
||||
<FastField name={'preferred_advance_deposit'}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={
|
||||
<strong>
|
||||
<T id={'customer_advance_deposit'} />
|
||||
</strong>
|
||||
<AccountantFormGroup
|
||||
name={'preferred_advance_deposit'}
|
||||
label={
|
||||
<strong>
|
||||
<T id={'customer_advance_deposit'} />
|
||||
</strong>
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_vendor_advanced_deposits'
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_vendor_advanced_deposits'
|
||||
}
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) => {
|
||||
setFieldValue('preferred_advance_deposit', id);
|
||||
}}
|
||||
selectedAccountId={value}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
// filterByParentTypes={[ACCOUNT_PARENT_TYPE.CURRENT_ASSET]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
fastField={true}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'preferred_advance_deposit'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_payment_account'} />}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.CURRENT_ASSET]}
|
||||
fastField={true}
|
||||
/>
|
||||
</AccountantFormGroup>
|
||||
|
||||
<CardFooterActions>
|
||||
<Button intent={Intent.PRIMARY} loading={isSubmitting} type="submit">
|
||||
@@ -242,3 +217,7 @@ export default function AccountantForm() {
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
const AccountantFormGroup = styled(FFormGroup)`
|
||||
width: 450px;
|
||||
`;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Form, FastField, useFormikContext } from 'formik';
|
||||
import { Form, useFormikContext } from 'formik';
|
||||
import { FormGroup, Button, Intent } from '@blueprintjs/core';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
AccountsSelectList,
|
||||
AccountsSelect,
|
||||
FieldRequiredHint,
|
||||
FormattedMessage as T,
|
||||
CardFooterActions
|
||||
FFormGroup,
|
||||
CardFooterActions,
|
||||
} from '@/components';
|
||||
import { inputIntent } from '@/utils';
|
||||
import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from '@/constants/accountTypes';
|
||||
|
||||
import { useItemPreferencesFormContext } from './ItemPreferencesFormProvider';
|
||||
@@ -29,113 +29,83 @@ export default function ItemForm() {
|
||||
|
||||
return (
|
||||
<Form>
|
||||
{/* ----------- preferred sell account ----------- */}
|
||||
<FastField name={'preferred_sell_account'}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={
|
||||
<strong>
|
||||
<T id={'preferred_sell_account'} />
|
||||
</strong>
|
||||
{/* ----------- Preferred Sell Account ----------- */}
|
||||
<FormGroup
|
||||
name={'preferred_sell_account'}
|
||||
label={
|
||||
<strong>
|
||||
<T id={'preferred_sell_account'} />
|
||||
</strong>
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_after_customer_make_payment'
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_after_customer_make_payment'
|
||||
}
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) => {
|
||||
setFieldValue('preferred_sell_account', id);
|
||||
}}
|
||||
selectedAccountId={value}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
fastField={true}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'preferred_sell_account'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_payment_account'} />}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/* ----------- preferred cost account ----------- */}
|
||||
<FastField name={'preferred_cost_account'}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={
|
||||
<strong>
|
||||
<T id={'preferred_cost_account'} />
|
||||
</strong>
|
||||
{/* ----------- Preferred Cost Account ----------- */}
|
||||
<FFormGroup
|
||||
name={'preferred_cost_account'}
|
||||
label={
|
||||
<strong>
|
||||
<T id={'preferred_cost_account'} />
|
||||
</strong>
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_after_customer_make_payment'
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_after_customer_make_payment'
|
||||
}
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) => {
|
||||
setFieldValue('preferred_cost_account', id);
|
||||
}}
|
||||
selectedAccountId={value}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.EXPENSE]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
fastField={true}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'preferred_cost_account'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_payment_account'} />}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.EXPENSE]}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/* ----------- preferred inventory account ----------- */}
|
||||
<FastField name={'preferred_inventory_account'}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={
|
||||
<strong>
|
||||
<T id={'preferred_inventory_account'} />
|
||||
</strong>
|
||||
{/* ----------- Preferred Inventory Account ----------- */}
|
||||
<FFormGroup
|
||||
name={'preferred_inventory_account'}
|
||||
label={
|
||||
<strong>
|
||||
<T id={'preferred_inventory_account'} />
|
||||
</strong>
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_vendor_advanced_deposits'
|
||||
}
|
||||
helperText={
|
||||
<T
|
||||
id={
|
||||
'select_a_preferred_account_to_deposit_into_it_vendor_advanced_deposits'
|
||||
}
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) => {
|
||||
setFieldValue('preferred_inventory_account', id);
|
||||
}}
|
||||
selectedAccountId={value}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
filterByTypes={[ACCOUNT_TYPE.INVENTORY]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
/>
|
||||
}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
fastField={true}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'preferred_inventory_account'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_payment_account'} />}
|
||||
filterByTypes={[ACCOUNT_TYPE.INVENTORY]}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
<CardFooterActions>
|
||||
<Button intent={Intent.PRIMARY} loading={isSubmitting} type="submit">
|
||||
|
||||
@@ -18,7 +18,7 @@ import { CLASSES } from '@/constants/classes';
|
||||
|
||||
import {
|
||||
FFormGroup,
|
||||
AccountsSelectList,
|
||||
AccountsSelect,
|
||||
VendorSelectField,
|
||||
FieldRequiredHint,
|
||||
InputPrependText,
|
||||
@@ -211,41 +211,30 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Payment account ------------ */}
|
||||
<FastField
|
||||
<FFormGroup
|
||||
name={'payment_account_id'}
|
||||
accounts={accounts}
|
||||
label={<T id={'payment_account'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
items={accounts}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
inline={true}
|
||||
fastField={true}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'payment_account'} />}
|
||||
className={classNames(
|
||||
'form-group--payment_account_id',
|
||||
'form-group--select-list',
|
||||
Classes.FILL,
|
||||
)}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'payment_account_id'} />}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('payment_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
selectedAccountId={value}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<AccountsSelect
|
||||
name={'payment_account_id'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_payment_account'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
]}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
fastField={true}
|
||||
fill={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/* ------------ Reference ------------ */}
|
||||
<FastField name={'reference'}>
|
||||
|
||||
@@ -84,7 +84,7 @@ export const vendorsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
*/
|
||||
export const accountsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.items !== oldProps.items ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -16,21 +16,21 @@ function InvoicesListProvider({ query, tableStateChanged, ...props }) {
|
||||
const { data: invoicesViews, isLoading: isViewsLoading } =
|
||||
useResourceViews('sale_invoices');
|
||||
|
||||
// Fetch the accounts resource fields.
|
||||
// Fetch resource fields of the sale invoices.
|
||||
const {
|
||||
data: resourceMeta,
|
||||
isLoading: isResourceLoading,
|
||||
isFetching: isResourceFetching,
|
||||
} = useResourceMeta('sale_invoices');
|
||||
|
||||
// Fetch accounts list according to the given custom view id.
|
||||
// Fetch sale invoices of the given query.
|
||||
const {
|
||||
data: { invoices, pagination, filterMeta },
|
||||
isFetching: isInvoicesFetching,
|
||||
isLoading: isInvoicesLoading,
|
||||
} = useInvoices(query, { keepPreviousData: true });
|
||||
|
||||
// Detarmines the datatable empty status.
|
||||
// Detarmines whether the table should show empty state.
|
||||
const isEmptyStatus =
|
||||
isEmpty(invoices) && !tableStateChanged && !isInvoicesLoading;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
} from '@/utils';
|
||||
import {
|
||||
FFormGroup,
|
||||
AccountsSelectList,
|
||||
AccountsSelect,
|
||||
CustomerSelectField,
|
||||
FieldRequiredHint,
|
||||
Icon,
|
||||
@@ -285,41 +285,30 @@ function PaymentReceiveHeaderFields({
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Deposit account ------------ */}
|
||||
<FastField
|
||||
<FFormGroup
|
||||
name={'deposit_account_id'}
|
||||
accounts={accounts}
|
||||
label={<T id={'deposit_to'} />}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
items={accounts}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
fastField={true}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'deposit_to'} />}
|
||||
className={classNames(
|
||||
'form-group--deposit_account_id',
|
||||
'form-group--select-list',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'deposit_account_id'} />}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('deposit_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_deposit_account'} />}
|
||||
selectedAccountId={value}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<AccountsSelect
|
||||
name={'deposit_account_id'}
|
||||
items={accounts}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
placeholder={<T id={'select_deposit_account'} />}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
]}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
fastField={true}
|
||||
fill={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/* ------------ Reference No. ------------ */}
|
||||
<FastField name={'reference_no'}>
|
||||
|
||||
@@ -150,7 +150,7 @@ export const customersFieldShouldUpdate = (newProps, oldProps) => {
|
||||
*/
|
||||
export const accountsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.items !== oldProps.items ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ import { CLASSES } from '@/constants/classes';
|
||||
|
||||
import {
|
||||
FFormGroup,
|
||||
AccountsSelectList,
|
||||
AccountsSelect,
|
||||
CustomerSelectField,
|
||||
FieldRequiredHint,
|
||||
Icon,
|
||||
@@ -125,38 +125,30 @@ function ReceiptFormHeader({
|
||||
/>
|
||||
|
||||
{/* ----------- Deposit account ----------- */}
|
||||
<FastField
|
||||
<FFormGroup
|
||||
label={<T id={'deposit_account'} />}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
name={'deposit_account_id'}
|
||||
accounts={accounts}
|
||||
items={accounts}
|
||||
fastField={true}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'deposit_account'} />}
|
||||
className={classNames('form-group--deposit-account', CLASSES.FILL)}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'deposit_account_id'} />}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('deposit_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_deposit_account'} />}
|
||||
selectedAccountId={value}
|
||||
popoverFill={true}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
]}
|
||||
allowCreate={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<AccountsSelect
|
||||
items={accounts}
|
||||
name={'deposit_account_id'}
|
||||
placeholder={<T id={'select_deposit_account'} />}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
]}
|
||||
allowCreate={true}
|
||||
fill={true}
|
||||
fastField={true}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/* ----------- Receipt date ----------- */}
|
||||
<FastField name={'receipt_date'}>
|
||||
|
||||
@@ -101,7 +101,7 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
*/
|
||||
export const accountsFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.items !== oldProps.items ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,16 +20,16 @@ function VendorsListProvider({ tableState, tableStateChanged, ...props }) {
|
||||
isFetching: isVendorsFetching,
|
||||
} = useVendors(tableQuery, { keepPreviousData: true });
|
||||
|
||||
// Fetch customers resource views and fields.
|
||||
// Fetch vendors resource views and fields.
|
||||
const { data: vendorsViews, isLoading: isVendorsViewsLoading } =
|
||||
useResourceViews('vendors');
|
||||
|
||||
// Fetch the customers resource fields.
|
||||
// Fetch the vendors resource fields.
|
||||
const {
|
||||
data: resourceMeta,
|
||||
isLoading: isResourceMetaLoading,
|
||||
isFetching: isResourceMetaFetching,
|
||||
} = useResourceMeta('customers');
|
||||
} = useResourceMeta('vendors');
|
||||
|
||||
// Detarmines the datatable empty status.
|
||||
const isEmptyStatus =
|
||||
|
||||
@@ -8,13 +8,13 @@ import {
|
||||
} from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { FormattedMessage as T } from '@/components';
|
||||
import { FFormGroup, FormattedMessage as T } from '@/components';
|
||||
import { momentFormatter, compose, tansformDateValue } from '@/utils';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import {
|
||||
AccountsSelectList,
|
||||
AccountsSelect,
|
||||
FieldRequiredHint,
|
||||
Icon,
|
||||
InputPrependButton,
|
||||
@@ -91,6 +91,7 @@ function WarehouseTransferFormHeaderFields({
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ----------- Transfer number ----------- */}
|
||||
<Field name={'transaction_number'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
@@ -130,60 +131,39 @@ function WarehouseTransferFormHeaderFields({
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
{/* ----------- Form Warehouse ----------- */}
|
||||
<FastField name={'from_warehouse_id'} accounts={warehouses}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'warehouse_transfer.label.from_warehouse'} />}
|
||||
className={classNames(
|
||||
'form-group--warehouse-transfer',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'from_warehouse_id'} />}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={warehouses}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('from_warehouse_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_warehouse_transfer'} />}
|
||||
selectedAccountId={value}
|
||||
popoverFill={true}
|
||||
allowCreate={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FFormGroup
|
||||
name={'from_warehouse_id'}
|
||||
items={warehouses}
|
||||
label={<T id={'warehouse_transfer.label.from_warehouse'} />}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'from_warehouse_id'}
|
||||
items={warehouses}
|
||||
placeholder={<T id={'select_warehouse_transfer'} />}
|
||||
allowCreate={true}
|
||||
fill={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/* ----------- To Warehouse ----------- */}
|
||||
<FastField name={'to_warehouse_id'} accounts={warehouses}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'warehouse_transfer.label.to_warehouse'} />}
|
||||
className={classNames(
|
||||
'form-group--warehouse-transfer',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'to_warehouse_id'} />}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={warehouses}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('to_warehouse_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_warehouse_transfer'} />}
|
||||
selectedAccountId={value}
|
||||
popoverFill={true}
|
||||
allowCreate={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FFormGroup
|
||||
name={'to_warehouse_id'}
|
||||
label={<T id={'warehouse_transfer.label.to_warehouse'} />}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
>
|
||||
<AccountsSelect
|
||||
name={'to_warehouse_id'}
|
||||
items={warehouses}
|
||||
placeholder={<T id={'select_warehouse_transfer'} />}
|
||||
fill={true}
|
||||
allowCreate={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"new_currency": "New Currency",
|
||||
"currency_name": "Currency Name",
|
||||
"currency_code": "Currency Code",
|
||||
"select_currency_code": "Select Currency Code",
|
||||
"select_currency_code": "Select Currency Code...",
|
||||
"edit_exchange_rate": "Edit Exchange Rate",
|
||||
"new_exchange_rate": "New Exchange Rate",
|
||||
"delete_exchange_rate": "Delete Exchange Rate",
|
||||
@@ -242,7 +242,7 @@
|
||||
"organization": "Organization.",
|
||||
"check_your_email_for_a_link_to_reset": "Check your email for a link to reset your password.If it doesn’t appear within a few minutes, check your spam folder.",
|
||||
"we_couldn_t_find_your_account_with_that_email": "We couldn't find your account with that email.",
|
||||
"select_parent_account": "Select Parent Account",
|
||||
"select_parent_account": "Select Parent Account...",
|
||||
"the_exchange_rate_has_been_edited_successfully": "The exchange rate has been edited successfully",
|
||||
"the_exchange_rate_has_been_created_successfully": "The exchange rate has been created successfully",
|
||||
"the_user_details_has_been_updated": "The user details has been updated",
|
||||
@@ -540,7 +540,7 @@
|
||||
"statement": "Statement",
|
||||
"deposit_account": "Deposit Account",
|
||||
"send_to_email": "Send to email",
|
||||
"select_deposit_account": "Select Deposit Account",
|
||||
"select_deposit_account": "Select Deposit Account...",
|
||||
"once_delete_this_receipt_you_will_able_to_restore_it": "Once you delete this receipt, you won't be able to restore it later. Are you sure you want to delete this receipt?",
|
||||
"the_receipt_has_been_created_successfully": "The receipt #{number} has been created successfully.",
|
||||
"the_receipt_has_been_edited_successfully": "The receipt #{number} has been edited successfully.",
|
||||
@@ -609,8 +609,8 @@
|
||||
"new_payment_made": "New Payment Made",
|
||||
"payment_made_list": "Payment Made List",
|
||||
"payment_account": "Payment Account",
|
||||
"select_vender_account": "Select Vender Account",
|
||||
"select_payment_account": "Select Payment Account",
|
||||
"select_vender_account": "Select Vender Account...",
|
||||
"select_payment_account": "Select Payment Account...",
|
||||
"the_payment_made_has_been_edited_successfully": "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_deleted_successfully": "The payment made has been deleted successfully.",
|
||||
@@ -1417,6 +1417,7 @@
|
||||
"siebar.cashflow": "Cash flow",
|
||||
"siebar.cashflow.label_cash_and_bank_accounts": "Cash & Bank Accounts",
|
||||
"cash_flow.label_account_transcations": "Account Transcations",
|
||||
"cash_flow.transactions_for_review": "Transactions for review",
|
||||
"cash_flow.label.deposit": "Deposit",
|
||||
"cash_flow.label.withdrawal": "Withdrawal",
|
||||
"cash_flow.label.running_balance": "Running balance",
|
||||
|
||||
@@ -52,6 +52,7 @@ body.hide-scrollbar .Pane2 {
|
||||
}
|
||||
|
||||
.bp3-fill {
|
||||
|
||||
.bp3-popover-wrapper,
|
||||
.bp3-popover-target {
|
||||
display: block;
|
||||
@@ -68,9 +69,9 @@ body.hide-scrollbar .Pane2 {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.bp3-select-popover .bp3-menu {
|
||||
.bp3-select-popover .bp3-menu,
|
||||
.bp3-multi-select-popover .bp3-menu {
|
||||
max-height: 300px;
|
||||
max-width: 400px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@@ -103,27 +104,21 @@ body.hide-scrollbar .Pane2 {
|
||||
background-color: #0066ff;
|
||||
}
|
||||
|
||||
.ReactVirtualized__Collection {
|
||||
}
|
||||
.ReactVirtualized__Collection {}
|
||||
|
||||
.ReactVirtualized__Collection__innerScrollContainer {
|
||||
}
|
||||
.ReactVirtualized__Collection__innerScrollContainer {}
|
||||
|
||||
/* Grid default theme */
|
||||
|
||||
.ReactVirtualized__Grid {
|
||||
}
|
||||
.ReactVirtualized__Grid {}
|
||||
|
||||
.ReactVirtualized__Grid__innerScrollContainer {
|
||||
}
|
||||
.ReactVirtualized__Grid__innerScrollContainer {}
|
||||
|
||||
/* Table default theme */
|
||||
|
||||
.ReactVirtualized__Table {
|
||||
}
|
||||
.ReactVirtualized__Table {}
|
||||
|
||||
.ReactVirtualized__Table__Grid {
|
||||
}
|
||||
.ReactVirtualized__Table__Grid {}
|
||||
|
||||
.ReactVirtualized__Table__headerRow {
|
||||
font-weight: 700;
|
||||
@@ -186,8 +181,7 @@ body.hide-scrollbar .Pane2 {
|
||||
|
||||
/* List default theme */
|
||||
|
||||
.ReactVirtualized__List {
|
||||
}
|
||||
.ReactVirtualized__List {}
|
||||
|
||||
.bp3-drawer {
|
||||
box-shadow: 0 0 0;
|
||||
@@ -243,7 +237,7 @@ html[lang^='ar'] {
|
||||
padding-left: 14px;
|
||||
padding-right: 14px;
|
||||
|
||||
& + .bp3-button {
|
||||
&+.bp3-button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
@@ -255,7 +249,7 @@ html[lang^='ar'] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
> .bp3-spinner {
|
||||
>.bp3-spinner {
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
@@ -283,6 +277,7 @@ html[lang^='ar'] {
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -292,6 +287,6 @@ html[lang^='ar'] {
|
||||
}
|
||||
|
||||
|
||||
span.table-tooltip-overview-target{
|
||||
span.table-tooltip-overview-target {
|
||||
display: inline;
|
||||
}
|
||||
Reference in New Issue
Block a user