mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d24474162 | ||
|
|
5962b990c4 | ||
|
|
9f21f649f0 | ||
|
|
d3d2112b8a | ||
|
|
3795322a65 | ||
|
|
fe5cd5a8ea | ||
|
|
c032a5db16 | ||
|
|
3fcb6fefde | ||
|
|
16f5cb713d | ||
|
|
f7a7925028 | ||
|
|
66fb0c9fa3 | ||
|
|
85acc85f17 | ||
|
|
c76ce09191 | ||
|
|
e3532098b2 | ||
|
|
b6783eb22e | ||
|
|
4ef00ab122 | ||
|
|
3dd827417a | ||
|
|
cbacd02aa2 | ||
|
|
3b7e0fb78a | ||
|
|
9add716395 | ||
|
|
083ea28a1f | ||
|
|
1b51742c36 | ||
|
|
0c6f23e770 | ||
|
|
37a8ca4e97 | ||
|
|
795303c3a8 | ||
|
|
63ba3f0898 | ||
|
|
62594efa00 |
@@ -34,24 +34,9 @@ export const ITEM_EVENT_DELETED = 'Item deleted';
|
||||
export const AUTH_SIGNED_UP = 'Auth Signed-up';
|
||||
export const AUTH_RESET_PASSWORD = 'Auth reset password';
|
||||
|
||||
export const BANK_TRANSACTION_MATCHED = 'Bank transaction matching deleted';
|
||||
export const BANK_TRANSACTION_EXCLUDED = 'Bank transaction excluded';
|
||||
export const BANK_TRANSACTION_CATEGORIZED = 'Bank transaction categorized';
|
||||
export const BANK_TRANSACTION_UNCATEGORIZED = 'Bank transaction uncategorized';
|
||||
export const BANK_ACCOUNT_DISCONNECTED = 'Bank account disconnected';
|
||||
|
||||
export const ACCOUNT_GROUP = 'Account';
|
||||
export const ITEM_GROUP = 'Item';
|
||||
export const AUTH_GROUP = 'Auth';
|
||||
export const SALE_GROUP = 'Sale';
|
||||
export const PAYMENT_GROUP = 'Payment';
|
||||
export const BILL_GROUP = 'Bill';
|
||||
export const EXPENSE_GROUP = 'Expense';
|
||||
|
||||
export const MANUAL_JOURNAL_CREATED = 'Manual journal created';
|
||||
export const MANUAL_JOURNAL_EDITED = 'Manual journal edited';
|
||||
export const MANUAL_JOURNAL_DELETED = 'Manual journal deleted';
|
||||
export const MANUAL_JOURNAL_PUBLISHED = 'Manual journal published';
|
||||
export const SUBSCRIPTION_CANCELLED = 'Subscription cancelled';
|
||||
export const SUBSCRIPTION_RESUMED = 'Subscription resumed';
|
||||
export const SUBSCRIPTION_PLAN_CHANGED = 'Subscription plan changed';
|
||||
|
||||
export const CUSTOMER_CREATED = 'Customer created';
|
||||
export const CUSTOMER_EDITED = 'Customer edited';
|
||||
@@ -61,7 +46,34 @@ export const VENDOR_CREATED = 'Vendor created';
|
||||
export const VENDOR_EDITED = 'Vendor edited';
|
||||
export const VENDOR_DELETED = 'Vendor deleted';
|
||||
|
||||
export const TRANSACTIONS_LOCKING_LOCKED = 'Transactions locking locked';
|
||||
export const TRANSACTIONS_LOCKING_LOCKING_CANCELLED =
|
||||
'Transactions locking cancelled';
|
||||
export const TRANSACTIONS_LOCKING_PARTIALLY_UNLOCKED =
|
||||
'Transactions locking partially unlocked';
|
||||
export const TRANSACTIONS_LOCKING_PARTIALLY_UNLOCK_CANCELLED =
|
||||
'Transactions locking partially unlock cancelled';
|
||||
|
||||
export const BANK_TRANSACTION_MATCHED = 'Bank transaction matching deleted';
|
||||
export const BANK_TRANSACTION_EXCLUDED = 'Bank transaction excluded';
|
||||
export const BANK_TRANSACTION_CATEGORIZED = 'Bank transaction categorized';
|
||||
export const BANK_TRANSACTION_UNCATEGORIZED = 'Bank transaction uncategorized';
|
||||
export const BANK_ACCOUNT_DISCONNECTED = 'Bank account disconnected';
|
||||
|
||||
export const MANUAL_JOURNAL_CREATED = 'Manual journal created';
|
||||
export const MANUAL_JOURNAL_EDITED = 'Manual journal edited';
|
||||
export const MANUAL_JOURNAL_DELETED = 'Manual journal deleted';
|
||||
export const MANUAL_JOURNAL_PUBLISHED = 'Manual journal published';
|
||||
|
||||
export const BANK_RULE_CREATED = 'Bank rule created';
|
||||
export const BANK_RULE_EDITED = 'Bank rule edited';
|
||||
export const BANK_RULE_DELETED = 'Bank rule deleted';
|
||||
|
||||
// # Event Groups
|
||||
export const ACCOUNT_GROUP = 'Account';
|
||||
export const ITEM_GROUP = 'Item';
|
||||
export const AUTH_GROUP = 'Auth';
|
||||
export const SALE_GROUP = 'Sale';
|
||||
export const PAYMENT_GROUP = 'Payment';
|
||||
export const BILL_GROUP = 'Bill';
|
||||
export const EXPENSE_GROUP = 'Expense';
|
||||
|
||||
@@ -146,6 +146,7 @@ export interface ICashflowTransactionUncategorizedPayload {
|
||||
tenantId: number;
|
||||
uncategorizedTransactionId: number;
|
||||
uncategorizedTransactions: Array<IUncategorizedCashflowTransaction>;
|
||||
oldMainUncategorizedTransaction: IUncategorizedCashflowTransaction;
|
||||
oldUncategorizedTransactions: Array<IUncategorizedCashflowTransaction>;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
@@ -78,9 +78,9 @@ export class RecognizeTranasctionsService {
|
||||
});
|
||||
|
||||
const bankRules = await BankRule.query(trx).onBuild((q) => {
|
||||
const rulesIds = castArray(ruleId);
|
||||
const rulesIds = !isEmpty(ruleId) ? castArray(ruleId) : [];
|
||||
|
||||
if (!isEmpty(rulesIds)) {
|
||||
if (rulesIds?.length > 0) {
|
||||
q.whereIn('id', rulesIds);
|
||||
}
|
||||
q.withGraphFetched('conditions');
|
||||
|
||||
@@ -33,22 +33,25 @@ export class UncategorizeCashflowTransaction {
|
||||
): Promise<Array<number>> {
|
||||
const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId);
|
||||
|
||||
const oldUncategorizedTransaction =
|
||||
const oldMainUncategorizedTransaction =
|
||||
await UncategorizedCashflowTransaction.query()
|
||||
.findById(uncategorizedTransactionId)
|
||||
.throwIfNotFound();
|
||||
|
||||
validateTransactionShouldBeCategorized(oldUncategorizedTransaction);
|
||||
validateTransactionShouldBeCategorized(oldMainUncategorizedTransaction);
|
||||
|
||||
const associatedUncategorizedTransactions =
|
||||
await UncategorizedCashflowTransaction.query()
|
||||
.where('categorizeRefId', oldUncategorizedTransaction.categorizeRefId)
|
||||
.where('categorizeRefId', oldMainUncategorizedTransaction.categorizeRefId)
|
||||
.where(
|
||||
'categorizeRefType',
|
||||
oldUncategorizedTransaction.categorizeRefType
|
||||
);
|
||||
oldMainUncategorizedTransaction.categorizeRefType
|
||||
)
|
||||
// Exclude the main transaction.
|
||||
.whereNot('id', uncategorizedTransactionId);
|
||||
|
||||
const oldUncategorizedTransactions = [
|
||||
oldUncategorizedTransaction,
|
||||
oldMainUncategorizedTransaction,
|
||||
...associatedUncategorizedTransactions,
|
||||
];
|
||||
const oldUncategoirzedTransactionsIds = oldUncategorizedTransactions.map(
|
||||
@@ -85,6 +88,7 @@ export class UncategorizeCashflowTransaction {
|
||||
{
|
||||
tenantId,
|
||||
uncategorizedTransactionId,
|
||||
oldMainUncategorizedTransaction,
|
||||
uncategorizedTransactions,
|
||||
oldUncategorizedTransactions,
|
||||
trx,
|
||||
|
||||
@@ -22,32 +22,25 @@ export class DeleteCashflowTransactionOnUncategorize {
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes the cashflow transaction on uncategorize transaction.
|
||||
* Deletes the cashflow transaction once uncategorize the bank transaction.
|
||||
* @param {ICashflowTransactionUncategorizedPayload} payload
|
||||
*/
|
||||
public async deleteCashflowTransactionOnUncategorize({
|
||||
tenantId,
|
||||
oldUncategorizedTransactions,
|
||||
oldMainUncategorizedTransaction,
|
||||
trx,
|
||||
}: ICashflowTransactionUncategorizedPayload) {
|
||||
const _oldUncategorizedTransactions = oldUncategorizedTransactions.filter(
|
||||
(transaction) => transaction.categorizeRefType === 'CashflowTransaction'
|
||||
);
|
||||
|
||||
// Deletes the cashflow transaction.
|
||||
if (_oldUncategorizedTransactions.length > 0) {
|
||||
const result = await PromisePool.withConcurrency(1)
|
||||
.for(_oldUncategorizedTransactions)
|
||||
.process(async (oldUncategorizedTransaction) => {
|
||||
await this.deleteCashflowTransactionService.deleteCashflowTransaction(
|
||||
tenantId,
|
||||
oldUncategorizedTransaction.categorizeRefId,
|
||||
trx
|
||||
);
|
||||
});
|
||||
if (result.errors.length > 0) {
|
||||
throw new ServiceError('SOMETHING_WRONG');
|
||||
}
|
||||
// Cannot continue if the main transaction does not reference to cashflow type.
|
||||
if (
|
||||
oldMainUncategorizedTransaction.categorizeRefType !==
|
||||
'CashflowTransaction'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
await this.deleteCashflowTransactionService.deleteCashflowTransaction(
|
||||
tenantId,
|
||||
oldMainUncategorizedTransaction.categorizeRefId,
|
||||
trx
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@ export class CustomerEventsTracker extends EventSubscriber {
|
||||
@Inject()
|
||||
private posthog: PosthogService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.customers.onCreated,
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { EventSubscriber } from '@/lib/EventPublisher/EventPublisher';
|
||||
import { ITransactionsLockingPartialUnlocked } from '@/interfaces';
|
||||
import { PosthogService } from '../PostHog';
|
||||
import {
|
||||
TRANSACTIONS_LOCKING_LOCKED,
|
||||
TRANSACTIONS_LOCKING_LOCKING_CANCELLED,
|
||||
TRANSACTIONS_LOCKING_PARTIALLY_UNLOCK_CANCELLED,
|
||||
TRANSACTIONS_LOCKING_PARTIALLY_UNLOCKED,
|
||||
} from '@/constants/event-tracker';
|
||||
import events from '@/subscribers/events';
|
||||
|
||||
@Service()
|
||||
export class TransactionsLockingEventsTracker extends EventSubscriber {
|
||||
@Inject()
|
||||
private posthog: PosthogService;
|
||||
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.transactionsLocking.locked,
|
||||
this.handleTransactionsLockingLockedEvent
|
||||
);
|
||||
bus.subscribe(
|
||||
events.transactionsLocking.lockCanceled,
|
||||
this.handleTransactionsLockingCancelledEvent
|
||||
);
|
||||
bus.subscribe(
|
||||
events.transactionsLocking.partialUnlocked,
|
||||
this.handleTransactionsLockingPartiallyUnlockedEvent
|
||||
);
|
||||
bus.subscribe(
|
||||
events.transactionsLocking.partialUnlockCanceled,
|
||||
this.handleTransactionsLockingPartiallyUnlockCancelledEvent
|
||||
);
|
||||
}
|
||||
|
||||
private handleTransactionsLockingLockedEvent = ({
|
||||
tenantId,
|
||||
}: ITransactionsLockingPartialUnlocked) => {
|
||||
this.posthog.trackEvent({
|
||||
distinctId: `tenant-${tenantId}`,
|
||||
event: TRANSACTIONS_LOCKING_LOCKED,
|
||||
properties: {},
|
||||
});
|
||||
};
|
||||
|
||||
private handleTransactionsLockingCancelledEvent = ({
|
||||
tenantId,
|
||||
}: ITransactionsLockingPartialUnlocked) => {
|
||||
this.posthog.trackEvent({
|
||||
distinctId: `tenant-${tenantId}`,
|
||||
event: TRANSACTIONS_LOCKING_LOCKING_CANCELLED,
|
||||
properties: {},
|
||||
});
|
||||
};
|
||||
|
||||
private handleTransactionsLockingPartiallyUnlockedEvent = ({
|
||||
tenantId,
|
||||
}: ITransactionsLockingPartialUnlocked) => {
|
||||
this.posthog.trackEvent({
|
||||
distinctId: `tenant-${tenantId}`,
|
||||
event: TRANSACTIONS_LOCKING_PARTIALLY_UNLOCKED,
|
||||
properties: {},
|
||||
});
|
||||
};
|
||||
|
||||
private handleTransactionsLockingPartiallyUnlockCancelledEvent = ({
|
||||
tenantId,
|
||||
}: ITransactionsLockingPartialUnlocked) => {
|
||||
this.posthog.trackEvent({
|
||||
distinctId: `tenant-${tenantId}`,
|
||||
event: TRANSACTIONS_LOCKING_PARTIALLY_UNLOCK_CANCELLED,
|
||||
properties: {},
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { EventSubscriber } from '@/lib/EventPublisher/EventPublisher';
|
||||
import { ITransactionsLockingPartialUnlocked } from '@/interfaces';
|
||||
import { PosthogService } from '../PostHog';
|
||||
import {
|
||||
SUBSCRIPTION_CANCELLED,
|
||||
SUBSCRIPTION_PLAN_CHANGED,
|
||||
SUBSCRIPTION_RESUMED,
|
||||
} from '@/constants/event-tracker';
|
||||
import events from '@/subscribers/events';
|
||||
|
||||
@Service()
|
||||
export class TransactionsLockingEventsTracker extends EventSubscriber {
|
||||
@Inject()
|
||||
private posthog: PosthogService;
|
||||
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.subscription.onSubscriptionResumed,
|
||||
this.handleSubscriptionResumedEvent
|
||||
);
|
||||
bus.subscribe(
|
||||
events.subscription.onSubscriptionCancelled,
|
||||
this.handleSubscriptionCancelledEvent
|
||||
);
|
||||
bus.subscribe(
|
||||
events.subscription.onSubscriptionPlanChanged,
|
||||
this.handleSubscriptionPlanChangedEvent
|
||||
);
|
||||
}
|
||||
|
||||
private handleSubscriptionResumedEvent = ({ tenantId }) => {
|
||||
this.posthog.trackEvent({
|
||||
distinctId: `tenant-${tenantId}`,
|
||||
event: SUBSCRIPTION_RESUMED,
|
||||
properties: {},
|
||||
});
|
||||
};
|
||||
|
||||
private handleSubscriptionCancelledEvent = ({ tenantId }) => {
|
||||
this.posthog.trackEvent({
|
||||
distinctId: `tenant-${tenantId}`,
|
||||
event: SUBSCRIPTION_CANCELLED,
|
||||
properties: {},
|
||||
});
|
||||
};
|
||||
|
||||
private handleSubscriptionPlanChangedEvent = ({ tenantId }) => {
|
||||
this.posthog.trackEvent({
|
||||
distinctId: `tenant-${tenantId}`,
|
||||
event: SUBSCRIPTION_PLAN_CHANGED,
|
||||
properties: {},
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -18,9 +18,6 @@ export class VendorEventsTracker extends EventSubscriber {
|
||||
@Inject()
|
||||
private posthog: PosthogService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(events.vendors.onCreated, this.handleTrackVendorCreatedEvent);
|
||||
bus.subscribe(events.vendors.onEdited, this.handleTrackEditedVendorEvent);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { sumBy, difference } from 'lodash';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ERRORS } from '../constants';
|
||||
import { ERRORS, SUPPORTED_EXPENSE_PAYMENT_ACCOUNT_TYPES } from '../constants';
|
||||
import {
|
||||
IAccount,
|
||||
IExpense,
|
||||
@@ -79,7 +79,9 @@ export class CommandExpenseValidator {
|
||||
* @throws {ServiceError}
|
||||
*/
|
||||
public validatePaymentAccountType = (paymentAccount: number[]) => {
|
||||
if (!paymentAccount.isParentType(ACCOUNT_PARENT_TYPE.CURRENT_ASSET)) {
|
||||
if (
|
||||
!paymentAccount.isAccountType(SUPPORTED_EXPENSE_PAYMENT_ACCOUNT_TYPES)
|
||||
) {
|
||||
throw new ServiceError(ERRORS.PAYMENT_ACCOUNT_HAS_INVALID_TYPE);
|
||||
}
|
||||
};
|
||||
|
||||
113
packages/server/src/services/Expenses/ExpenseGL.ts
Normal file
113
packages/server/src/services/Expenses/ExpenseGL.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
AccountNormal,
|
||||
IExpenseCategory,
|
||||
ILedger,
|
||||
ILedgerEntry,
|
||||
} from '@/interfaces';
|
||||
import Ledger from '../Accounting/Ledger';
|
||||
|
||||
export class ExpenseGL {
|
||||
private expense: any;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor(expense: any) {
|
||||
this.expense = expense;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the expense GL common entry.
|
||||
* @param {IExpense} expense
|
||||
* @returns {Partial<ILedgerEntry>}
|
||||
*/
|
||||
private getExpenseGLCommonEntry = (): Partial<ILedgerEntry> => {
|
||||
return {
|
||||
currencyCode: this.expense.currencyCode,
|
||||
exchangeRate: this.expense.exchangeRate,
|
||||
|
||||
transactionType: 'Expense',
|
||||
transactionId: this.expense.id,
|
||||
|
||||
date: this.expense.paymentDate,
|
||||
userId: this.expense.userId,
|
||||
|
||||
debit: 0,
|
||||
credit: 0,
|
||||
|
||||
branchId: this.expense.branchId,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the expense GL payment entry.
|
||||
* @param {IExpense} expense
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getExpenseGLPaymentEntry = (): ILedgerEntry => {
|
||||
const commonEntry = this.getExpenseGLCommonEntry();
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
credit: this.expense.localAmount,
|
||||
accountId: this.expense.paymentAccountId,
|
||||
accountNormal:
|
||||
this.expense?.paymentAccount?.accountNormal === 'debit'
|
||||
? AccountNormal.DEBIT
|
||||
: AccountNormal.CREDIT,
|
||||
index: 1,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the expense GL category entry.
|
||||
* @param {IExpense} expense -
|
||||
* @param {IExpenseCategory} expenseCategory -
|
||||
* @param {number} index
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getExpenseGLCategoryEntry = R.curry(
|
||||
(category: IExpenseCategory, index: number): ILedgerEntry => {
|
||||
const commonEntry = this.getExpenseGLCommonEntry();
|
||||
const localAmount = category.amount * this.expense.exchangeRate;
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
accountId: category.expenseAccountId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
debit: localAmount,
|
||||
note: category.description,
|
||||
index: index + 2,
|
||||
projectId: category.projectId,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves the expense GL entries.
|
||||
* @param {IExpense} expense
|
||||
* @returns {ILedgerEntry[]}
|
||||
*/
|
||||
public getExpenseGLEntries = (): ILedgerEntry[] => {
|
||||
const getCategoryEntry = this.getExpenseGLCategoryEntry();
|
||||
|
||||
const paymentEntry = this.getExpenseGLPaymentEntry();
|
||||
const categoryEntries = this.expense.categories.map(getCategoryEntry);
|
||||
|
||||
return [paymentEntry, ...categoryEntries];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the given expense ledger.
|
||||
* @param {IExpense} expense
|
||||
* @returns {ILedger}
|
||||
*/
|
||||
public getExpenseLedger = (): ILedger => {
|
||||
const entries = this.getExpenseGLEntries();
|
||||
|
||||
console.log(entries, 'entries');
|
||||
|
||||
return new Ledger(entries);
|
||||
};
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import * as R from 'ramda';
|
||||
import { Service } from 'typedi';
|
||||
import {
|
||||
AccountNormal,
|
||||
IExpense,
|
||||
IExpenseCategory,
|
||||
ILedger,
|
||||
ILedgerEntry,
|
||||
} from '@/interfaces';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
|
||||
@Service()
|
||||
export class ExpenseGLEntries {
|
||||
/**
|
||||
* Retrieves the expense GL common entry.
|
||||
* @param {IExpense} expense
|
||||
* @returns
|
||||
*/
|
||||
private getExpenseGLCommonEntry = (expense: IExpense) => {
|
||||
return {
|
||||
currencyCode: expense.currencyCode,
|
||||
exchangeRate: expense.exchangeRate,
|
||||
|
||||
transactionType: 'Expense',
|
||||
transactionId: expense.id,
|
||||
|
||||
date: expense.paymentDate,
|
||||
userId: expense.userId,
|
||||
|
||||
debit: 0,
|
||||
credit: 0,
|
||||
|
||||
branchId: expense.branchId,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the expense GL payment entry.
|
||||
* @param {IExpense} expense
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getExpenseGLPaymentEntry = (expense: IExpense): ILedgerEntry => {
|
||||
const commonEntry = this.getExpenseGLCommonEntry(expense);
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
credit: expense.localAmount,
|
||||
accountId: expense.paymentAccountId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
index: 1,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the expense GL category entry.
|
||||
* @param {IExpense} expense -
|
||||
* @param {IExpenseCategory} expenseCategory -
|
||||
* @param {number} index
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getExpenseGLCategoryEntry = R.curry(
|
||||
(
|
||||
expense: IExpense,
|
||||
category: IExpenseCategory,
|
||||
index: number
|
||||
): ILedgerEntry => {
|
||||
const commonEntry = this.getExpenseGLCommonEntry(expense);
|
||||
const localAmount = category.amount * expense.exchangeRate;
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
accountId: category.expenseAccountId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
debit: localAmount,
|
||||
note: category.description,
|
||||
index: index + 2,
|
||||
projectId: category.projectId,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves the expense GL entries.
|
||||
* @param {IExpense} expense
|
||||
* @returns {ILedgerEntry[]}
|
||||
*/
|
||||
public getExpenseGLEntries = (expense: IExpense): ILedgerEntry[] => {
|
||||
const getCategoryEntry = this.getExpenseGLCategoryEntry(expense);
|
||||
|
||||
const paymentEntry = this.getExpenseGLPaymentEntry(expense);
|
||||
const categoryEntries = expense.categories.map(getCategoryEntry);
|
||||
|
||||
return [paymentEntry, ...categoryEntries];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the given expense ledger.
|
||||
* @param {IExpense} expense
|
||||
* @returns {ILedger}
|
||||
*/
|
||||
public getExpenseLedger = (expense: IExpense): ILedger => {
|
||||
const entries = this.getExpenseGLEntries(expense);
|
||||
|
||||
return new Ledger(entries);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { IExpense, ILedger } from '@/interfaces';
|
||||
import { ExpenseGL } from './ExpenseGL';
|
||||
import HasTenancyService from '../Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class ExpenseGLEntries {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Retrieves the expense G/L of the given id.
|
||||
* @param {number} tenantId
|
||||
* @param {number} expenseId
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns {Promise<ILedger>}
|
||||
*/
|
||||
public getExpenseLedgerById = async (
|
||||
tenantId: number,
|
||||
expenseId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<ILedger> => {
|
||||
const { Expense } = await this.tenancy.models(tenantId);
|
||||
|
||||
const expense = await Expense.query(trx)
|
||||
.findById(expenseId)
|
||||
.withGraphFetched('categories')
|
||||
.withGraphFetched('paymentAccount')
|
||||
.throwIfNotFound();
|
||||
|
||||
return this.getExpenseLedger(expense);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the given expense ledger.
|
||||
* @param {IExpense} expense
|
||||
* @returns {ILedger}
|
||||
*/
|
||||
public getExpenseLedger = (expense: IExpense): ILedger => {
|
||||
const expenseGL = new ExpenseGL(expense);
|
||||
|
||||
return expenseGL.getExpenseLedger();
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { Knex } from 'knex';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ExpenseGLEntries } from './ExpenseGLEntries';
|
||||
import { ExpenseGLEntries } from './ExpenseGLEntriesService';
|
||||
|
||||
@Service()
|
||||
export class ExpenseGLEntriesStorage {
|
||||
@@ -12,9 +12,6 @@ export class ExpenseGLEntriesStorage {
|
||||
@Inject()
|
||||
private ledgerStorage: LedgerStorageService;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Writes the expense GL entries.
|
||||
* @param {number} tenantId
|
||||
@@ -26,15 +23,12 @@ export class ExpenseGLEntriesStorage {
|
||||
expenseId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const { Expense } = await this.tenancy.models(tenantId);
|
||||
|
||||
const expense = await Expense.query(trx)
|
||||
.findById(expenseId)
|
||||
.withGraphFetched('categories');
|
||||
|
||||
// Retrieves the given expense ledger.
|
||||
const expenseLedger = this.expenseGLEntries.getExpenseLedger(expense);
|
||||
|
||||
const expenseLedger = await this.expenseGLEntries.getExpenseLedgerById(
|
||||
tenantId,
|
||||
expenseId,
|
||||
trx
|
||||
);
|
||||
// Commits the expense ledger entries.
|
||||
await this.ledgerStorage.commit(tenantId, expenseLedger, trx);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
|
||||
|
||||
export const DEFAULT_VIEW_COLUMNS = [];
|
||||
export const DEFAULT_VIEWS = [
|
||||
{
|
||||
@@ -76,3 +78,12 @@ export const ExpensesSampleData = [
|
||||
Publish: 'T',
|
||||
},
|
||||
];
|
||||
|
||||
export const SUPPORTED_EXPENSE_PAYMENT_ACCOUNT_TYPES = [
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.CREDIT_CARD,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
ACCOUNT_TYPE.NON_CURRENT_ASSET,
|
||||
ACCOUNT_TYPE.FIXED_ASSET,
|
||||
];
|
||||
|
||||
@@ -9,7 +9,12 @@ import {
|
||||
Classes,
|
||||
Tooltip,
|
||||
Position,
|
||||
MenuItem,
|
||||
Menu,
|
||||
MenuDivider,
|
||||
} from '@blueprintjs/core';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
|
||||
import { FormattedMessage as T, Icon, Hint, If } from '@/components';
|
||||
|
||||
import DashboardTopbarUser from '@/components/Dashboard/TopbarUser';
|
||||
@@ -19,9 +24,20 @@ import DashboardBackLink from '@/components/Dashboard/DashboardBackLink';
|
||||
import withUniversalSearchActions from '@/containers/UniversalSearch/withUniversalSearchActions';
|
||||
import withDashboardActions from '@/containers/Dashboard/withDashboardActions';
|
||||
import withDashboard from '@/containers/Dashboard/withDashboard';
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
|
||||
import QuickNewDropdown from '@/containers/QuickNewDropdown/QuickNewDropdown';
|
||||
import { DashboardHamburgerButton, DashboardQuickSearchButton } from './_components';
|
||||
import {
|
||||
DashboardHamburgerButton,
|
||||
DashboardQuickSearchButton,
|
||||
} from './_components';
|
||||
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
import {
|
||||
COMMUNITY_BIGCAPITAL_LINK,
|
||||
DOCS_BIGCAPITAL_LINK,
|
||||
} from '@/constants/routes';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
@@ -41,6 +57,9 @@ function DashboardTopbar({
|
||||
|
||||
// #withGlobalSearch
|
||||
openGlobalSearch,
|
||||
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
@@ -112,11 +131,34 @@ function DashboardTopbar({
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'help-24'} iconSize={20} />}
|
||||
text={<T id={'help'} />}
|
||||
/>
|
||||
<Popover2
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={'Documents'}
|
||||
onClick={() => window.open(DOCS_BIGCAPITAL_LINK)}
|
||||
labelElement={<Icon icon={'share'} iconSize={16} />}
|
||||
/>
|
||||
<MenuItem
|
||||
text={'Community support'}
|
||||
onClick={() => window.open(COMMUNITY_BIGCAPITAL_LINK)}
|
||||
labelElement={<Icon icon={'share'} iconSize={16} />}
|
||||
/>
|
||||
<MenuItem
|
||||
text={'Keyboard shortcuts'}
|
||||
onClick={() => openDialog(DialogsName.KeyboardShortcutForm)}
|
||||
/>
|
||||
<MenuDivider />
|
||||
<MenuItem text={'Share feedback'} />
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'help-24'} iconSize={20} />}
|
||||
text={<T id={'help'} />}
|
||||
/>
|
||||
</Popover2>
|
||||
<NavbarDivider />
|
||||
</NavbarGroup>
|
||||
</Navbar>
|
||||
@@ -138,4 +180,5 @@ export default compose(
|
||||
pageHint,
|
||||
})),
|
||||
withDashboardActions,
|
||||
withDialogActions,
|
||||
)(DashboardTopbar);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// @ts-nocheck
|
||||
import React, { useContext } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { camelCase} from 'lodash';
|
||||
import { camelCase } from 'lodash';
|
||||
|
||||
import { If, Skeleton } from '@/components';
|
||||
import { useAppIntlContext } from '@/components/AppIntlProvider';
|
||||
import TableContext from './TableContext';
|
||||
import { saveInvoke, ignoreEventFromSelectors } from '@/utils';
|
||||
import { isCellLoading } from './utils';
|
||||
import { MoneyDisplay } from '../Money/MoneyDisplay';
|
||||
|
||||
const ROW_CLICK_SELECTORS_INGORED = ['.expand-toggle', '.selection-checkbox'];
|
||||
|
||||
@@ -58,7 +59,7 @@ export default function TableCell({ cell, row, index }) {
|
||||
return;
|
||||
}
|
||||
saveInvoke(onCellClick, cell, event);
|
||||
};
|
||||
};
|
||||
const cellType = camelCase(cell.column.Cell.cellType) || 'text';
|
||||
|
||||
return (
|
||||
@@ -109,7 +110,11 @@ export default function TableCell({ cell, row, index }) {
|
||||
</span>
|
||||
</If>
|
||||
|
||||
{cell.render('Cell')}
|
||||
{cell.column?.money ? (
|
||||
<MoneyDisplay>{cell.render('Cell')}</MoneyDisplay>
|
||||
) : (
|
||||
<>{cell.render('Cell')}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
.root {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
9
packages/webapp/src/components/Money/MoneyDisplay.tsx
Normal file
9
packages/webapp/src/components/Money/MoneyDisplay.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import styles from './MoneyDisplay.module.scss';
|
||||
|
||||
interface MoneyDisplayProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function MoneyDisplay({ children }: MoneyDisplayProps) {
|
||||
return <span className={styles.root}>{children}</span>;
|
||||
}
|
||||
2
packages/webapp/src/constants/routes.ts
Normal file
2
packages/webapp/src/constants/routes.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const DOCS_BIGCAPITAL_LINK = 'https://docs.bigcapital.app';
|
||||
export const COMMUNITY_BIGCAPITAL_LINK = 'https://community.bigcapital.app';
|
||||
@@ -27,6 +27,7 @@ export const useManualJournalsColumns = () => {
|
||||
accessor: 'formatted_amount',
|
||||
width: 115,
|
||||
clickable: true,
|
||||
money: true,
|
||||
align: 'right',
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
},
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Tag } from '@blueprintjs/core';
|
||||
import { Intent, Tag, Classes } from '@blueprintjs/core';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { If, AppToaster } from '@/components';
|
||||
import { NormalCell, BalanceCell, BankBalanceCell } from './components';
|
||||
@@ -73,7 +74,7 @@ export const useAccountsTableColumns = () => {
|
||||
id: 'type',
|
||||
Header: intl.get('type'),
|
||||
accessor: 'account_type_label',
|
||||
className: 'type',
|
||||
className: clsx('type', Classes.TEXT_MUTED),
|
||||
width: 140,
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
@@ -91,6 +92,7 @@ export const useAccountsTableColumns = () => {
|
||||
id: 'currency',
|
||||
Header: intl.get('currency'),
|
||||
accessor: 'currency_code',
|
||||
className: clsx(Classes.TEXT_MUTED),
|
||||
width: 75,
|
||||
clickable: true,
|
||||
},
|
||||
@@ -102,6 +104,7 @@ export const useAccountsTableColumns = () => {
|
||||
width: 150,
|
||||
clickable: true,
|
||||
align: 'right',
|
||||
money: true,
|
||||
},
|
||||
{
|
||||
id: 'balance',
|
||||
@@ -110,6 +113,7 @@ export const useAccountsTableColumns = () => {
|
||||
Cell: BalanceCell,
|
||||
width: 150,
|
||||
clickable: true,
|
||||
money: true,
|
||||
align: 'right',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -34,21 +34,23 @@ import {
|
||||
} from '@/constants/cashflowOptions';
|
||||
import { useRefreshCashflowTransactions } from '@/hooks/query';
|
||||
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
|
||||
import { useMediaQuery } from '@/hooks/useMediaQuery';
|
||||
import { useAppShellContext } from '@/components/AppShell/AppContentShell/AppContentShellProvider';
|
||||
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import withSettings from '@/containers/Settings/withSettings';
|
||||
import withSettingsActions from '@/containers/Settings/withSettingsActions';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
import { withBankingActions } from '../withBankingActions';
|
||||
import { withBanking } from '../withBanking';
|
||||
import withAlertActions from '@/containers/Alert/withAlertActions';
|
||||
import {
|
||||
useUpdateBankAccount,
|
||||
useExcludeUncategorizedTransactions,
|
||||
useUnexcludeUncategorizedTransactions,
|
||||
} from '@/hooks/query/bank-rules';
|
||||
import { withBankingActions } from '../withBankingActions';
|
||||
import { withBanking } from '../withBanking';
|
||||
import withAlertActions from '@/containers/Alert/withAlertActions';
|
||||
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
import { compose } from '@/utils';
|
||||
|
||||
function AccountTransactionsActionsBar({
|
||||
// #withDialogActions
|
||||
@@ -221,6 +223,13 @@ function AccountTransactionsActionsBar({
|
||||
});
|
||||
};
|
||||
|
||||
const { hideAside } = useAppShellContext();
|
||||
const isMin1350Query = useMediaQuery('(min-width: 1350px)');
|
||||
|
||||
// Shrink actions to dropdown if the aside is open and matched the media query,
|
||||
// To avoid actions overflow in small screens.
|
||||
const shrinkActions = !hideAside && !isMin1350Query;
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
@@ -241,23 +250,27 @@ function AccountTransactionsActionsBar({
|
||||
}}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
text={<T id={'print'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="file-import-16" iconSize={16} />}
|
||||
text={<T id={'import'} />}
|
||||
onClick={handleImportBtnClick}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<If condition={!shrinkActions}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
text={<T id={'print'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="file-import-16" iconSize={16} />}
|
||||
text={<T id={'import'} />}
|
||||
onClick={handleImportBtnClick}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</If>
|
||||
|
||||
<DashboardRowsHeightButton
|
||||
initialValue={cashflowTansactionsTableSize}
|
||||
onChange={handleTableRowSizeChange}
|
||||
@@ -290,6 +303,40 @@ function AccountTransactionsActionsBar({
|
||||
</Tooltip>
|
||||
</If>
|
||||
|
||||
<If condition={shrinkActions}>
|
||||
<NavbarDivider />
|
||||
<Popover
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
modifiers={{
|
||||
offset: { offset: '0, 4' },
|
||||
}}
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
text={<T id={'print'} />}
|
||||
/>
|
||||
<MenuItem
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
<MenuItem
|
||||
icon={<Icon icon="file-import-16" iconSize={16} />}
|
||||
text={<T id={'import'} />}
|
||||
onClick={handleImportBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
icon={<Icon icon="more-h-16" iconSize={16} />}
|
||||
minimal={true}
|
||||
/>
|
||||
</Popover>
|
||||
</If>
|
||||
|
||||
{!isEmpty(uncategorizedTransationsIdsSelected) && (
|
||||
<Button
|
||||
icon={<Icon icon="disable" iconSize={16} />}
|
||||
@@ -368,7 +415,6 @@ function AccountTransactionsActionsBar({
|
||||
</If>
|
||||
|
||||
<MenuItem onClick={handleBankRulesClick} text={'Bank rules'} />
|
||||
|
||||
<MenuDivider />
|
||||
<If condition={isSyncingOwner && isFeedsActive}>
|
||||
<MenuItem
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// @ts-nocheck
|
||||
import { Suspense, lazy } from 'react';
|
||||
import { Spinner } from '@blueprintjs/core';
|
||||
import { AppContentShell } from '@/components/AppShell';
|
||||
|
||||
const CategorizeTransactionAside = lazy(() =>
|
||||
import('../CategorizeTransactionAside/CategorizeTransactionAside').then(
|
||||
(module) => ({ default: module.CategorizeTransactionAside }),
|
||||
),
|
||||
);
|
||||
|
||||
export function AccountTransactionsAside() {
|
||||
return (
|
||||
<AppContentShell.Aside>
|
||||
<Suspense fallback={<Spinner size={20} />}>
|
||||
<CategorizeTransactionAside />
|
||||
</Suspense>
|
||||
</AppContentShell.Aside>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
import { curry } from 'lodash/fp';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Popover,
|
||||
Menu,
|
||||
@@ -11,10 +13,9 @@ import {
|
||||
Classes,
|
||||
} from '@blueprintjs/core';
|
||||
import { Icon } from '@/components';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { curry } from 'lodash/fp';
|
||||
|
||||
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
|
||||
import { useAppShellContext } from '@/components/AppShell/AppContentShell/AppContentShellProvider';
|
||||
|
||||
function AccountSwitchButton() {
|
||||
const { currentAccount } = useAccountTransactionsContext();
|
||||
@@ -22,7 +23,7 @@ function AccountSwitchButton() {
|
||||
return (
|
||||
<AccountSwitchButtonBase
|
||||
minimal={true}
|
||||
rightIcon={<Icon icon={'arrow-drop-down'} iconSize={24} />}
|
||||
rightIcon={<Icon icon={'caret-down-16'} iconSize={16} />}
|
||||
>
|
||||
<AccountSwitchText>{currentAccount.name}</AccountSwitchText>
|
||||
</AccountSwitchButtonBase>
|
||||
@@ -110,12 +111,16 @@ function AccountTransactionsDetailsBarSkeleton() {
|
||||
}
|
||||
|
||||
function AccountTransactionsDetailsContent() {
|
||||
const { hideAside } = useAppShellContext();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<AccountSwitchItem />
|
||||
<AccountNumberItem />
|
||||
|
||||
{/** Hide some details once the aside opens to preserve space on details bar. */}
|
||||
{hideAside && <AccountNumberItem />}
|
||||
<AccountBalanceItem />
|
||||
<AccountBankBalanceItem />
|
||||
{hideAside && <AccountBankBalanceItem />}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React, { Suspense } from 'react';
|
||||
import * as R from 'ramda';
|
||||
import { Spinner } from '@blueprintjs/core';
|
||||
import { Suspense, lazy } from 'react';
|
||||
|
||||
import '@/style/pages/CashFlow/AccountTransactions/List.scss';
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import { AccountTransactionsDetailsBar } from './AccountTransactionsDetailsBar';
|
||||
import { AccountTransactionsFilterTabs } from './AccountTransactionsFilterTabs';
|
||||
import { AppContentShell } from '@/components/AppShell';
|
||||
import { CategorizeTransactionAside } from '../CategorizeTransactionAside/CategorizeTransactionAside';
|
||||
import { AccountTransactionsAside } from './AccountTransactionsAside';
|
||||
import { AccountTransactionsLoadingBar } from './components';
|
||||
import { withBanking } from '../withBanking';
|
||||
|
||||
@@ -56,14 +56,6 @@ function AccountTransactionsMain() {
|
||||
);
|
||||
}
|
||||
|
||||
function AccountTransactionsAside() {
|
||||
return (
|
||||
<AppContentShell.Aside>
|
||||
<CategorizeTransactionAside />
|
||||
</AppContentShell.Aside>
|
||||
);
|
||||
}
|
||||
|
||||
export default R.compose(
|
||||
withBanking(
|
||||
({ selectedUncategorizedTransactionId, openMatchingTransactionAside }) => ({
|
||||
@@ -73,11 +65,8 @@ export default R.compose(
|
||||
),
|
||||
)(AccountTransactionsListRoot);
|
||||
|
||||
const AccountsTransactionsAll = React.lazy(
|
||||
() => import('./AccountsTransactionsAll'),
|
||||
);
|
||||
|
||||
const AccountsTransactionsUncategorized = React.lazy(
|
||||
const AccountsTransactionsAll = lazy(() => import('./AccountsTransactionsAll'));
|
||||
const AccountsTransactionsUncategorized = lazy(
|
||||
() => import('./AllTransactionsUncategorized'),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
DataTable,
|
||||
TableFastCell,
|
||||
TableSkeletonRows,
|
||||
TableSkeletonHeader,
|
||||
@@ -17,9 +15,10 @@ import { useMemorizedColumnsWidths } from '@/hooks';
|
||||
import { useExcludedTransactionsColumns } from './_utils';
|
||||
import { useExcludedTransactionsBoot } from './ExcludedTransactionsTableBoot';
|
||||
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
|
||||
import { useUnexcludeUncategorizedTransaction } from '@/hooks/query/bank-rules';
|
||||
|
||||
import { ActionsMenu } from './_components';
|
||||
import { useUnexcludeUncategorizedTransaction } from '@/hooks/query/bank-rules';
|
||||
import { BankAccountDataTable } from '../components/BankAccountDataTable';
|
||||
import {
|
||||
WithBankingActionsProps,
|
||||
withBankingActions,
|
||||
@@ -78,7 +77,7 @@ function ExcludedTransactionsTableRoot({
|
||||
};
|
||||
|
||||
return (
|
||||
<CashflowTransactionsTable
|
||||
<BankAccountDataTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={excludedBankTransactions}
|
||||
@@ -116,42 +115,3 @@ function ExcludedTransactionsTableRoot({
|
||||
export const ExcludedTransactionsTable = R.compose(withBankingActions)(
|
||||
ExcludedTransactionsTableRoot,
|
||||
);
|
||||
|
||||
const DashboardConstrantTable = styled(DataTable)`
|
||||
.table {
|
||||
.thead {
|
||||
.th {
|
||||
background: #fff;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody {
|
||||
.tr:last-child .td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CashflowTransactionsTable = styled(DashboardConstrantTable)`
|
||||
.table .tbody {
|
||||
.tbody-inner .tr.no-results {
|
||||
.td {
|
||||
padding: 2rem 0;
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
font-weight: 400;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody-inner {
|
||||
.tr .td {
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -54,12 +54,14 @@ export function useExcludedTransactionsColumns() {
|
||||
accessor: 'formatted_deposit_amount',
|
||||
align: 'right',
|
||||
width: depositWidth,
|
||||
money: true
|
||||
},
|
||||
{
|
||||
Header: 'Withdrawal',
|
||||
accessor: 'formatted_withdrawal_amount',
|
||||
align: 'right',
|
||||
width: withdrawalWidth,
|
||||
money: true
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
DataTable,
|
||||
TableFastCell,
|
||||
TableSkeletonRows,
|
||||
TableSkeletonHeader,
|
||||
@@ -16,6 +13,7 @@ import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
|
||||
import { usePendingTransactionsContext } from './PendingTransactionsTableBoot';
|
||||
import { usePendingTransactionsTableColumns } from './_hooks';
|
||||
|
||||
import { BankAccountDataTable } from '../components/BankAccountDataTable';
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
@@ -37,7 +35,7 @@ function PendingTransactionsDataTableRoot({
|
||||
} = usePendingTransactionsContext();
|
||||
|
||||
return (
|
||||
<CashflowTransactionsTable
|
||||
<BankAccountDataTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={pendingTransactions || []}
|
||||
@@ -54,7 +52,6 @@ function PendingTransactionsDataTableRoot({
|
||||
vListOverscanRowCount={0}
|
||||
noResults={'There is no pending transactions in the current account.'}
|
||||
windowScrollerProps={{ scrollElement: scrollableRef }}
|
||||
className={clsx('table-constrant')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -65,47 +62,3 @@ export const PendingTransactionsDataTable = compose(
|
||||
})),
|
||||
withBankingActions,
|
||||
)(PendingTransactionsDataTableRoot);
|
||||
|
||||
const DashboardConstrantTable = styled(DataTable)`
|
||||
.table {
|
||||
.thead {
|
||||
.th {
|
||||
background: #fff;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
font-size: 13px;i
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody {
|
||||
.tr:last-child .td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CashflowTransactionsTable = styled(DashboardConstrantTable)`
|
||||
.table .tbody {
|
||||
.tbody-inner .tr.no-results {
|
||||
.td {
|
||||
padding: 2rem 0;
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
font-weight: 400;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody-inner {
|
||||
.tr .td:not(:first-child) {
|
||||
border-left: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.td-description {
|
||||
color: #5f6b7c;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -48,6 +48,7 @@ export function usePendingTransactionsTableColumns() {
|
||||
textOverview: true,
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
money: true
|
||||
},
|
||||
{
|
||||
id: 'withdrawal',
|
||||
@@ -58,6 +59,7 @@ export function usePendingTransactionsTableColumns() {
|
||||
textOverview: true,
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
money: true
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
withBankingActions,
|
||||
} from '../../withBankingActions';
|
||||
import styles from './RecognizedTransactionsTable.module.scss';
|
||||
import { BankAccountDataTable } from '../components/BankAccountDataTable';
|
||||
|
||||
interface RecognizedTransactionsTableProps extends WithBankingActionsProps {}
|
||||
|
||||
@@ -83,7 +84,7 @@ function RecognizedTransactionsTableRoot({
|
||||
};
|
||||
|
||||
return (
|
||||
<CashflowTransactionsTable
|
||||
<BankAccountDataTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={recognizedTransactions}
|
||||
@@ -100,14 +101,12 @@ function RecognizedTransactionsTableRoot({
|
||||
ContextMenu={ActionsMenu}
|
||||
onCellClick={handleCellClick}
|
||||
// #TableVirtualizedListRows props.
|
||||
vListrowHeight={'small' == 'small' ? 32 : 40}
|
||||
vListrowHeight={40}
|
||||
vListOverscanRowCount={0}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
windowScrollerProps={{ scrollElement: scrollableRef }}
|
||||
noResults={<RecognizedTransactionsTableNoResults />}
|
||||
className="table-constrant"
|
||||
payload={{
|
||||
onExclude: handleExcludeClick,
|
||||
onCategorize: handleCategorizeClick,
|
||||
@@ -120,45 +119,6 @@ export const RecognizedTransactionsTable = compose(withBankingActions)(
|
||||
RecognizedTransactionsTableRoot,
|
||||
);
|
||||
|
||||
const DashboardConstrantTable = styled(DataTable)`
|
||||
.table {
|
||||
.thead {
|
||||
.th {
|
||||
background: #fff;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody {
|
||||
.tr:last-child .td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CashflowTransactionsTable = styled(DashboardConstrantTable)`
|
||||
.table .tbody {
|
||||
.tbody-inner .tr.no-results {
|
||||
.td {
|
||||
padding: 2rem 0;
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
font-weight: 400;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody-inner {
|
||||
.tr .td {
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
function RecognizedTransactionsTableNoResults() {
|
||||
return (
|
||||
<Stack spacing={12} className={styles.emptyState}>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
import { Classes } from '@blueprintjs/core';
|
||||
import { Group, Icon } from '@/components';
|
||||
import { getColumnWidth } from '@/utils';
|
||||
import React from 'react';
|
||||
import { useRecognizedTransactionsBoot } from './RecognizedTransactionsTableBoot';
|
||||
|
||||
const getReportColWidth = (data, accessor, headerText) => {
|
||||
@@ -28,10 +30,6 @@ const recognizeAccessor = (transaction) => {
|
||||
);
|
||||
};
|
||||
|
||||
const descriptionAccessor = (transaction) => {
|
||||
return <span style={{ color: '#5F6B7C' }}>{transaction.description}</span>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve uncategorized transactions columns table.
|
||||
*/
|
||||
@@ -59,7 +57,8 @@ export function useUncategorizedTransactionsColumns() {
|
||||
},
|
||||
{
|
||||
Header: 'Description',
|
||||
accessor: descriptionAccessor,
|
||||
accessor: 'description',
|
||||
className: clsx(Classes.TEXT_MUTED),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
@@ -82,12 +81,14 @@ export function useUncategorizedTransactionsColumns() {
|
||||
accessor: 'formatted_deposit_amount',
|
||||
align: 'right',
|
||||
width: depositWidth,
|
||||
money: true
|
||||
},
|
||||
{
|
||||
Header: 'Withdrawal',
|
||||
accessor: 'formatted_withdrawal_amount',
|
||||
align: 'right',
|
||||
width: withdrawalWidth,
|
||||
money: true
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import {
|
||||
DataTable,
|
||||
TableFastCell,
|
||||
TableSkeletonRows,
|
||||
TableSkeletonHeader,
|
||||
TableVirtualizedListRows,
|
||||
FormattedMessage as T,
|
||||
AppToaster,
|
||||
} from '@/components';
|
||||
import { TABLES } from '@/constants/tables';
|
||||
import { ActionsMenu } from './components';
|
||||
import { BankAccountDataTable } from '../components/BankAccountDataTable';
|
||||
|
||||
import withSettings from '@/containers/Settings/withSettings';
|
||||
import { withBankingActions } from '../../withBankingActions';
|
||||
import { withBanking } from '../../withBanking';
|
||||
|
||||
import { useMemorizedColumnsWidths } from '@/hooks';
|
||||
import { useAccountUncategorizedTransactionsContext } from '../AllTransactionsUncategorizedBoot';
|
||||
@@ -25,7 +24,6 @@ import { useAccountUncategorizedTransactionsColumns } from './hooks';
|
||||
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
import { withBanking } from '../../withBanking';
|
||||
import styles from './AccountTransactionsUncategorizedTable.module.scss';
|
||||
|
||||
/**
|
||||
@@ -48,7 +46,6 @@ function AccountTransactionsDataTable({
|
||||
}) {
|
||||
// Retrieve table columns.
|
||||
const columns = useAccountUncategorizedTransactionsColumns();
|
||||
|
||||
const { scrollableRef } = useAccountTransactionsContext();
|
||||
|
||||
// Retrieve list context.
|
||||
@@ -100,7 +97,7 @@ function AccountTransactionsDataTable({
|
||||
};
|
||||
|
||||
return (
|
||||
<CashflowTransactionsTable
|
||||
<BankAccountDataTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={uncategorizedTransactions || []}
|
||||
@@ -119,7 +116,7 @@ function AccountTransactionsDataTable({
|
||||
ContextMenu={ActionsMenu}
|
||||
onCellClick={handleCellClick}
|
||||
// #TableVirtualizedListRows props.
|
||||
vListrowHeight={cashflowTansactionsTableSize === 'small' ? 32 : 40}
|
||||
vListrowHeight={cashflowTansactionsTableSize === 'small' ? 34 : 40}
|
||||
vListOverscanRowCount={0}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
@@ -132,7 +129,7 @@ function AccountTransactionsDataTable({
|
||||
}}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
windowScrollerProps={{ scrollElement: scrollableRef }}
|
||||
className={clsx('table-constrant', styles.table, {
|
||||
className={clsx(styles.table, {
|
||||
[styles.showCategorizeColumn]: enableMultipleCategorization,
|
||||
})}
|
||||
/>
|
||||
@@ -151,47 +148,3 @@ export default compose(
|
||||
}),
|
||||
),
|
||||
)(AccountTransactionsDataTable);
|
||||
|
||||
const DashboardConstrantTable = styled(DataTable)`
|
||||
.table {
|
||||
.thead {
|
||||
.th {
|
||||
background: #fff;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
font-size: 13px;i
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody {
|
||||
.tr:last-child .td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CashflowTransactionsTable = styled(DashboardConstrantTable)`
|
||||
.table .tbody {
|
||||
.tbody-inner .tr.no-results {
|
||||
.td {
|
||||
padding: 2rem 0;
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
font-weight: 400;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody-inner {
|
||||
.tr .td:not(:first-child) {
|
||||
border-left: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.td-description {
|
||||
color: #5f6b7c;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import clsx from 'classnames';
|
||||
import {
|
||||
Checkbox,
|
||||
Classes,
|
||||
Intent,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
@@ -97,6 +99,7 @@ export function useAccountUncategorizedTransactionsColumns() {
|
||||
width: 160,
|
||||
textOverview: true,
|
||||
clickable: true,
|
||||
className: clsx(Classes.TEXT_MUTED),
|
||||
},
|
||||
{
|
||||
id: 'payee',
|
||||
@@ -123,21 +126,21 @@ export function useAccountUncategorizedTransactionsColumns() {
|
||||
id: 'deposit',
|
||||
Header: intl.get('banking.label.deposit'),
|
||||
accessor: 'formatted_deposit_amount',
|
||||
width: 40,
|
||||
className: 'deposit',
|
||||
textOverview: true,
|
||||
align: 'right',
|
||||
width: 40,
|
||||
textOverview: true,
|
||||
clickable: true,
|
||||
money: true
|
||||
},
|
||||
{
|
||||
id: 'withdrawal',
|
||||
Header: intl.get('banking.label.withdrawal'),
|
||||
accessor: 'formatted_withdrawal_amount',
|
||||
className: 'withdrawal',
|
||||
width: 40,
|
||||
textOverview: true,
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
money: true
|
||||
},
|
||||
{
|
||||
id: 'categorize_include',
|
||||
|
||||
@@ -123,6 +123,7 @@ export function useAccountTransactionsColumns() {
|
||||
textOverview: true,
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
money: true,
|
||||
},
|
||||
{
|
||||
id: 'withdrawal',
|
||||
@@ -133,16 +134,18 @@ export function useAccountTransactionsColumns() {
|
||||
textOverview: true,
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
money: true,
|
||||
},
|
||||
{
|
||||
id: 'running_balance',
|
||||
Header: intl.get('banking.label.running_balance'),
|
||||
accessor: 'formatted_running_balance',
|
||||
className: 'running_balance',
|
||||
align: 'right',
|
||||
width: 150,
|
||||
textOverview: true,
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
money: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
.root {
|
||||
:global .table{
|
||||
.thead {
|
||||
.th {
|
||||
background: #fff;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody-inner .tr.no-results {
|
||||
.td {
|
||||
padding: 2rem 0;
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
font-weight: 400;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody-inner {
|
||||
.tr .td{
|
||||
border-bottom: 1px solid #ececec;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import clsx from 'classnames';
|
||||
import { DataTable } from '@/components';
|
||||
import styles from './BankAccountDataTable.module.scss';
|
||||
|
||||
interface BankAccountDataTableProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function BankAccountDataTable({
|
||||
className,
|
||||
...props
|
||||
}: BankAccountDataTableProps) {
|
||||
return (
|
||||
<DataTable
|
||||
{...props}
|
||||
className={clsx('table-constrant', styles.root, className)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// @ts-nocheck
|
||||
import { Suspense } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import * as R from 'ramda';
|
||||
import { Spinner } from '@blueprintjs/core';
|
||||
import { CategorizeTransactionBoot } from './CategorizeTransactionBoot';
|
||||
import { CategorizeTransactionForm } from './CategorizeTransactionForm';
|
||||
import { withBanking } from '@/containers/CashFlow/withBanking';
|
||||
@@ -13,7 +15,9 @@ function CategorizeTransactionContentRoot({
|
||||
uncategorizedTransactionsIds={transactionsToCategorizeIdsSelected}
|
||||
>
|
||||
<CategorizeTransactionDrawerBody>
|
||||
<CategorizeTransactionForm />
|
||||
<Suspense fallback={<Spinner size={40} />}>
|
||||
<CategorizeTransactionForm />
|
||||
</Suspense>
|
||||
</CategorizeTransactionDrawerBody>
|
||||
</CategorizeTransactionBoot>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { createContext } from 'react';
|
||||
import { defaultTo } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import { useGetBankTransactionsMatches } from '@/hooks/query/bank-rules';
|
||||
import { Spinner } from '@blueprintjs/core';
|
||||
|
||||
interface MatchingTransactionBootValues {
|
||||
isMatchingTransactionsLoading: boolean;
|
||||
@@ -52,6 +53,11 @@ function MatchingTransactionBoot({
|
||||
matches,
|
||||
} as MatchingTransactionBootValues;
|
||||
|
||||
const isLoading = isMatchingTransactionsLoading;
|
||||
|
||||
if (isLoading) {
|
||||
return <Spinner size={40} />;
|
||||
}
|
||||
return <RuleFormBootContext.Provider value={provider} {...props} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React, { useMemo } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import clsx from 'classnames';
|
||||
import {
|
||||
Menu,
|
||||
MenuItem,
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
import { Can, Icon, Money, If, AvatarCell } from '@/components';
|
||||
import { CustomerAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
import { safeCallback } from '@/utils';
|
||||
import { CLASSES } from '@/constants';
|
||||
|
||||
/**
|
||||
* Actions menu.
|
||||
@@ -140,7 +142,7 @@ export function useCustomersTableColumns() {
|
||||
id: 'company_name',
|
||||
Header: intl.get('company_name'),
|
||||
accessor: 'company_name',
|
||||
className: 'company_name',
|
||||
className: clsx('company_name', CLASSES.TEXT_MUTED),
|
||||
width: 150,
|
||||
clickable: true,
|
||||
},
|
||||
@@ -148,9 +150,9 @@ export function useCustomersTableColumns() {
|
||||
id: 'work_phone',
|
||||
Header: intl.get('phone_number'),
|
||||
accessor: PhoneNumberAccessor,
|
||||
className: 'phone_number',
|
||||
width: 100,
|
||||
clickable: true,
|
||||
className: clsx('phone_number', CLASSES.TEXT_MUTED)
|
||||
},
|
||||
{
|
||||
id: 'note',
|
||||
@@ -159,6 +161,7 @@ export function useCustomersTableColumns() {
|
||||
disableSortBy: true,
|
||||
width: 85,
|
||||
clickable: true,
|
||||
className: clsx(CLASSES.TEXT_MUTED)
|
||||
},
|
||||
{
|
||||
id: 'balance',
|
||||
@@ -167,6 +170,7 @@ export function useCustomersTableColumns() {
|
||||
align: 'right',
|
||||
width: 100,
|
||||
clickable: true,
|
||||
money: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { InputGroup, FormGroup, Position, Classes } from '@blueprintjs/core';
|
||||
import { FormGroup, Position, Classes } from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import { CustomersSelect, FormattedMessage as T } from '@/components';
|
||||
import { CustomersSelect, FInputGroup, FormattedMessage as T } from '@/components';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import {
|
||||
@@ -15,15 +15,14 @@ import {
|
||||
import { customersFieldShouldUpdate, accountsFieldShouldUpdate } from './utils';
|
||||
import {
|
||||
CurrencySelectList,
|
||||
CustomerSelectField,
|
||||
FFormGroup,
|
||||
AccountsSelect,
|
||||
FieldRequiredHint,
|
||||
Hint,
|
||||
} from '@/components';
|
||||
import { ExpensesExchangeRateInputField } from './components';
|
||||
import { ACCOUNT_PARENT_TYPE } from '@/constants/accountTypes';
|
||||
import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||
import { SUPPORTED_EXPENSE_PAYMENT_ACCOUNT_TYPES } from './constants';
|
||||
|
||||
/**
|
||||
* Expense form header.
|
||||
@@ -68,7 +67,7 @@ export default function ExpenseFormHeader() {
|
||||
name={'payment_account_id'}
|
||||
items={accounts}
|
||||
placeholder={<T id={'select_payment_account'} />}
|
||||
filterByParentTypes={[ACCOUNT_PARENT_TYPE.CURRENT_ASSET]}
|
||||
filterByTypes={SUPPORTED_EXPENSE_PAYMENT_ACCOUNT_TYPES}
|
||||
allowCreate={true}
|
||||
fastField={true}
|
||||
shouldUpdate={accountsFieldShouldUpdate}
|
||||
@@ -107,19 +106,15 @@ export default function ExpenseFormHeader() {
|
||||
formGroupProps={{ label: ' ', inline: true }}
|
||||
/>
|
||||
|
||||
<FastField name={'reference_no'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'reference_no'} />}
|
||||
className={classNames('form-group--ref_no', Classes.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reference_no" />}
|
||||
inline={true}
|
||||
>
|
||||
<InputGroup minimal={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/* ----------- Reference No. ----------- */}
|
||||
<FFormGroup
|
||||
name={'reference_no'}
|
||||
label={<T id={'reference_no'} />}
|
||||
inline={true}
|
||||
fastField
|
||||
>
|
||||
<FInputGroup minimal={true} name={'reference_no'} fastField />
|
||||
</FFormGroup>
|
||||
|
||||
{/* ----------- Customer ----------- */}
|
||||
<ExpenseFormCustomerSelect />
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ACCOUNT_TYPE } from "@/constants";
|
||||
|
||||
export const SUPPORTED_EXPENSE_PAYMENT_ACCOUNT_TYPES = [
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.CREDIT_CARD,
|
||||
ACCOUNT_TYPE.OTHER_CURRENT_ASSET,
|
||||
ACCOUNT_TYPE.NON_CURRENT_ASSET,
|
||||
ACCOUNT_TYPE.FIXED_ASSET,
|
||||
];
|
||||
@@ -140,26 +140,25 @@ export function useExpensesTableColumns() {
|
||||
id: 'amount',
|
||||
Header: intl.get('full_amount'),
|
||||
accessor: 'formatted_amount',
|
||||
className: 'amount',
|
||||
align: 'right',
|
||||
width: 150,
|
||||
clickable: true,
|
||||
money: true,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
},
|
||||
{
|
||||
id: 'payment_account',
|
||||
Header: intl.get('payment_account'),
|
||||
accessor: 'payment_account.name',
|
||||
className: 'payment_account',
|
||||
width: 150,
|
||||
clickable: true,
|
||||
className: clsx(CLASSES.TEXT_MUTED),
|
||||
},
|
||||
{
|
||||
id: 'expense_account',
|
||||
Header: intl.get('expense_account'),
|
||||
accessor: ExpenseAccountAccessor,
|
||||
width: 160,
|
||||
className: 'expense_account',
|
||||
disableSortBy: true,
|
||||
clickable: true,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import React, { useMemo } from 'react';
|
||||
import * as R from 'ramda';
|
||||
import { getColumnWidth } from '@/utils';
|
||||
import { Align } from '@/constants';
|
||||
@@ -25,6 +24,7 @@ const currentAccessor = R.curry((data, column) => {
|
||||
className: column.id,
|
||||
width: getColumnWidth(data, accessor, { minWidth: 120 }),
|
||||
align: Align.Right,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ const totalAccessor = R.curry((data, column) => {
|
||||
className: column.key,
|
||||
width: getColumnWidth(data, accessor, { minWidth: 120 }),
|
||||
align: Align.Right,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -51,6 +52,7 @@ const agingPeriodAccessor = R.curry((data, column) => {
|
||||
className: column.key,
|
||||
width: getColumnWidth(data, accessor, { minWidth: 120 }),
|
||||
align: Align.Right,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import * as R from 'ramda';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import { Align } from '@/constants';
|
||||
import { CellTextSpan } from '@/components/Datatable/Cells';
|
||||
import { getColumnWidth } from '@/utils';
|
||||
|
||||
const getTableCellValueAccessor = (index) => `cells[${index}].value`;
|
||||
@@ -12,12 +11,11 @@ const getReportColWidth = (data, accessor, headerText) => {
|
||||
return getColumnWidth(
|
||||
data,
|
||||
accessor,
|
||||
{ magicSpacing: 10, minWidth: 100 },
|
||||
{ magicSpacing: 12, minWidth: 100 },
|
||||
headerText,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Account name column mapper.
|
||||
*/
|
||||
@@ -77,6 +75,7 @@ const dateRangeMapper = R.curry((data, column) => {
|
||||
key: column.key,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
align: isDateColumnHasColumns ? Align.Center : Align.Right,
|
||||
};
|
||||
return R.compose(
|
||||
@@ -104,9 +103,9 @@ const totalMapper = R.curry((data, column) => {
|
||||
Header: column.label,
|
||||
accessor,
|
||||
textOverview: true,
|
||||
Cell: CellTextSpan,
|
||||
width,
|
||||
disableSortBy: true,
|
||||
money: true,
|
||||
align: hasChildren ? Align.Center : Align.Right,
|
||||
};
|
||||
return R.compose(
|
||||
@@ -129,6 +128,7 @@ const percentageOfColumnAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -147,6 +147,7 @@ const percentageOfRowAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -165,6 +166,7 @@ const previousYearAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -183,6 +185,7 @@ const previousYearChangeAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -201,6 +204,7 @@ const previousYearPercentageAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -219,6 +223,7 @@ const previousPeriodAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -237,6 +242,7 @@ const previousPeriodChangeAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -255,6 +261,7 @@ const previousPeriodPercentageAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -30,13 +30,14 @@ const dateRangeMapper = (data, index, column) => ({
|
||||
key: column.key,
|
||||
accessor: `cells[${index}].value`,
|
||||
width: getColumnWidth(data, `cells.${index}.value`, {
|
||||
magicSpacing: 10,
|
||||
magicSpacing: 12,
|
||||
minWidth: 100,
|
||||
}),
|
||||
className: `date-period ${column.key}`,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
align: Align.Right,
|
||||
money: true
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -50,11 +51,12 @@ const totalMapper = (data, index, column) => ({
|
||||
textOverview: true,
|
||||
Cell: CellTextSpan,
|
||||
width: getColumnWidth(data, `cells[${index}].value`, {
|
||||
magicSpacing: 10,
|
||||
magicSpacing: 12,
|
||||
minWidth: 100,
|
||||
}),
|
||||
disableSortBy: true,
|
||||
align: Align.Right,
|
||||
money: true
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,9 +61,10 @@ export const useCustomersTransactionsColumns = () => {
|
||||
textOverview: true,
|
||||
width: getColumnWidth(tableRows, 'cells[5].value', {
|
||||
minWidth: 100,
|
||||
magicSpacing: 10,
|
||||
magicSpacing: 12,
|
||||
}),
|
||||
align: Align.Right,
|
||||
money: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('debit'),
|
||||
@@ -72,9 +73,10 @@ export const useCustomersTransactionsColumns = () => {
|
||||
textOverview: true,
|
||||
width: getColumnWidth(tableRows, 'cells[6].value', {
|
||||
minWidth: 100,
|
||||
magicSpacing: 10,
|
||||
magicSpacing: 12,
|
||||
}),
|
||||
align: Align.Right,
|
||||
money: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('running_balance'),
|
||||
@@ -83,9 +85,10 @@ export const useCustomersTransactionsColumns = () => {
|
||||
textOverview: true,
|
||||
width: getColumnWidth(tableRows, 'cells[7].value', {
|
||||
minWidth: 120,
|
||||
magicSpacing: 10,
|
||||
magicSpacing: 12,
|
||||
}),
|
||||
align: Align.Right,
|
||||
money: true,
|
||||
},
|
||||
],
|
||||
[tableRows],
|
||||
|
||||
@@ -41,6 +41,7 @@ const numericColumnAccessor = R.curry((data, column) => {
|
||||
...column,
|
||||
align: Align.Right,
|
||||
width,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ const numericColumn = R.curry((data, index, column) => ({
|
||||
}),
|
||||
disableSortBy: true,
|
||||
align: Align.Right,
|
||||
money: true,
|
||||
}));
|
||||
|
||||
const columnsMapper = R.curry((data, index, column) => ({
|
||||
|
||||
@@ -41,6 +41,7 @@ const numericColumnAccessor = R.curry((data, column) => {
|
||||
return {
|
||||
...column,
|
||||
align: Align.Right,
|
||||
money: true,
|
||||
width,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -41,6 +41,7 @@ const numericColumnAccessor = R.curry((data, column) => {
|
||||
return {
|
||||
...column,
|
||||
align: Align.Right,
|
||||
money: true,
|
||||
width,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@ import * as R from 'ramda';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import { Align } from '@/constants';
|
||||
import { CellTextSpan } from '@/components/Datatable/Cells';
|
||||
import { getColumnWidth } from '@/utils';
|
||||
|
||||
const getTableCellValueAccessor = (index) => `cells[${index}].value`;
|
||||
@@ -34,6 +33,7 @@ const percentageOfIncomeAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -52,6 +52,7 @@ const percentageOfExpenseAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -70,6 +71,7 @@ const percentageOfColumnAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -88,6 +90,7 @@ const percentageOfRowAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -106,6 +109,7 @@ const previousYearAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -124,6 +128,7 @@ const previousYearChangeAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -142,6 +147,7 @@ const previousYearPercentageAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -160,6 +166,7 @@ const previousPeriodAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -178,6 +185,7 @@ const previousPeriodChangeAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -196,6 +204,7 @@ const previousPeriodPercentageAccessor = R.curry((data, column) => {
|
||||
align: Align.Right,
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -274,10 +283,10 @@ const totalColumn = R.curry((data, column) => {
|
||||
Header: column.label,
|
||||
accessor,
|
||||
textOverview: true,
|
||||
Cell: CellTextSpan,
|
||||
width,
|
||||
disableSortBy: true,
|
||||
align: hasChildren ? Align.Center : Align.Right,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -338,6 +347,7 @@ const dateRangeColumn = R.curry((data, column) => {
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
align: isDateColumnHasColumns ? Align.Center : Align.Right,
|
||||
money: true
|
||||
};
|
||||
return R.compose(
|
||||
R.when(
|
||||
|
||||
@@ -41,6 +41,7 @@ const numericColumnAccessor = R.curry((data, column) => {
|
||||
...column,
|
||||
align: Align.Right,
|
||||
width,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ const numericColumnAccessor = R.curry((data, column) => {
|
||||
...column,
|
||||
align: Align.Right,
|
||||
width,
|
||||
money: true
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ const amountAccessor = R.curry((data, column) => {
|
||||
minWidth: AMOUNT_COLUMNS_MIN_WIDTH,
|
||||
}),
|
||||
align: Align.Right,
|
||||
money: true,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ const percentageColumnAccessor = () => ({
|
||||
width: 140,
|
||||
textOverview: true,
|
||||
align: Align.Right,
|
||||
money: true
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -61,6 +62,7 @@ const totalColumnAccessor = () => ({
|
||||
width: 140,
|
||||
textOverview: true,
|
||||
align: Align.Right,
|
||||
money: true
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
useVendorsTransactionsCsvExport,
|
||||
useVendorsTransactionsXlsxExport,
|
||||
} from '@/hooks/query';
|
||||
import { Align } from '@/constants';
|
||||
|
||||
/**
|
||||
* Retrieve vendors transactions columns.
|
||||
@@ -63,6 +64,8 @@ export const useVendorsTransactionsColumns = () => {
|
||||
minWidth: 100,
|
||||
magicSpacing: 10,
|
||||
}),
|
||||
money: true,
|
||||
align: Align.Right,
|
||||
},
|
||||
{
|
||||
Header: intl.get('debit'),
|
||||
@@ -73,6 +76,8 @@ export const useVendorsTransactionsColumns = () => {
|
||||
minWidth: 100,
|
||||
magicSpacing: 10,
|
||||
}),
|
||||
money: true,
|
||||
align: Align.Right,
|
||||
},
|
||||
{
|
||||
Header: intl.get('running_balance'),
|
||||
@@ -83,6 +88,8 @@ export const useVendorsTransactionsColumns = () => {
|
||||
minWidth: 120,
|
||||
magicSpacing: 10,
|
||||
}),
|
||||
money: true,
|
||||
align: Align.Right,
|
||||
},
|
||||
],
|
||||
[table],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import clsx from 'classnames';
|
||||
import { isNumber } from 'lodash';
|
||||
import {
|
||||
Menu,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
Position,
|
||||
Button,
|
||||
Popover,
|
||||
Classes,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, Icon, Money, If, Can } from '@/components';
|
||||
import { isBlank, safeCallback } from '@/utils';
|
||||
@@ -182,7 +184,7 @@ export const useItemsTableColumns = () => {
|
||||
id: 'code',
|
||||
Header: intl.get('item_code'),
|
||||
accessor: 'code',
|
||||
className: 'code',
|
||||
className: clsx(Classes.TEXT_MUTED),
|
||||
width: 120,
|
||||
clickable: true,
|
||||
},
|
||||
@@ -198,7 +200,7 @@ export const useItemsTableColumns = () => {
|
||||
id: 'category',
|
||||
Header: intl.get('category'),
|
||||
accessor: 'category.name',
|
||||
className: 'category',
|
||||
className: clsx(Classes.TEXT_MUTED),
|
||||
width: 150,
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
@@ -210,6 +212,7 @@ export const useItemsTableColumns = () => {
|
||||
align: 'right',
|
||||
width: 150,
|
||||
clickable: true,
|
||||
money: true,
|
||||
},
|
||||
{
|
||||
id: 'cost_price',
|
||||
@@ -218,6 +221,7 @@ export const useItemsTableColumns = () => {
|
||||
align: 'right',
|
||||
width: 150,
|
||||
clickable: true,
|
||||
money: true,
|
||||
},
|
||||
{
|
||||
id: 'quantity_on_hand',
|
||||
@@ -227,6 +231,7 @@ export const useItemsTableColumns = () => {
|
||||
align: 'right',
|
||||
width: 140,
|
||||
clickable: true,
|
||||
money: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
||||
@@ -189,6 +189,7 @@ export function useBillsTableColumns() {
|
||||
width: 120,
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
money: true,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -154,6 +154,7 @@ export function useVendorsCreditNoteTableColumns() {
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
disableSortBy: true,
|
||||
money: true,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -12,8 +12,13 @@ import {
|
||||
Button,
|
||||
} from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
|
||||
import { FormattedMessage as T, VendorsSelect } from '@/components';
|
||||
import { FastField, useFormikContext, ErrorMessage } from 'formik';
|
||||
import {
|
||||
FInputGroup,
|
||||
FMoneyInputGroup,
|
||||
FormattedMessage as T,
|
||||
VendorsSelect,
|
||||
} from '@/components';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
|
||||
import {
|
||||
@@ -25,7 +30,6 @@ import {
|
||||
Hint,
|
||||
Icon,
|
||||
VendorDrawerLink,
|
||||
MoneyInputGroup,
|
||||
} from '@/components';
|
||||
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
||||
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
|
||||
@@ -49,7 +53,7 @@ import { accountsFieldShouldUpdate, vendorsFieldShouldUpdate } from './utils';
|
||||
function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
|
||||
// Formik form context.
|
||||
const {
|
||||
values: { entries },
|
||||
values: { entries, currency_code },
|
||||
setFieldValue,
|
||||
} = useFormikContext();
|
||||
|
||||
@@ -115,47 +119,34 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Full amount ------------ */}
|
||||
<Field name={'amount'}>
|
||||
{({
|
||||
form: {
|
||||
values: { currency_code, entries },
|
||||
},
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={<T id={'full_amount'} />}
|
||||
inline={true}
|
||||
className={('form-group--full-amount', Classes.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
labelInfo={<Hint />}
|
||||
helperText={<ErrorMessage name="amount" />}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={currency_code} />
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
onChange={(value) => {
|
||||
setFieldValue('amount', value);
|
||||
}}
|
||||
onBlurValue={onFullAmountBlur}
|
||||
/>
|
||||
</ControlGroup>
|
||||
<FFormGroup
|
||||
name={'amount'}
|
||||
label={<T id={'full_amount'} />}
|
||||
inline={true}
|
||||
labelInfo={<Hint />}
|
||||
fastField
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={currency_code} />
|
||||
<FMoneyInputGroup
|
||||
fastField
|
||||
name={'amount'}
|
||||
onBlurValue={onFullAmountBlur}
|
||||
/>
|
||||
</ControlGroup>
|
||||
|
||||
{!isEmpty(entries) && (
|
||||
<Button
|
||||
onClick={handleReceiveFullAmountClick}
|
||||
className={'receive-full-amount'}
|
||||
small={true}
|
||||
minimal={true}
|
||||
>
|
||||
<T id={'receive_full_amount'} /> (
|
||||
<Money amount={payableFullAmount} currency={currency_code} />)
|
||||
</Button>
|
||||
)}
|
||||
</FormGroup>
|
||||
{!isEmpty(entries) && (
|
||||
<Button
|
||||
onClick={handleReceiveFullAmountClick}
|
||||
className={'receive-full-amount'}
|
||||
small={true}
|
||||
minimal={true}
|
||||
>
|
||||
<T id={'receive_full_amount'} /> (
|
||||
<Money amount={payableFullAmount} currency={currency_code} />)
|
||||
</Button>
|
||||
)}
|
||||
</Field>
|
||||
</FFormGroup>
|
||||
|
||||
{/* ------------ Payment number ------------ */}
|
||||
<FastField name={'payment_number'}>
|
||||
@@ -203,23 +194,14 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
|
||||
</FFormGroup>
|
||||
|
||||
{/* ------------ Reference ------------ */}
|
||||
<FastField name={'reference'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'reference'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--reference', Classes.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reference" />}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
minimal={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FFormGroup
|
||||
name={'reference'}
|
||||
label={<T id={'reference'} />}
|
||||
inline={true}
|
||||
fastField
|
||||
>
|
||||
<FInputGroup name={'reference'} minimal={true} fastField />
|
||||
</FFormGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ export const defaultPaymentMade = {
|
||||
|
||||
export const transformToEditForm = (paymentMade, paymentMadeEntries) => {
|
||||
const attachments = transformAttachmentsToForm(paymentMade);
|
||||
const appliedAmount = safeSumBy(paymentMadeEntries, 'payment_amount');
|
||||
|
||||
return {
|
||||
...transformToForm(paymentMade, defaultPaymentMade),
|
||||
|
||||
@@ -117,6 +117,7 @@ export function usePaymentMadesTableColumns() {
|
||||
className: 'amount',
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
money: true,
|
||||
},
|
||||
{
|
||||
id: 'reference_no',
|
||||
|
||||
@@ -207,6 +207,7 @@ export function useEstiamtesTableColumns() {
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
money: true
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
|
||||
@@ -229,7 +229,6 @@ export function useInvoicesTableColumns() {
|
||||
Header: intl.get('invoice_no__'),
|
||||
accessor: 'invoice_no',
|
||||
width: 100,
|
||||
className: 'invoice_no',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
@@ -241,6 +240,7 @@ export function useInvoicesTableColumns() {
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
},
|
||||
{
|
||||
@@ -266,7 +266,6 @@ export function useInvoicesTableColumns() {
|
||||
Header: intl.get('reference_no'),
|
||||
accessor: 'reference_no',
|
||||
width: 90,
|
||||
className: 'reference_no',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ const Schema = Yup.object().shape({
|
||||
deposit_account_id: Yup.number()
|
||||
.required()
|
||||
.label(intl.get('deposit_account_')),
|
||||
full_amount: Yup.number().nullable(),
|
||||
amount: Yup.number().required().label('Amount'),
|
||||
payment_receive_no: Yup.string()
|
||||
.nullable()
|
||||
.max(DATATYPES_LENGTH.STRING)
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function PaymentReceiveFormAlerts() {
|
||||
const handleClearingAllLines = () => {
|
||||
const newEntries = clearAllPaymentEntries(entries);
|
||||
setFieldValue('entries', newEntries);
|
||||
setFieldValue('full_amount', '');
|
||||
setFieldValue('amount', '');
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -12,12 +12,13 @@ import {
|
||||
} from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { isEmpty, toSafeInteger } from 'lodash';
|
||||
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
|
||||
import { FastField, useFormikContext, ErrorMessage } from 'formik';
|
||||
|
||||
import {
|
||||
FeatureCan,
|
||||
CustomersSelect,
|
||||
FormattedMessage as T,
|
||||
FMoneyInputGroup,
|
||||
} from '@/components';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import {
|
||||
@@ -32,7 +33,6 @@ import {
|
||||
AccountsSelect,
|
||||
FieldRequiredHint,
|
||||
Icon,
|
||||
MoneyInputGroup,
|
||||
InputPrependText,
|
||||
CustomerDrawerLink,
|
||||
Hint,
|
||||
@@ -64,7 +64,7 @@ export default function PaymentReceiveHeaderFields() {
|
||||
|
||||
// Formik form context.
|
||||
const {
|
||||
values: { entries },
|
||||
values: { entries, currency_code },
|
||||
setFieldValue,
|
||||
} = useFormikContext();
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function PaymentReceiveHeaderFields() {
|
||||
const fullAmount = safeSumBy(newEntries, 'payment_amount');
|
||||
|
||||
setFieldValue('entries', newEntries);
|
||||
setFieldValue('full_amount', fullAmount);
|
||||
setFieldValue('amount', fullAmount);
|
||||
};
|
||||
// Handles the full-amount field blur.
|
||||
const onFullAmountBlur = (value) => {
|
||||
@@ -124,48 +124,34 @@ export default function PaymentReceiveHeaderFields() {
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Full amount ------------ */}
|
||||
<Field name={'amount'}>
|
||||
{({
|
||||
form: {
|
||||
setFieldValue,
|
||||
values: { currency_code, entries },
|
||||
},
|
||||
field: { value, onChange },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={<T id={'full_amount'} />}
|
||||
inline={true}
|
||||
className={('form-group--full-amount', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
labelInfo={<Hint />}
|
||||
helperText={<ErrorMessage name="full_amount" />}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={currency_code} />
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
onChange={(value) => {
|
||||
setFieldValue('amount', value);
|
||||
}}
|
||||
onBlurValue={onFullAmountBlur}
|
||||
/>
|
||||
</ControlGroup>
|
||||
<FFormGroup
|
||||
name={'amount'}
|
||||
label={<T id={'full_amount'} />}
|
||||
inline={true}
|
||||
labelInfo={<Hint />}
|
||||
fastField
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={currency_code} />
|
||||
<FMoneyInputGroup
|
||||
name={'amount'}
|
||||
onBlurValue={onFullAmountBlur}
|
||||
fastField
|
||||
/>
|
||||
</ControlGroup>
|
||||
|
||||
{!isEmpty(entries) && (
|
||||
<Button
|
||||
onClick={handleReceiveFullAmountClick}
|
||||
className={'receive-full-amount'}
|
||||
small={true}
|
||||
minimal={true}
|
||||
>
|
||||
<T id={'receive_full_amount'} /> (
|
||||
<Money amount={totalDueAmount} currency={currency_code} />)
|
||||
</Button>
|
||||
)}
|
||||
</FormGroup>
|
||||
{!isEmpty(entries) && (
|
||||
<Button
|
||||
onClick={handleReceiveFullAmountClick}
|
||||
className={'receive-full-amount'}
|
||||
small={true}
|
||||
minimal={true}
|
||||
>
|
||||
<T id={'receive_full_amount'} /> (
|
||||
<Money amount={totalDueAmount} currency={currency_code} />)
|
||||
</Button>
|
||||
)}
|
||||
</Field>
|
||||
</FFormGroup>
|
||||
|
||||
{/* ------------ Payment receive no. ------------ */}
|
||||
<PaymentReceivePaymentNoField />
|
||||
@@ -197,23 +183,14 @@ export default function PaymentReceiveHeaderFields() {
|
||||
</FFormGroup>
|
||||
|
||||
{/* ------------ Reference No. ------------ */}
|
||||
<FastField name={'reference_no'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'reference'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--reference', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reference" />}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
minimal={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FFormGroup
|
||||
name={'reference_no'}
|
||||
label={<T id={'reference'} />}
|
||||
inline={true}
|
||||
fastField
|
||||
>
|
||||
<InputGroup name={'reference_no'} minimal={true} fastField />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Project name -----------*/}
|
||||
<FeatureCan feature={Features.Projects}>
|
||||
|
||||
@@ -71,7 +71,6 @@ export const defaultRequestPayment = {
|
||||
*/
|
||||
export const transformToEditForm = (paymentReceive, paymentReceiveEntries) => ({
|
||||
...transformToForm(paymentReceive, defaultPaymentReceive),
|
||||
full_amount: safeSumBy(paymentReceiveEntries, 'payment_amount'),
|
||||
entries: [
|
||||
...paymentReceiveEntries.map((paymentReceiveEntry) => ({
|
||||
...transformToForm(paymentReceiveEntry, defaultPaymentReceiveEntry),
|
||||
|
||||
@@ -113,6 +113,7 @@ export function usePaymentReceivesColumns() {
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -160,6 +160,7 @@ export function useReceiptsTableColumns() {
|
||||
align: 'right',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
money: true,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Intent,
|
||||
Classes,
|
||||
} from '@blueprintjs/core';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { Can, Icon, Money, If, AvatarCell } from '@/components';
|
||||
import { VendorAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
@@ -163,7 +164,7 @@ export function useVendorsTableColumns() {
|
||||
id: 'company_name',
|
||||
Header: intl.get('company_name'),
|
||||
accessor: 'company_name',
|
||||
className: 'company_name',
|
||||
className: clsx(Classes.TEXT_MUTED),
|
||||
width: 150,
|
||||
clickable: true,
|
||||
},
|
||||
@@ -171,7 +172,7 @@ export function useVendorsTableColumns() {
|
||||
id: 'work_phone',
|
||||
Header: intl.get('phone_number'),
|
||||
accessor: PhoneNumberAccessor,
|
||||
className: 'work_phone',
|
||||
className: clsx(Classes.TEXT_MUTED),
|
||||
width: 100,
|
||||
clickable: true,
|
||||
},
|
||||
@@ -180,6 +181,7 @@ export function useVendorsTableColumns() {
|
||||
Header: intl.get('note'),
|
||||
accessor: NoteAccessor,
|
||||
disableSortBy: true,
|
||||
className: clsx(Classes.TEXT_MUTED),
|
||||
width: 85,
|
||||
clickable: true,
|
||||
},
|
||||
@@ -190,6 +192,7 @@ export function useVendorsTableColumns() {
|
||||
align: 'right',
|
||||
width: 100,
|
||||
clickable: true,
|
||||
money: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
||||
63
packages/webapp/src/hooks/useMediaQuery.ts
Normal file
63
packages/webapp/src/hooks/useMediaQuery.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
export interface UseMediaQueryOptions {
|
||||
getInitialValueInEffect: boolean;
|
||||
}
|
||||
|
||||
type MediaQueryCallback = (event: { matches: boolean; media: string }) => void;
|
||||
|
||||
/**
|
||||
* Older versions of Safari (shipped withCatalina and before) do not support addEventListener on matchMedia
|
||||
* https://stackoverflow.com/questions/56466261/matchmedia-addlistener-marked-as-deprecated-addeventlistener-equivalent
|
||||
* */
|
||||
function attachMediaListener(
|
||||
query: MediaQueryList,
|
||||
callback: MediaQueryCallback,
|
||||
) {
|
||||
try {
|
||||
query.addEventListener('change', callback);
|
||||
return () => query.removeEventListener('change', callback);
|
||||
} catch (e) {
|
||||
query.addListener(callback);
|
||||
return () => query.removeListener(callback);
|
||||
}
|
||||
}
|
||||
|
||||
function getInitialValue(query: string, initialValue?: boolean) {
|
||||
if (typeof initialValue === 'boolean') {
|
||||
return initialValue;
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && 'matchMedia' in window) {
|
||||
return window.matchMedia(query).matches;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function useMediaQuery(
|
||||
query: string,
|
||||
initialValue?: boolean,
|
||||
{ getInitialValueInEffect }: UseMediaQueryOptions = {
|
||||
getInitialValueInEffect: true,
|
||||
},
|
||||
) {
|
||||
const [matches, setMatches] = useState(
|
||||
getInitialValueInEffect ? initialValue : getInitialValue(query),
|
||||
);
|
||||
const queryRef = useRef<MediaQueryList>();
|
||||
|
||||
useEffect(() => {
|
||||
if ('matchMedia' in window) {
|
||||
queryRef.current = window.matchMedia(query);
|
||||
setMatches(queryRef.current.matches);
|
||||
return attachMediaListener(queryRef.current, (event) =>
|
||||
setMatches(event.matches),
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [query]);
|
||||
|
||||
return matches;
|
||||
}
|
||||
@@ -642,4 +642,10 @@ export default {
|
||||
],
|
||||
viewBox: '0 0 16 16',
|
||||
},
|
||||
share: {
|
||||
path: [
|
||||
'M10.99 13.99h-9v-9h4.76l2-2H.99c-.55 0-1 .45-1 1v11c0 .55.45 1 1 1h11c.55 0 1-.45 1-1V7.24l-2 2v4.75zm4-14h-5c-.55 0-1 .45-1 1s.45 1 1 1h2.59L7.29 7.28a1 1 0 00-.3.71 1.003 1.003 0 001.71.71l5.29-5.29V6c0 .55.45 1 1 1s1-.45 1-1V1c0-.56-.45-1.01-1-1.01z'
|
||||
],
|
||||
viewBox: '0 0 16 16',
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
border-spacing: 0;
|
||||
min-width: 100%;
|
||||
display: block;
|
||||
color: #101219;
|
||||
|
||||
.thead .thead-inner,
|
||||
.tbody .tbody-inner {
|
||||
@@ -21,7 +22,7 @@
|
||||
padding: 0.68rem 0.5rem;
|
||||
background: #f5f5f5;
|
||||
font-size: 14px;
|
||||
color: #4E5B6F;
|
||||
color: #424853;
|
||||
font-weight: 400;
|
||||
border-bottom: 1px solid #d2dde2;
|
||||
|
||||
@@ -180,9 +181,9 @@
|
||||
}
|
||||
|
||||
.tr .td {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
border-bottom: 1px solid #ececec;
|
||||
align-items: center;
|
||||
color: #101219;
|
||||
|
||||
|
||||
.placeholder {
|
||||
color: #a0a0a0;
|
||||
@@ -377,6 +378,8 @@
|
||||
.table-constrant,
|
||||
.table--constrant {
|
||||
.table {
|
||||
color: #000;
|
||||
|
||||
.thead {
|
||||
.tr:first-of-type .th {
|
||||
border-top: 1px solid #000000;
|
||||
@@ -394,7 +397,6 @@
|
||||
background: #fff;
|
||||
padding: 0.5rem 0.5rem;
|
||||
border-bottom: 0;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user