mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
Merge branch 'develop' into more-e2e-test-cases
This commit is contained in:
@@ -22,6 +22,9 @@
|
|||||||
"field.status.unpaid": "Unpaid",
|
"field.status.unpaid": "Unpaid",
|
||||||
"field.status.opened": "Opened",
|
"field.status.opened": "Opened",
|
||||||
"field.status.draft": "Draft",
|
"field.status.draft": "Draft",
|
||||||
"field.created_at": "Created At"
|
"field.created_at": "Created At",
|
||||||
|
"allocation_method": "Allocation Method",
|
||||||
|
"allocation_method.quantity": "Quantity",
|
||||||
|
"allocation_method.value": "Valuation"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,6 @@ import { ClsMiddleware } from 'nestjs-cls';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import './utils/moment-mysql';
|
import './utils/moment-mysql';
|
||||||
import { AppModule } from './modules/App/App.module';
|
import { AppModule } from './modules/App/App.module';
|
||||||
import { ServiceErrorFilter } from './common/filters/service-error.filter';
|
|
||||||
import { ModelHasRelationsFilter } from './common/filters/model-has-relations.filter';
|
|
||||||
import { ValidationPipe } from './common/pipes/ClassValidation.pipe';
|
|
||||||
import { ToJsonInterceptor } from './common/interceptors/to-json.interceptor';
|
|
||||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||||
|
|
||||||
global.__public_dirname = path.join(__dirname, '..', 'public');
|
global.__public_dirname = path.join(__dirname, '..', 'public');
|
||||||
@@ -25,11 +21,6 @@ async function bootstrap() {
|
|||||||
// create and mount the middleware manually here
|
// create and mount the middleware manually here
|
||||||
app.use(new ClsMiddleware({}).use);
|
app.use(new ClsMiddleware({}).use);
|
||||||
|
|
||||||
app.useGlobalInterceptors(new ToJsonInterceptor());
|
|
||||||
|
|
||||||
// use the validation pipe globally
|
|
||||||
app.useGlobalPipes(new ValidationPipe());
|
|
||||||
|
|
||||||
const config = new DocumentBuilder()
|
const config = new DocumentBuilder()
|
||||||
.setTitle('Bigcapital')
|
.setTitle('Bigcapital')
|
||||||
.setDescription('Financial accounting software')
|
.setDescription('Financial accounting software')
|
||||||
@@ -39,9 +30,6 @@ async function bootstrap() {
|
|||||||
const documentFactory = () => SwaggerModule.createDocument(app, config);
|
const documentFactory = () => SwaggerModule.createDocument(app, config);
|
||||||
SwaggerModule.setup('swagger', app, documentFactory);
|
SwaggerModule.setup('swagger', app, documentFactory);
|
||||||
|
|
||||||
app.useGlobalFilters(new ServiceErrorFilter());
|
|
||||||
app.useGlobalFilters(new ModelHasRelationsFilter());
|
|
||||||
|
|
||||||
await app.listen(process.env.PORT ?? 3000);
|
await app.listen(process.env.PORT ?? 3000);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
|
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||||
import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
|
import { APP_GUARD, APP_INTERCEPTOR, APP_PIPE, APP_FILTER } from '@nestjs/core';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||||
import { RedisModule } from '@liaoliaots/nestjs-redis';
|
import { RedisModule } from '@liaoliaots/nestjs-redis';
|
||||||
@@ -36,6 +36,10 @@ import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module';
|
|||||||
import { BranchesModule } from '../Branches/Branches.module';
|
import { BranchesModule } from '../Branches/Branches.module';
|
||||||
import { WarehousesModule } from '../Warehouses/Warehouses.module';
|
import { WarehousesModule } from '../Warehouses/Warehouses.module';
|
||||||
import { SerializeInterceptor } from '@/common/interceptors/serialize.interceptor';
|
import { SerializeInterceptor } from '@/common/interceptors/serialize.interceptor';
|
||||||
|
import { ValidationPipe } from '@/common/pipes/ClassValidation.pipe';
|
||||||
|
import { ToJsonInterceptor } from '@/common/interceptors/to-json.interceptor';
|
||||||
|
import { ServiceErrorFilter } from '@/common/filters/service-error.filter';
|
||||||
|
import { ModelHasRelationsFilter } from '@/common/filters/model-has-relations.filter';
|
||||||
import { ChromiumlyTenancyModule } from '../ChromiumlyTenancy/ChromiumlyTenancy.module';
|
import { ChromiumlyTenancyModule } from '../ChromiumlyTenancy/ChromiumlyTenancy.module';
|
||||||
import { CustomersModule } from '../Customers/Customers.module';
|
import { CustomersModule } from '../Customers/Customers.module';
|
||||||
import { VendorsModule } from '../Vendors/Vendors.module';
|
import { VendorsModule } from '../Vendors/Vendors.module';
|
||||||
@@ -234,10 +238,18 @@ import { AppThrottleModule } from './AppThrottle.module';
|
|||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_PIPE,
|
||||||
|
useClass: ValidationPipe,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: APP_GUARD,
|
provide: APP_GUARD,
|
||||||
useClass: ThrottlerGuard,
|
useClass: ThrottlerGuard,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: APP_INTERCEPTOR,
|
||||||
|
useClass: ToJsonInterceptor,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: APP_INTERCEPTOR,
|
provide: APP_INTERCEPTOR,
|
||||||
useClass: SerializeInterceptor,
|
useClass: SerializeInterceptor,
|
||||||
@@ -250,6 +262,14 @@ import { AppThrottleModule } from './AppThrottle.module';
|
|||||||
provide: APP_INTERCEPTOR,
|
provide: APP_INTERCEPTOR,
|
||||||
useClass: ExcludeNullInterceptor,
|
useClass: ExcludeNullInterceptor,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: APP_FILTER,
|
||||||
|
useClass: ServiceErrorFilter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: APP_FILTER,
|
||||||
|
useClass: ModelHasRelationsFilter,
|
||||||
|
},
|
||||||
AppService,
|
AppService,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import {
|
|||||||
validateTransactionShouldBeExcluded,
|
validateTransactionShouldBeExcluded,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import {
|
import {
|
||||||
IBankTransactionExcludedEventPayload,
|
IBankTransactionUnexcludedEventPayload,
|
||||||
IBankTransactionExcludingEventPayload,
|
IBankTransactionUnexcludingEventPayload,
|
||||||
} from '../types/BankTransactionsExclude.types';
|
} from '../types/BankTransactionsExclude.types';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
@@ -24,7 +24,7 @@ export class UnexcludeBankTransactionService {
|
|||||||
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
|
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
|
||||||
typeof UncategorizedBankTransaction
|
typeof UncategorizedBankTransaction
|
||||||
>,
|
>,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given bank transaction as excluded.
|
* Marks the given bank transaction as excluded.
|
||||||
@@ -50,7 +50,8 @@ export class UnexcludeBankTransactionService {
|
|||||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
await this.eventEmitter.emitAsync(events.bankTransactions.onUnexcluding, {
|
await this.eventEmitter.emitAsync(events.bankTransactions.onUnexcluding, {
|
||||||
uncategorizedTransactionId,
|
uncategorizedTransactionId,
|
||||||
} as IBankTransactionExcludingEventPayload);
|
trx,
|
||||||
|
} as IBankTransactionUnexcludingEventPayload);
|
||||||
|
|
||||||
await this.uncategorizedBankTransactionModel()
|
await this.uncategorizedBankTransactionModel()
|
||||||
.query(trx)
|
.query(trx)
|
||||||
@@ -61,7 +62,8 @@ export class UnexcludeBankTransactionService {
|
|||||||
|
|
||||||
await this.eventEmitter.emitAsync(events.bankTransactions.onUnexcluded, {
|
await this.eventEmitter.emitAsync(events.bankTransactions.onUnexcluded, {
|
||||||
uncategorizedTransactionId,
|
uncategorizedTransactionId,
|
||||||
} as IBankTransactionExcludedEventPayload);
|
trx,
|
||||||
|
} as IBankTransactionUnexcludedEventPayload);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class DecrementUncategorizedTransactionOnExclude {
|
|||||||
private readonly uncategorizedBankTransaction: TenantModelProxy<
|
private readonly uncategorizedBankTransaction: TenantModelProxy<
|
||||||
typeof UncategorizedBankTransaction
|
typeof UncategorizedBankTransaction
|
||||||
>,
|
>,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the cashflow transaction whether matched with bank transaction on deleting.
|
* Validates the cashflow transaction whether matched with bank transaction on deleting.
|
||||||
@@ -50,7 +50,7 @@ export class DecrementUncategorizedTransactionOnExclude {
|
|||||||
trx,
|
trx,
|
||||||
}: IBankTransactionUnexcludedEventPayload) {
|
}: IBankTransactionUnexcludedEventPayload) {
|
||||||
const transaction = await this.uncategorizedBankTransaction()
|
const transaction = await this.uncategorizedBankTransaction()
|
||||||
.query()
|
.query(trx)
|
||||||
.findById(uncategorizedTransactionId);
|
.findById(uncategorizedTransactionId);
|
||||||
//
|
//
|
||||||
await this.account()
|
await this.account()
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class BillAllocateLandedCostController {
|
|||||||
private billAllocatedCostTransactions: BillAllocatedLandedCostTransactions,
|
private billAllocatedCostTransactions: BillAllocatedLandedCostTransactions,
|
||||||
private revertAllocatedLandedCost: RevertAllocatedLandedCost,
|
private revertAllocatedLandedCost: RevertAllocatedLandedCost,
|
||||||
private landedCostTransactions: LandedCostTranasctions,
|
private landedCostTransactions: LandedCostTranasctions,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
@Get('/transactions')
|
@Get('/transactions')
|
||||||
@ApiOperation({ summary: 'Get landed cost transactions' })
|
@ApiOperation({ summary: 'Get landed cost transactions' })
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class BillAllocatedLandedCostTransactions {
|
|||||||
private readonly billLandedCostModel: TenantModelProxy<
|
private readonly billLandedCostModel: TenantModelProxy<
|
||||||
typeof BillLandedCost
|
typeof BillLandedCost
|
||||||
>,
|
>,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the bill associated landed cost transactions.
|
* Retrieve the bill associated landed cost transactions.
|
||||||
@@ -77,6 +77,13 @@ export class BillAllocatedLandedCostTransactions {
|
|||||||
transaction.fromTransactionType,
|
transaction.fromTransactionType,
|
||||||
transaction,
|
transaction,
|
||||||
);
|
);
|
||||||
|
const allocationMethodFormattedKey = transaction.allocationMethodFormatted;
|
||||||
|
const allocationMethodFormatted = allocationMethodFormattedKey
|
||||||
|
? this.i18nService.t(allocationMethodFormattedKey, {
|
||||||
|
defaultValue: allocationMethodFormattedKey,
|
||||||
|
})
|
||||||
|
: '';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
formattedAmount: formatNumber(transaction.amount, {
|
formattedAmount: formatNumber(transaction.amount, {
|
||||||
currencyCode: transaction.currencyCode,
|
currencyCode: transaction.currencyCode,
|
||||||
@@ -84,12 +91,14 @@ export class BillAllocatedLandedCostTransactions {
|
|||||||
...omit(transaction, [
|
...omit(transaction, [
|
||||||
'allocatedFromBillEntry',
|
'allocatedFromBillEntry',
|
||||||
'allocatedFromExpenseEntry',
|
'allocatedFromExpenseEntry',
|
||||||
|
'allocationMethodFormatted',
|
||||||
]),
|
]),
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
formattedLocalAmount: formatNumber(transaction.localAmount, {
|
formattedLocalAmount: formatNumber(transaction.localAmount, {
|
||||||
currencyCode: 'USD',
|
currencyCode: 'USD',
|
||||||
}),
|
}),
|
||||||
|
allocationMethodFormatted,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { LandedCostTransactionsQueryDto } from '../dtos/LandedCostTransactionsQu
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LandedCostTranasctions {
|
export class LandedCostTranasctions {
|
||||||
constructor(private readonly transactionLandedCost: TransactionLandedCost) {}
|
constructor(private readonly transactionLandedCost: TransactionLandedCost) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the landed costs based on the given query.
|
* Retrieve the landed costs based on the given query.
|
||||||
@@ -45,8 +45,8 @@ export class LandedCostTranasctions {
|
|||||||
)(transactionType);
|
)(transactionType);
|
||||||
|
|
||||||
return pipe(
|
return pipe(
|
||||||
this.transformLandedCostTransactions,
|
|
||||||
R.map(transformLandedCost),
|
R.map(transformLandedCost),
|
||||||
|
this.transformLandedCostTransactions,
|
||||||
)(transactions);
|
)(transactions);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ export class LandedCostTranasctions {
|
|||||||
const entries = R.map<
|
const entries = R.map<
|
||||||
ILandedCostTransactionEntry,
|
ILandedCostTransactionEntry,
|
||||||
ILandedCostTransactionEntryDOJO
|
ILandedCostTransactionEntryDOJO
|
||||||
>(transformLandedCostEntry)(transaction.entries);
|
>(transformLandedCostEntry)(transaction.entries ?? []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...transaction,
|
...transaction,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
IsOptional,
|
IsOptional,
|
||||||
IsArray,
|
IsArray,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
IsDecimal,
|
|
||||||
IsString,
|
IsString,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
@@ -17,8 +16,9 @@ export class AllocateBillLandedCostItemDto {
|
|||||||
@ToNumber()
|
@ToNumber()
|
||||||
entryId: number;
|
entryId: number;
|
||||||
|
|
||||||
@IsDecimal()
|
@IsNumber()
|
||||||
cost: string; // Use string for IsDecimal, or use @IsNumber() if you want a number
|
@ToNumber()
|
||||||
|
cost: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AllocateBillLandedCostDto {
|
export class AllocateBillLandedCostDto {
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ export class BillLandedCost extends BaseModel {
|
|||||||
const allocationMethod = lowerCase(this.allocationMethod);
|
const allocationMethod = lowerCase(this.allocationMethod);
|
||||||
|
|
||||||
const keyLabelsPairs = {
|
const keyLabelsPairs = {
|
||||||
value: 'allocation_method.value.label',
|
value: 'bill.allocation_method.value',
|
||||||
quantity: 'allocation_method.quantity.label',
|
quantity: 'bill.allocation_method.quantity',
|
||||||
};
|
};
|
||||||
return keyLabelsPairs[allocationMethod] || '';
|
return keyLabelsPairs[allocationMethod] || '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,115 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useReducer, useEffect } from 'react';
|
import React, { useReducer, useEffect } from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { Button, ButtonGroup, Intent, HTMLSelect } from '@blueprintjs/core';
|
import { Button, ButtonGroup, Intent, HTMLSelect } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T } from '@/components';
|
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { range } from 'lodash';
|
import { range } from 'lodash';
|
||||||
import { Icon } from '@/components';
|
import styled from 'styled-components';
|
||||||
|
import { x } from '@xstyled/emotion';
|
||||||
|
import { Icon, FormattedMessage as T } from '@/components';
|
||||||
|
import { useIsDarkMode } from '@/hooks/useDarkMode';
|
||||||
|
|
||||||
import '@/style/components/DataTable/Pagination.scss';
|
// Styled components
|
||||||
|
const StyledButtonGroup = styled(ButtonGroup)`
|
||||||
|
.bp4-button {
|
||||||
|
background: transparent;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledPaginationButton = styled(Button)`
|
||||||
|
--x-button-text-color: #666666;
|
||||||
|
--x-button-hover-background: #E6EFFB;
|
||||||
|
--x-button-active-text-color: #000;
|
||||||
|
--x-button-active-background: #E6EFFB;
|
||||||
|
--x-button-active-disabled-background: #E6EFFB;
|
||||||
|
|
||||||
|
.bp4-dark & {
|
||||||
|
--x-button-text-color: rgba(255, 255, 255, 0.8);
|
||||||
|
--x-button-hover-background: var(--color-dark-gray3);
|
||||||
|
--x-button-active-text-color: var(--color-light-gray2);
|
||||||
|
--x-button-active-background: var(--color-dark-gray3);
|
||||||
|
--x-button-active-disabled-background: var(--color-dark-gray3);
|
||||||
|
}
|
||||||
|
min-width: 24px;
|
||||||
|
min-height: 24px;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
&:not([class*="bp4-intent-"]).bp4-minimal {
|
||||||
|
color: var(--x-button-text-color);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--x-button-hover-background);
|
||||||
|
}
|
||||||
|
.bp4-icon {
|
||||||
|
margin-right: 4px;
|
||||||
|
color: var(--x-button-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.is-active {
|
||||||
|
&.bp4-intent-primary.bp4-minimal:disabled,
|
||||||
|
&.bp4-intent-primary.bp4-minimal.bp4-disabled {
|
||||||
|
background-color: var(--x-button-active-disabled-background);
|
||||||
|
|
||||||
|
.bp4-button-text {
|
||||||
|
color: var(--x-button-active-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledPreviousButton = styled(StyledPaginationButton)`
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
|
||||||
|
.bp4-icon {
|
||||||
|
[dir="rtl"] & {
|
||||||
|
transform: scale(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledNextButton = styled(StyledPaginationButton)`
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
|
||||||
|
.bp4-icon {
|
||||||
|
order: 1;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledHTMLSelect = styled(HTMLSelect)`
|
||||||
|
--x-html-select-text-color: #666;
|
||||||
|
--x-html-select-border-color: #e8e8e8;
|
||||||
|
|
||||||
|
.bp4-dark & {
|
||||||
|
--x-html-select-text-color: rgba(255, 255, 255, 0.8);
|
||||||
|
--x-html-select-border-color: rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
&.bp4-html-select.bp4-minimal {
|
||||||
|
margin-left: 6px;
|
||||||
|
|
||||||
|
select {
|
||||||
|
height: 24px;
|
||||||
|
width: auto;
|
||||||
|
padding: 0;
|
||||||
|
padding-right: 14px;
|
||||||
|
padding-left: 8px;
|
||||||
|
border: 1px solid var(--x-html-select-border-color);
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: var(--x-html-select-text-color);
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
border-left-width: 3px;
|
||||||
|
border-right-width: 3px;
|
||||||
|
border-top-width: 4px;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const TYPE = {
|
const TYPE = {
|
||||||
PAGE_CHANGE: 'PAGE_CHANGE',
|
PAGE_CHANGE: 'PAGE_CHANGE',
|
||||||
@@ -91,6 +192,7 @@ export function Pagination({
|
|||||||
onPageChange,
|
onPageChange,
|
||||||
onPageSizeChange,
|
onPageSizeChange,
|
||||||
}) {
|
}) {
|
||||||
|
const isDark = useIsDarkMode();
|
||||||
const [state, dispatch] = useReducer(
|
const [state, dispatch] = useReducer(
|
||||||
reducer,
|
reducer,
|
||||||
{ currentPage, total, size },
|
{ currentPage, total, size },
|
||||||
@@ -107,10 +209,10 @@ export function Pagination({
|
|||||||
}, [total, size, currentPage]);
|
}, [total, size, currentPage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="pagination">
|
<x.div display="flex" p="20px 14px" fontSize="13px">
|
||||||
<div class="pagination__buttons-group">
|
<x.div>
|
||||||
<ButtonGroup>
|
<StyledButtonGroup>
|
||||||
<Button
|
<StyledPreviousButton
|
||||||
disabled={state.currentPage <= 1}
|
disabled={state.currentPage <= 1}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch({ type: 'PAGE_CHANGE', page: state.currentPage - 1 });
|
dispatch({ type: 'PAGE_CHANGE', page: state.currentPage - 1 });
|
||||||
@@ -121,14 +223,13 @@ export function Pagination({
|
|||||||
onPageChange({ page, pageSize });
|
onPageChange({ page, pageSize });
|
||||||
}}
|
}}
|
||||||
minimal={true}
|
minimal={true}
|
||||||
className={'pagination__item pagination__item--previous'}
|
|
||||||
icon={<Icon icon={'arrow-back-24'} iconSize={12} />}
|
icon={<Icon icon={'arrow-back-24'} iconSize={12} />}
|
||||||
>
|
>
|
||||||
<T id="previous" />
|
<T id="previous" />
|
||||||
</Button>
|
</StyledPreviousButton>
|
||||||
|
|
||||||
{state.pages.map((page) => (
|
{state.pages.map((page) => (
|
||||||
<Button
|
<StyledPaginationButton
|
||||||
key={page}
|
key={page}
|
||||||
intent={state.currentPage === page ? Intent.PRIMARY : Intent.NONE}
|
intent={state.currentPage === page ? Intent.PRIMARY : Intent.NONE}
|
||||||
disabled={state.currentPage === page}
|
disabled={state.currentPage === page}
|
||||||
@@ -139,18 +240,12 @@ export function Pagination({
|
|||||||
onPageChange({ page, pageSize });
|
onPageChange({ page, pageSize });
|
||||||
}}
|
}}
|
||||||
minimal={true}
|
minimal={true}
|
||||||
className={classNames(
|
className={state.currentPage === page ? 'is-active' : ''}
|
||||||
'pagination__item',
|
|
||||||
'pagination__item--page',
|
|
||||||
{
|
|
||||||
'is-active': state.currentPage === page,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{page}
|
{page}
|
||||||
</Button>
|
</StyledPaginationButton>
|
||||||
))}
|
))}
|
||||||
<Button
|
<StyledNextButton
|
||||||
disabled={state.currentPage === state.totalPages}
|
disabled={state.currentPage === state.totalPages}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
@@ -163,18 +258,17 @@ export function Pagination({
|
|||||||
onPageChange({ page, pageSize });
|
onPageChange({ page, pageSize });
|
||||||
}}
|
}}
|
||||||
minimal={true}
|
minimal={true}
|
||||||
className={'pagination__item pagination__item--next'}
|
|
||||||
icon={<Icon icon={'arrow-forward-24'} iconSize={12} />}
|
icon={<Icon icon={'arrow-forward-24'} iconSize={12} />}
|
||||||
>
|
>
|
||||||
<T id="next" />
|
<T id="next" />
|
||||||
</Button>
|
</StyledNextButton>
|
||||||
</ButtonGroup>
|
</StyledButtonGroup>
|
||||||
</div>
|
</x.div>
|
||||||
|
|
||||||
<div class="pagination__controls">
|
<x.div display="flex" alignItems="center" ml="auto">
|
||||||
<div class="pagination__goto-control">
|
<x.div display="none">
|
||||||
Go to
|
Go to
|
||||||
<HTMLSelect
|
<StyledHTMLSelect
|
||||||
minimal={true}
|
minimal={true}
|
||||||
options={range(1, state.totalPages + 1)}
|
options={range(1, state.totalPages + 1)}
|
||||||
value={state.currentPage}
|
value={state.currentPage}
|
||||||
@@ -186,11 +280,11 @@ export function Pagination({
|
|||||||
onPageChange({ page, pageSize });
|
onPageChange({ page, pageSize });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</x.div>
|
||||||
|
|
||||||
<div class="pagination__pagesize-control">
|
<x.div ml="12px" color={isDark ? 'rgba(255, 255, 255, 0.6)' : '#666'}>
|
||||||
<T id={'page_size'} />
|
<T id={'page_size'} />
|
||||||
<HTMLSelect
|
<StyledHTMLSelect
|
||||||
minimal={true}
|
minimal={true}
|
||||||
options={pageSizesOptions}
|
options={pageSizesOptions}
|
||||||
value={size}
|
value={size}
|
||||||
@@ -202,17 +296,17 @@ export function Pagination({
|
|||||||
onPageSizeChange({ pageSize, page: 1 });
|
onPageSizeChange({ pageSize, page: 1 });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</x.div>
|
||||||
</div>
|
</x.div>
|
||||||
|
|
||||||
<div class="pagination__info">
|
<x.div color={isDark ? 'rgba(255, 255, 255, 0.6)' : '#666'} ml="12px" display="flex" alignItems="center">
|
||||||
{intl.get('showing_current_page_to_total', {
|
{intl.get('showing_current_page_to_total', {
|
||||||
currentPage: state.currentPage,
|
currentPage: state.currentPage,
|
||||||
totalPages: state.totalPages,
|
totalPages: state.totalPages,
|
||||||
total: total,
|
total: total,
|
||||||
})}
|
})}
|
||||||
</div>
|
</x.div>
|
||||||
</div>
|
</x.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { getColumnWidth } from '@/utils';
|
import { getColumnWidth } from '@/utils';
|
||||||
import { useExcludedTransactionsBoot } from './ExcludedTransactionsTableBoot';
|
import { useExcludedTransactionsBoot } from './ExcludedTransactionsTableBoot';
|
||||||
|
import { CLASSES } from '@/constants';
|
||||||
|
|
||||||
const getReportColWidth = (data, accessor, headerText) => {
|
const getReportColWidth = (data, accessor, headerText) => {
|
||||||
return getColumnWidth(
|
return getColumnWidth(
|
||||||
@@ -13,7 +14,7 @@ const getReportColWidth = (data, accessor, headerText) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const descriptionAccessor = (transaction) => {
|
const descriptionAccessor = (transaction) => {
|
||||||
return <span style={{ color: '#5F6B7C' }}>{transaction.description}</span>;
|
return <span className={CLASSES.TEXT_MUTED}>{transaction.description}</span>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function AllocateLandedCostDialogProvider({
|
|||||||
dialogName,
|
dialogName,
|
||||||
...props
|
...props
|
||||||
}) {
|
}) {
|
||||||
const [transactionsType, setTransactionsType] = React.useState(null);
|
const [transactionsType, setTransactionsType] = React.useState('Bill');
|
||||||
const [transactionId, setTransactionId] = React.useState(null);
|
const [transactionId, setTransactionId] = React.useState(null);
|
||||||
const [transactionEntryId, setTransactionEntryId] = React.useState(null);
|
const [transactionEntryId, setTransactionEntryId] = React.useState(null);
|
||||||
|
|
||||||
@@ -34,7 +34,8 @@ function AllocateLandedCostDialogProvider({
|
|||||||
});
|
});
|
||||||
// Retrieve the landed cost transactions based on the given transactions type.
|
// Retrieve the landed cost transactions based on the given transactions type.
|
||||||
const {
|
const {
|
||||||
data: { transactions: landedCostTransactions },
|
data: landedCostTransactions,
|
||||||
|
isLoading: isLandedCostTransactionsLoading,
|
||||||
} = useLandedCostTransaction(transactionsType, {
|
} = useLandedCostTransaction(transactionsType, {
|
||||||
enabled: !!transactionsType,
|
enabled: !!transactionsType,
|
||||||
});
|
});
|
||||||
@@ -88,6 +89,7 @@ function AllocateLandedCostDialogProvider({
|
|||||||
costTransactionEntries,
|
costTransactionEntries,
|
||||||
transactionsType,
|
transactionsType,
|
||||||
landedCostTransactions,
|
landedCostTransactions,
|
||||||
|
isLandedCostTransactionsLoading,
|
||||||
setTransactionsType,
|
setTransactionsType,
|
||||||
setTransactionId,
|
setTransactionId,
|
||||||
setTransactionEntryId,
|
setTransactionEntryId,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ function AllocateLandedCostFloatingActions({
|
|||||||
<DialogFooterActions alignment={'left'}>
|
<DialogFooterActions alignment={'left'}>
|
||||||
{costTransactionEntry && (
|
{costTransactionEntry && (
|
||||||
<UnallocatedAmount>
|
<UnallocatedAmount>
|
||||||
<T id={'landed_cost.dialog.label_unallocated_cost_amount'}/>
|
<T id={'landed_cost.dialog.label_unallocated_cost_amount'} />
|
||||||
<strong>{formattedUnallocatedCostAmount}</strong>
|
<strong>{formattedUnallocatedCostAmount}</strong>
|
||||||
</UnallocatedAmount>
|
</UnallocatedAmount>
|
||||||
)}
|
)}
|
||||||
@@ -68,11 +68,16 @@ const AllocateDialogFooter = styled(DialogFooter)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const UnallocatedAmount = styled.div`
|
const UnallocatedAmount = styled.div`
|
||||||
color: #3f5278;
|
--x-color-text: #3f5278;
|
||||||
|
|
||||||
|
.bp4-dark & {
|
||||||
|
--x-color-text: var(--color-light-gray1);
|
||||||
|
}
|
||||||
|
color: var(--x-color-text);
|
||||||
align-self: center;
|
align-self: center;
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
color: #353535;
|
color: var(--x-color-text);
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ import {
|
|||||||
RadioGroup,
|
RadioGroup,
|
||||||
Radio,
|
Radio,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
|
Spinner,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { x } from '@xstyled/emotion';
|
||||||
import { FormattedMessage as T, If, FFormGroup, FSelect, FRadioGroup, FInputGroup } from '@/components';
|
import { FormattedMessage as T, If, FFormGroup, FSelect, FRadioGroup, FInputGroup } from '@/components';
|
||||||
import { handleStringChange } from '@/utils';
|
import { handleStringChange } from '@/utils';
|
||||||
import { FieldRequiredHint } from '@/components';
|
import { FieldRequiredHint } from '@/components';
|
||||||
@@ -28,7 +30,7 @@ import { useAllocateLandedConstDialogContext } from './AllocateLandedCostDialogP
|
|||||||
*/
|
*/
|
||||||
export default function AllocateLandedCostFormFields() {
|
export default function AllocateLandedCostFormFields() {
|
||||||
// Allocated landed cost dialog.
|
// Allocated landed cost dialog.
|
||||||
const { costTransactionEntries, landedCostTransactions } =
|
const { costTransactionEntries, landedCostTransactions, isLandedCostTransactionsLoading } =
|
||||||
useAllocateLandedConstDialogContext();
|
useAllocateLandedConstDialogContext();
|
||||||
|
|
||||||
const { values, setFieldValue, form } = useFormikContext();
|
const { values, setFieldValue, form } = useFormikContext();
|
||||||
@@ -97,21 +99,35 @@ export default function AllocateLandedCostFormFields() {
|
|||||||
inline
|
inline
|
||||||
fill
|
fill
|
||||||
>
|
>
|
||||||
<FSelect
|
<x.div position="relative" w="100%">
|
||||||
name={'transaction_id'}
|
<FSelect
|
||||||
items={landedCostTransactions}
|
name={'transaction_id'}
|
||||||
onItemChange={handleTransactionChange}
|
items={landedCostTransactions || []}
|
||||||
filterable={false}
|
onItemChange={handleTransactionChange}
|
||||||
valueAccessor={'id'}
|
filterable={false}
|
||||||
textAccessor={'name'}
|
valueAccessor={'id'}
|
||||||
labelAccessor={'formatted_unallocated_cost_amount'}
|
textAccessor={'name'}
|
||||||
placeholder={intl.get('landed_cost.dialog.label_select_transaction')}
|
labelAccessor={'formatted_unallocated_cost_amount'}
|
||||||
popoverProps={{ minimal: true }}
|
placeholder={intl.get('landed_cost.dialog.label_select_transaction')}
|
||||||
/>
|
popoverProps={{ minimal: true }}
|
||||||
|
disabled={isLandedCostTransactionsLoading}
|
||||||
|
/>
|
||||||
|
{isLandedCostTransactionsLoading && (
|
||||||
|
<x.div
|
||||||
|
position="absolute"
|
||||||
|
right="35px"
|
||||||
|
top="50%"
|
||||||
|
transform="translateY(-50%)"
|
||||||
|
pointerEvents="none"
|
||||||
|
>
|
||||||
|
<Spinner size={16} />
|
||||||
|
</x.div>
|
||||||
|
)}
|
||||||
|
</x.div>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
{/*------------ Transaction line -----------*/}
|
{/*------------ Transaction line -----------*/}
|
||||||
<If condition={costTransactionEntries.length > 0}>
|
<If condition={costTransactionEntries?.length > 0}>
|
||||||
<FFormGroup
|
<FFormGroup
|
||||||
name={'transaction_entry_id'}
|
name={'transaction_entry_id'}
|
||||||
label={<T id={'transaction_line'} />}
|
label={<T id={'transaction_line'} />}
|
||||||
|
|||||||
@@ -32,13 +32,12 @@ function BadDebtForm({
|
|||||||
// #withCurrentOrganization
|
// #withCurrentOrganization
|
||||||
organization: { base_currency },
|
organization: { base_currency },
|
||||||
}) {
|
}) {
|
||||||
const { invoice, dialogName, createBadDebtMutate, cancelBadDebtMutate } =
|
const { invoice, dialogName, createBadDebtMutate } = useBadDebtContext();
|
||||||
useBadDebtContext();
|
|
||||||
|
|
||||||
// Initial form values
|
// Initial form values
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
...defaultInitialValues,
|
...defaultInitialValues,
|
||||||
amount: invoice.due_amount,
|
amount: invoice?.due_amount || '',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles the form submit.
|
// Handles the form submit.
|
||||||
@@ -67,7 +66,7 @@ function BadDebtForm({
|
|||||||
}
|
}
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
};
|
};
|
||||||
createBadDebtMutate([invoice.id, form]).then(onSuccess).catch(onError);
|
createBadDebtMutate([invoice?.id, form]).then(onSuccess).catch(onError);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
FMoneyInputGroup,
|
FMoneyInputGroup,
|
||||||
FTextArea,
|
FTextArea,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
|
FFormGroup,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
|
|
||||||
import { useAutofocus } from '@/hooks';
|
import { useAutofocus } from '@/hooks';
|
||||||
@@ -53,7 +54,7 @@ function BadDebtFormFields() {
|
|||||||
fill
|
fill
|
||||||
>
|
>
|
||||||
<ControlGroup>
|
<ControlGroup>
|
||||||
<InputPrependText text={invoice.currency_code} />
|
<InputPrependText text={invoice?.currency_code || ''} />
|
||||||
<FMoneyInputGroup
|
<FMoneyInputGroup
|
||||||
name={'amount'}
|
name={'amount'}
|
||||||
minimal={true}
|
minimal={true}
|
||||||
@@ -73,6 +74,7 @@ function BadDebtFormFields() {
|
|||||||
name={'expense_account_id'}
|
name={'expense_account_id'}
|
||||||
items={accounts}
|
items={accounts}
|
||||||
filterByTypes={[ACCOUNT_TYPE.EXPENSE]}
|
filterByTypes={[ACCOUNT_TYPE.EXPENSE]}
|
||||||
|
fill
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ function BadDebtDialog({ dialogName, payload: { invoiceId = null }, isOpen }) {
|
|||||||
name={dialogName}
|
name={dialogName}
|
||||||
title={<T id={'bad_debt.dialog.bad_debt'} />}
|
title={<T id={'bad_debt.dialog.bad_debt'} />}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
canEscapeJeyClose={true}
|
canEscapeKeyClose={true}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
className={'dialog--bad-debt'}
|
className={'dialog--bad-debt'}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -424,7 +424,7 @@ export function useUnexcludeUncategorizedTransactions(
|
|||||||
UnexcludeBankTransactionsValue
|
UnexcludeBankTransactionsValue
|
||||||
>(
|
>(
|
||||||
(value: { ids: Array<number | string> }) =>
|
(value: { ids: Array<number | string> }) =>
|
||||||
apiRequest.delete(`/banking/exclude/bulk`, { ids: value.ids }),
|
apiRequest.delete(`/banking/exclude/bulk`, { data: { ids: value.ids } }),
|
||||||
{
|
{
|
||||||
onSuccess: (res, id) => {
|
onSuccess: (res, id) => {
|
||||||
onValidateExcludeUncategorizedTransaction(queryClient);
|
onValidateExcludeUncategorizedTransaction(queryClient);
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ export function useCancelBadDebt(props) {
|
|||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation(
|
||||||
(id) => apiRequest.post(`sale-invoices/${id}/writeoff/cancel`),
|
(id) => apiRequest.post(`sale-invoices/${id}/cancel-writeoff`),
|
||||||
{
|
{
|
||||||
onSuccess: (res, id) => {
|
onSuccess: (res, id) => {
|
||||||
// Invalidate
|
// Invalidate
|
||||||
|
|||||||
@@ -67,9 +67,6 @@ export function useLandedCostTransaction(query, props) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
select: (res) => res.data,
|
select: (res) => res.data,
|
||||||
defaultData: {
|
|
||||||
transactions: [],
|
|
||||||
},
|
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bp4-dialog-footer{
|
.bp4-dialog-footer{
|
||||||
padding-top: 10px;
|
// padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bigcapital-datatable {
|
.bigcapital-datatable {
|
||||||
@@ -30,6 +30,10 @@
|
|||||||
border: 1px solid #d1dee2;
|
border: 1px solid #d1dee2;
|
||||||
min-width: auto;
|
min-width: auto;
|
||||||
|
|
||||||
|
.bp4-dark & {
|
||||||
|
border-color: var(--color-dark-gray5);
|
||||||
|
}
|
||||||
|
|
||||||
.tbody,
|
.tbody,
|
||||||
.tbody-inner {
|
.tbody-inner {
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -43,6 +47,10 @@
|
|||||||
padding: 0.4rem;
|
padding: 0.4rem;
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
border-left: 1px solid #ececec;
|
border-left: 1px solid #ececec;
|
||||||
|
|
||||||
|
.bp4-dark & {
|
||||||
|
border-left-color: var(--color-dark-gray5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bp4-form-group{
|
.bp4-form-group{
|
||||||
@@ -51,6 +59,10 @@
|
|||||||
&:not(.bp4-intent-danger) .bp4-input{
|
&:not(.bp4-intent-danger) .bp4-input{
|
||||||
border: 1px solid #d0dfe2;
|
border: 1px solid #d0dfe2;
|
||||||
|
|
||||||
|
.bp4-dark & {
|
||||||
|
border-color: var(--color-dark-gray5);
|
||||||
|
}
|
||||||
|
|
||||||
&:focus{
|
&:focus{
|
||||||
box-shadow: 0 0 0 1px #116cd0;
|
box-shadow: 0 0 0 1px #116cd0;
|
||||||
border-color: #116cd0;
|
border-color: #116cd0;
|
||||||
|
|||||||
Reference in New Issue
Block a user