mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43066c4f1f | ||
|
|
d5402b6a9b | ||
|
|
174aec78ca | ||
|
|
7162e948dd | ||
|
|
8ad1be1d52 | ||
|
|
a96ced10db | ||
|
|
2d39e38578 | ||
|
|
af80afcf59 | ||
|
|
66cb0521e5 | ||
|
|
9204b76346 | ||
|
|
36cbb1eef5 | ||
|
|
441e27581b | ||
|
|
e0d9a56a29 | ||
|
|
5a017104ce | ||
|
|
25ca620836 | ||
|
|
5a3655e093 | ||
|
|
49c2777587 | ||
|
|
a5680c08c2 | ||
|
|
d909dad1bf | ||
|
|
f32cc752ef | ||
|
|
a7f98201cc | ||
|
|
a1d0fc3f0a | ||
|
|
11575cfb96 | ||
|
|
a2160c0595 | ||
|
|
956a9b58dd | ||
|
|
acb701d618 | ||
|
|
09ff72d302 | ||
|
|
7375512fec | ||
|
|
77e65389a4 | ||
|
|
1972861c97 | ||
|
|
c47acdee03 | ||
|
|
8689962bf3 | ||
|
|
3258159474 | ||
|
|
36bfa573ad | ||
|
|
2c05785096 | ||
|
|
6af4be9c6c |
@@ -6,4 +6,5 @@ export default registerAs('s3', () => ({
|
|||||||
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
|
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
|
||||||
endpoint: process.env.S3_ENDPOINT,
|
endpoint: process.env.S3_ENDPOINT,
|
||||||
bucket: process.env.S3_BUCKET,
|
bucket: process.env.S3_BUCKET,
|
||||||
|
forcePathStyle: process.env.S3_FORCE_PATH_STYLE === 'true',
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
ParseIntPipe,
|
ParseIntPipe,
|
||||||
Put,
|
Put,
|
||||||
HttpCode,
|
HttpCode,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { AccountsApplication } from './AccountsApplication.service';
|
import { AccountsApplication } from './AccountsApplication.service';
|
||||||
import { CreateAccountDTO } from './CreateAccount.dto';
|
import { CreateAccountDTO } from './CreateAccount.dto';
|
||||||
@@ -32,6 +33,11 @@ import {
|
|||||||
BulkDeleteDto,
|
BulkDeleteDto,
|
||||||
ValidateBulkDeleteResponseDto,
|
ValidateBulkDeleteResponseDto,
|
||||||
} from '@/common/dtos/BulkDelete.dto';
|
} from '@/common/dtos/BulkDelete.dto';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { AccountAction } from './Accounts.types';
|
||||||
|
|
||||||
@Controller('accounts')
|
@Controller('accounts')
|
||||||
@ApiTags('Accounts')
|
@ApiTags('Accounts')
|
||||||
@@ -40,11 +46,13 @@ import {
|
|||||||
@ApiExtraModels(GetAccountTransactionResponseDto)
|
@ApiExtraModels(GetAccountTransactionResponseDto)
|
||||||
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class AccountsController {
|
export class AccountsController {
|
||||||
constructor(private readonly accountsApplication: AccountsApplication) { }
|
constructor(private readonly accountsApplication: AccountsApplication) { }
|
||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
|
@RequirePermission(AccountAction.DELETE, AbilitySubject.Account)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Validates which accounts can be deleted and returns counts of deletable and non-deletable accounts.',
|
'Validates which accounts can be deleted and returns counts of deletable and non-deletable accounts.',
|
||||||
@@ -67,6 +75,7 @@ export class AccountsController {
|
|||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
|
@RequirePermission(AccountAction.DELETE, AbilitySubject.Account)
|
||||||
@ApiOperation({ summary: 'Deletes multiple accounts in bulk.' })
|
@ApiOperation({ summary: 'Deletes multiple accounts in bulk.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -81,6 +90,7 @@ export class AccountsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(AccountAction.CREATE, AbilitySubject.Account)
|
||||||
@ApiOperation({ summary: 'Create an account' })
|
@ApiOperation({ summary: 'Create an account' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -91,6 +101,7 @@ export class AccountsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(AccountAction.EDIT, AbilitySubject.Account)
|
||||||
@ApiOperation({ summary: 'Edit the given account.' })
|
@ApiOperation({ summary: 'Edit the given account.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -111,6 +122,7 @@ export class AccountsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(AccountAction.DELETE, AbilitySubject.Account)
|
||||||
@ApiOperation({ summary: 'Delete the given account.' })
|
@ApiOperation({ summary: 'Delete the given account.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -129,6 +141,7 @@ export class AccountsController {
|
|||||||
|
|
||||||
@Post(':id/activate')
|
@Post(':id/activate')
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
|
@RequirePermission(AccountAction.EDIT, AbilitySubject.Account)
|
||||||
@ApiOperation({ summary: 'Activate the given account.' })
|
@ApiOperation({ summary: 'Activate the given account.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -147,6 +160,7 @@ export class AccountsController {
|
|||||||
|
|
||||||
@Post(':id/inactivate')
|
@Post(':id/inactivate')
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
|
@RequirePermission(AccountAction.EDIT, AbilitySubject.Account)
|
||||||
@ApiOperation({ summary: 'Inactivate the given account.' })
|
@ApiOperation({ summary: 'Inactivate the given account.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -164,6 +178,7 @@ export class AccountsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('types')
|
@Get('types')
|
||||||
|
@RequirePermission(AccountAction.VIEW, AbilitySubject.Account)
|
||||||
@ApiOperation({ summary: 'Retrieves the account types.' })
|
@ApiOperation({ summary: 'Retrieves the account types.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -180,6 +195,7 @@ export class AccountsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('transactions')
|
@Get('transactions')
|
||||||
|
@RequirePermission(AccountAction.VIEW, AbilitySubject.Account)
|
||||||
@ApiOperation({ summary: 'Retrieves the account transactions.' })
|
@ApiOperation({ summary: 'Retrieves the account transactions.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -198,6 +214,7 @@ export class AccountsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(AccountAction.VIEW, AbilitySubject.Account)
|
||||||
@ApiOperation({ summary: 'Retrieves the account details.' })
|
@ApiOperation({ summary: 'Retrieves the account details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -216,6 +233,7 @@ export class AccountsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(AccountAction.VIEW, AbilitySubject.Account)
|
||||||
@ApiOperation({ summary: 'Retrieves the accounts.' })
|
@ApiOperation({ summary: 'Retrieves the accounts.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { ToNumber } from '@/common/decorators/Validators';
|
|||||||
|
|
||||||
class BankRuleConditionDto {
|
class BankRuleConditionDto {
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsIn(['description', 'amount'])
|
@IsIn(['description', 'amount', 'payee'])
|
||||||
field: string;
|
field: string;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
|||||||
@@ -15,8 +15,13 @@ export const RecognizeUncategorizedTransactionsJob =
|
|||||||
export const RecognizeUncategorizedTransactionsQueue =
|
export const RecognizeUncategorizedTransactionsQueue =
|
||||||
'recognize-uncategorized-transactions-queue';
|
'recognize-uncategorized-transactions-queue';
|
||||||
|
|
||||||
|
|
||||||
export interface RecognizeUncategorizedTransactionsJobPayload extends TenantJobPayload {
|
export interface RecognizeUncategorizedTransactionsJobPayload extends TenantJobPayload {
|
||||||
ruleId: number,
|
ruleId: number,
|
||||||
transactionsCriteria: any;
|
transactionsCriteria?: RecognizeTransactionsCriteria;
|
||||||
|
/**
|
||||||
|
* When true, first reverts recognized transactions before recognizing again.
|
||||||
|
* Used when a bank rule is edited to ensure transactions previously recognized
|
||||||
|
* by lower-priority rules are re-evaluated against the updated rule.
|
||||||
|
*/
|
||||||
|
shouldRevert?: boolean;
|
||||||
}
|
}
|
||||||
@@ -93,6 +93,10 @@ export class RecognizeTranasctionsService {
|
|||||||
q.whereIn('id', rulesIds);
|
q.whereIn('id', rulesIds);
|
||||||
}
|
}
|
||||||
q.withGraphFetched('conditions');
|
q.withGraphFetched('conditions');
|
||||||
|
|
||||||
|
// Order by the 'order' field to ensure higher priority rules (lower order values)
|
||||||
|
// are matched first.
|
||||||
|
q.orderBy('order', 'asc');
|
||||||
});
|
});
|
||||||
|
|
||||||
const bankRulesByAccountId = transformToMapBy(
|
const bankRulesByAccountId = transformToMapBy(
|
||||||
|
|||||||
@@ -69,10 +69,13 @@ export class TriggerRecognizedTransactionsSubscriber {
|
|||||||
const tenantPayload = await this.tenancyContect.getTenantJobPayload();
|
const tenantPayload = await this.tenancyContect.getTenantJobPayload();
|
||||||
const payload = {
|
const payload = {
|
||||||
ruleId: bankRule.id,
|
ruleId: bankRule.id,
|
||||||
|
shouldRevert: true,
|
||||||
...tenantPayload,
|
...tenantPayload,
|
||||||
} as RecognizeUncategorizedTransactionsJobPayload;
|
} as RecognizeUncategorizedTransactionsJobPayload;
|
||||||
|
|
||||||
// Re-recognize the transactions based on the new rules.
|
// Re-recognize the transactions based on the new rules.
|
||||||
|
// Setting shouldRevert to true ensures that transactions previously recognized
|
||||||
|
// by this or lower-priority rules are re-evaluated against the updated rule.
|
||||||
await this.recognizeTransactionsQueue.add(
|
await this.recognizeTransactionsQueue.add(
|
||||||
RecognizeUncategorizedTransactionsJob,
|
RecognizeUncategorizedTransactionsJob,
|
||||||
payload,
|
payload,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Processor, WorkerHost } from '@nestjs/bullmq';
|
|||||||
import { Scope } from '@nestjs/common';
|
import { Scope } from '@nestjs/common';
|
||||||
import { ClsService, UseCls } from 'nestjs-cls';
|
import { ClsService, UseCls } from 'nestjs-cls';
|
||||||
import { RecognizeTranasctionsService } from '../commands/RecognizeTranasctions.service';
|
import { RecognizeTranasctionsService } from '../commands/RecognizeTranasctions.service';
|
||||||
|
import { RevertRecognizedTransactionsService } from '../commands/RevertRecognizedTransactions.service';
|
||||||
import {
|
import {
|
||||||
RecognizeUncategorizedTransactionsJobPayload,
|
RecognizeUncategorizedTransactionsJobPayload,
|
||||||
RecognizeUncategorizedTransactionsQueue,
|
RecognizeUncategorizedTransactionsQueue,
|
||||||
@@ -15,10 +16,12 @@ import {
|
|||||||
export class RegonizeTransactionsPrcessor extends WorkerHost {
|
export class RegonizeTransactionsPrcessor extends WorkerHost {
|
||||||
/**
|
/**
|
||||||
* @param {RecognizeTranasctionsService} recognizeTranasctionsService -
|
* @param {RecognizeTranasctionsService} recognizeTranasctionsService -
|
||||||
|
* @param {RevertRecognizedTransactionsService} revertRecognizedTransactionsService -
|
||||||
* @param {ClsService} clsService -
|
* @param {ClsService} clsService -
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private readonly recognizeTranasctionsService: RecognizeTranasctionsService,
|
private readonly recognizeTranasctionsService: RecognizeTranasctionsService,
|
||||||
|
private readonly revertRecognizedTransactionsService: RevertRecognizedTransactionsService,
|
||||||
private readonly clsService: ClsService,
|
private readonly clsService: ClsService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
@@ -29,12 +32,21 @@ export class RegonizeTransactionsPrcessor extends WorkerHost {
|
|||||||
*/
|
*/
|
||||||
@UseCls()
|
@UseCls()
|
||||||
async process(job: Job<RecognizeUncategorizedTransactionsJobPayload>) {
|
async process(job: Job<RecognizeUncategorizedTransactionsJobPayload>) {
|
||||||
const { ruleId, transactionsCriteria } = job.data;
|
const { ruleId, transactionsCriteria, shouldRevert } = job.data;
|
||||||
|
|
||||||
this.clsService.set('organizationId', job.data.organizationId);
|
this.clsService.set('organizationId', job.data.organizationId);
|
||||||
this.clsService.set('userId', job.data.userId);
|
this.clsService.set('userId', job.data.userId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// If shouldRevert is true, first revert recognized transactions before re-recognizing.
|
||||||
|
// This is used when a bank rule is edited to ensure transactions previously recognized
|
||||||
|
// by lower-priority rules are re-evaluated against the updated rule.
|
||||||
|
if (shouldRevert) {
|
||||||
|
await this.revertRecognizedTransactionsService.revertRecognizedTransactions(
|
||||||
|
ruleId,
|
||||||
|
transactionsCriteria,
|
||||||
|
);
|
||||||
|
}
|
||||||
await this.recognizeTranasctionsService.recognizeTransactions(
|
await this.recognizeTranasctionsService.recognizeTransactions(
|
||||||
ruleId,
|
ruleId,
|
||||||
transactionsCriteria,
|
transactionsCriteria,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { BillPaymentsApplication } from './BillPaymentsApplication.service';
|
import { BillPaymentsApplication } from './BillPaymentsApplication.service';
|
||||||
import {
|
import {
|
||||||
@@ -26,12 +27,18 @@ import { BillPaymentsPages } from './commands/BillPaymentsPages.service';
|
|||||||
import { BillPaymentResponseDto } from './dtos/BillPaymentResponse.dto';
|
import { BillPaymentResponseDto } from './dtos/BillPaymentResponse.dto';
|
||||||
import { PaginatedResponseDto } from '@/common/dtos/PaginatedResults.dto';
|
import { PaginatedResponseDto } from '@/common/dtos/PaginatedResults.dto';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { IPaymentMadeAction } from './types/BillPayments.types';
|
||||||
|
|
||||||
@Controller('bill-payments')
|
@Controller('bill-payments')
|
||||||
@ApiTags('Bill Payments')
|
@ApiTags('Bill Payments')
|
||||||
@ApiExtraModels(BillPaymentResponseDto)
|
@ApiExtraModels(BillPaymentResponseDto)
|
||||||
@ApiExtraModels(PaginatedResponseDto)
|
@ApiExtraModels(PaginatedResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class BillPaymentsController {
|
export class BillPaymentsController {
|
||||||
constructor(
|
constructor(
|
||||||
private billPaymentsApplication: BillPaymentsApplication,
|
private billPaymentsApplication: BillPaymentsApplication,
|
||||||
@@ -39,12 +46,14 @@ export class BillPaymentsController {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(IPaymentMadeAction.Create, AbilitySubject.PaymentMade)
|
||||||
@ApiOperation({ summary: 'Create a new bill payment.' })
|
@ApiOperation({ summary: 'Create a new bill payment.' })
|
||||||
public createBillPayment(@Body() billPaymentDTO: CreateBillPaymentDto) {
|
public createBillPayment(@Body() billPaymentDTO: CreateBillPaymentDto) {
|
||||||
return this.billPaymentsApplication.createBillPayment(billPaymentDTO);
|
return this.billPaymentsApplication.createBillPayment(billPaymentDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':billPaymentId')
|
@Delete(':billPaymentId')
|
||||||
|
@RequirePermission(IPaymentMadeAction.Delete, AbilitySubject.PaymentMade)
|
||||||
@ApiOperation({ summary: 'Delete the given bill payment.' })
|
@ApiOperation({ summary: 'Delete the given bill payment.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'billPaymentId',
|
name: 'billPaymentId',
|
||||||
@@ -59,6 +68,7 @@ export class BillPaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':billPaymentId')
|
@Put(':billPaymentId')
|
||||||
|
@RequirePermission(IPaymentMadeAction.Edit, AbilitySubject.PaymentMade)
|
||||||
@ApiOperation({ summary: 'Edit the given bill payment.' })
|
@ApiOperation({ summary: 'Edit the given bill payment.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'billPaymentId',
|
name: 'billPaymentId',
|
||||||
@@ -77,6 +87,7 @@ export class BillPaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('/new-page/entries')
|
@Get('/new-page/entries')
|
||||||
|
@RequirePermission(IPaymentMadeAction.View, AbilitySubject.PaymentMade)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Retrieves the payable entries of the new page once vendor be selected.',
|
'Retrieves the payable entries of the new page once vendor be selected.',
|
||||||
@@ -95,6 +106,7 @@ export class BillPaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':billPaymentId/bills')
|
@Get(':billPaymentId/bills')
|
||||||
|
@RequirePermission(IPaymentMadeAction.View, AbilitySubject.PaymentMade)
|
||||||
@ApiOperation({ summary: 'Retrieves the bills of the given bill payment.' })
|
@ApiOperation({ summary: 'Retrieves the bills of the given bill payment.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'billPaymentId',
|
name: 'billPaymentId',
|
||||||
@@ -107,6 +119,7 @@ export class BillPaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('/:billPaymentId/edit-page')
|
@Get('/:billPaymentId/edit-page')
|
||||||
|
@RequirePermission(IPaymentMadeAction.View, AbilitySubject.PaymentMade)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Retrieves the edit page of the given bill payment.',
|
summary: 'Retrieves the edit page of the given bill payment.',
|
||||||
})
|
})
|
||||||
@@ -126,6 +139,7 @@ export class BillPaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':billPaymentId')
|
@Get(':billPaymentId')
|
||||||
|
@RequirePermission(IPaymentMadeAction.View, AbilitySubject.PaymentMade)
|
||||||
@ApiOperation({ summary: 'Retrieves the bill payment details.' })
|
@ApiOperation({ summary: 'Retrieves the bill payment details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -145,6 +159,7 @@ export class BillPaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(IPaymentMadeAction.View, AbilitySubject.PaymentMade)
|
||||||
@ApiOperation({ summary: 'Retrieves the bill payments list.' })
|
@ApiOperation({ summary: 'Retrieves the bill payments list.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
Get,
|
Get,
|
||||||
Query,
|
Query,
|
||||||
HttpCode,
|
HttpCode,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { BillsApplication } from './Bills.application';
|
import { BillsApplication } from './Bills.application';
|
||||||
import { IBillsFilter } from './Bills.types';
|
import { IBillsFilter } from './Bills.types';
|
||||||
@@ -28,6 +29,11 @@ import {
|
|||||||
BulkDeleteDto,
|
BulkDeleteDto,
|
||||||
ValidateBulkDeleteResponseDto,
|
ValidateBulkDeleteResponseDto,
|
||||||
} from '@/common/dtos/BulkDelete.dto';
|
} from '@/common/dtos/BulkDelete.dto';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { BillAction } from './Bills.types';
|
||||||
|
|
||||||
@Controller('bills')
|
@Controller('bills')
|
||||||
@ApiTags('Bills')
|
@ApiTags('Bills')
|
||||||
@@ -35,10 +41,12 @@ import {
|
|||||||
@ApiExtraModels(PaginatedResponseDto)
|
@ApiExtraModels(PaginatedResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class BillsController {
|
export class BillsController {
|
||||||
constructor(private billsApplication: BillsApplication) { }
|
constructor(private billsApplication: BillsApplication) { }
|
||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
|
@RequirePermission(BillAction.Delete, AbilitySubject.Bill)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Validate which bills can be deleted and return the results.',
|
summary: 'Validate which bills can be deleted and return the results.',
|
||||||
})
|
})
|
||||||
@@ -58,6 +66,7 @@ export class BillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
|
@RequirePermission(BillAction.Delete, AbilitySubject.Bill)
|
||||||
@ApiOperation({ summary: 'Deletes multiple bills.' })
|
@ApiOperation({ summary: 'Deletes multiple bills.' })
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
@@ -73,12 +82,14 @@ export class BillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(BillAction.Create, AbilitySubject.Bill)
|
||||||
@ApiOperation({ summary: 'Create a new bill.' })
|
@ApiOperation({ summary: 'Create a new bill.' })
|
||||||
createBill(@Body() billDTO: CreateBillDto) {
|
createBill(@Body() billDTO: CreateBillDto) {
|
||||||
return this.billsApplication.createBill(billDTO);
|
return this.billsApplication.createBill(billDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(BillAction.Edit, AbilitySubject.Bill)
|
||||||
@ApiOperation({ summary: 'Edit the given bill.' })
|
@ApiOperation({ summary: 'Edit the given bill.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'id',
|
name: 'id',
|
||||||
@@ -91,6 +102,7 @@ export class BillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(BillAction.Delete, AbilitySubject.Bill)
|
||||||
@ApiOperation({ summary: 'Delete the given bill.' })
|
@ApiOperation({ summary: 'Delete the given bill.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'id',
|
name: 'id',
|
||||||
@@ -103,6 +115,7 @@ export class BillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(BillAction.View, AbilitySubject.Bill)
|
||||||
@ApiOperation({ summary: 'Retrieves the bills.' })
|
@ApiOperation({ summary: 'Retrieves the bills.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -132,6 +145,7 @@ export class BillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/payment-transactions')
|
@Get(':id/payment-transactions')
|
||||||
|
@RequirePermission(BillAction.View, AbilitySubject.Bill)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Retrieve the specific bill associated payment transactions.',
|
summary: 'Retrieve the specific bill associated payment transactions.',
|
||||||
})
|
})
|
||||||
@@ -146,6 +160,7 @@ export class BillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(BillAction.View, AbilitySubject.Bill)
|
||||||
@ApiOperation({ summary: 'Retrieves the bill details.' })
|
@ApiOperation({ summary: 'Retrieves the bill details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -165,6 +180,7 @@ export class BillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id/open')
|
@Patch(':id/open')
|
||||||
|
@RequirePermission(BillAction.Edit, AbilitySubject.Bill)
|
||||||
@ApiOperation({ summary: 'Open the given bill.' })
|
@ApiOperation({ summary: 'Open the given bill.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'id',
|
name: 'id',
|
||||||
@@ -177,6 +193,7 @@ export class BillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('due')
|
@Get('due')
|
||||||
|
@RequirePermission(BillAction.View, AbilitySubject.Bill)
|
||||||
@ApiOperation({ summary: 'Retrieves the due bills.' })
|
@ApiOperation({ summary: 'Retrieves the due bills.' })
|
||||||
getDueBills(@Body('vendorId') vendorId?: number) {
|
getDueBills(@Body('vendorId') vendorId?: number) {
|
||||||
return this.billsApplication.getDueBills(vendorId);
|
return this.billsApplication.getDueBills(vendorId);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
|
||||||
import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto';
|
import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto';
|
||||||
|
import { BranchResponseDto } from '@/modules/Branches/dtos/BranchResponse.dto';
|
||||||
import { DiscountType } from '@/common/types/Discount';
|
import { DiscountType } from '@/common/types/Discount';
|
||||||
|
|
||||||
export class BillResponseDto {
|
export class BillResponseDto {
|
||||||
@@ -89,6 +91,14 @@ export class BillResponseDto {
|
|||||||
})
|
})
|
||||||
branchId?: number;
|
branchId?: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Branch details',
|
||||||
|
type: () => BranchResponseDto,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@Type(() => BranchResponseDto)
|
||||||
|
branch?: BranchResponseDto;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The ID of the project',
|
description: 'The ID of the project',
|
||||||
example: 301,
|
example: 301,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export class BillTransformer extends Transformer {
|
|||||||
'taxes',
|
'taxes',
|
||||||
'entries',
|
'entries',
|
||||||
'attachments',
|
'attachments',
|
||||||
|
'branch',
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ import { ValidateBranchExistance } from './integrations/ValidateBranchExistance'
|
|||||||
import { ManualJournalBranchesValidator } from './integrations/ManualJournals/ManualJournalsBranchesValidator';
|
import { ManualJournalBranchesValidator } from './integrations/ManualJournals/ManualJournalsBranchesValidator';
|
||||||
import { CashflowTransactionsActivateBranches } from './integrations/Cashflow/CashflowActivateBranches';
|
import { CashflowTransactionsActivateBranches } from './integrations/Cashflow/CashflowActivateBranches';
|
||||||
import { ExpensesActivateBranches } from './integrations/Expense/ExpensesActivateBranches';
|
import { ExpensesActivateBranches } from './integrations/Expense/ExpensesActivateBranches';
|
||||||
|
import { BillActivateBranches } from './integrations/Purchases/BillBranchesActivate';
|
||||||
|
import { VendorCreditActivateBranches } from './integrations/Purchases/VendorCreditBranchesActivate';
|
||||||
|
import { BillPaymentsActivateBranches } from './integrations/Purchases/PaymentMadeBranchesActivate';
|
||||||
|
import { BillBranchesActivateSubscriber } from './subscribers/Activate/BillBranchesActivateSubscriber';
|
||||||
|
import { VendorCreditBranchesActivateSubscriber } from './subscribers/Activate/VendorCreditBranchesActivateSubscriber';
|
||||||
|
import { PaymentMadeActivateBranchesSubscriber } from './subscribers/Activate/PaymentMadeBranchesActivateSubscriber';
|
||||||
import { FeaturesModule } from '../Features/Features.module';
|
import { FeaturesModule } from '../Features/Features.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@@ -66,7 +72,13 @@ import { FeaturesModule } from '../Features/Features.module';
|
|||||||
ValidateBranchExistance,
|
ValidateBranchExistance,
|
||||||
ManualJournalBranchesValidator,
|
ManualJournalBranchesValidator,
|
||||||
CashflowTransactionsActivateBranches,
|
CashflowTransactionsActivateBranches,
|
||||||
ExpensesActivateBranches
|
ExpensesActivateBranches,
|
||||||
|
BillActivateBranches,
|
||||||
|
VendorCreditActivateBranches,
|
||||||
|
BillPaymentsActivateBranches,
|
||||||
|
BillBranchesActivateSubscriber,
|
||||||
|
VendorCreditBranchesActivateSubscriber,
|
||||||
|
PaymentMadeActivateBranchesSubscriber
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
BranchesSettingsService,
|
BranchesSettingsService,
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
import { Bill } from '@/modules/Bills/models/Bill';
|
import { Bill } from '@/modules/Bills/models/Bill';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BillActivateBranches {
|
export class BillActivateBranches {
|
||||||
constructor(private readonly billModel: TenantModelProxy<typeof Bill>) {}
|
constructor(
|
||||||
|
@Inject(Bill.name)
|
||||||
|
private readonly billModel: TenantModelProxy<typeof Bill>,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates all bills transactions with the primary branch.
|
* Updates all bills transactions with the primary branch.
|
||||||
@@ -17,7 +20,7 @@ export class BillActivateBranches {
|
|||||||
primaryBranchId: number,
|
primaryBranchId: number,
|
||||||
trx?: Knex.Transaction,
|
trx?: Knex.Transaction,
|
||||||
) => {
|
) => {
|
||||||
// Updates the sale invoice with primary branch.
|
// Updates the bills with primary branch.
|
||||||
await Bill.query(trx).update({ branchId: primaryBranchId });
|
await this.billModel().query(trx).update({ branchId: primaryBranchId });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
import { BillPayment } from '@/modules/BillPayments/models/BillPayment';
|
import { BillPayment } from '@/modules/BillPayments/models/BillPayment';
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BillPaymentsActivateBranches {
|
export class BillPaymentsActivateBranches {
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(BillPayment.name)
|
||||||
private readonly billPaymentModel: TenantModelProxy<typeof BillPayment>,
|
private readonly billPaymentModel: TenantModelProxy<typeof BillPayment>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
|
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VendorCreditActivateBranches {
|
export class VendorCreditActivateBranches {
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
private readonly vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
|
private readonly vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { IBranchesActivatedPayload } from '../../Branches.types';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { BillActivateBranches } from '../../integrations/Purchases/BillBranchesActivate';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BillBranchesActivateSubscriber {
|
||||||
|
constructor(
|
||||||
|
private readonly billActivateBranches: BillActivateBranches,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates bills transactions with the primary branch once
|
||||||
|
* the multi-branches is activated.
|
||||||
|
* @param {IBranchesActivatedPayload}
|
||||||
|
*/
|
||||||
|
@OnEvent(events.branch.onActivated)
|
||||||
|
async updateBillsWithBranchOnActivated({
|
||||||
|
primaryBranch,
|
||||||
|
trx,
|
||||||
|
}: IBranchesActivatedPayload) {
|
||||||
|
await this.billActivateBranches.updateBillsWithBranch(
|
||||||
|
primaryBranch.id,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { IBranchesActivatedPayload } from '../../Branches.types';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { VendorCreditActivateBranches } from '../../integrations/Purchases/VendorCreditBranchesActivate';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class VendorCreditBranchesActivateSubscriber {
|
||||||
|
constructor(
|
||||||
|
private readonly vendorCreditActivateBranches: VendorCreditActivateBranches,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates vendor credits transactions with the primary branch once
|
||||||
|
* the multi-branches is activated.
|
||||||
|
* @param {IBranchesActivatedPayload}
|
||||||
|
*/
|
||||||
|
@OnEvent(events.branch.onActivated)
|
||||||
|
async updateVendorCreditsWithBranchOnActivated({
|
||||||
|
primaryBranch,
|
||||||
|
trx,
|
||||||
|
}: IBranchesActivatedPayload) {
|
||||||
|
await this.vendorCreditActivateBranches.updateVendorCreditsWithBranch(
|
||||||
|
primaryBranch.id,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,35 @@
|
|||||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { ICreditNoteRefundDTO } from '../CreditNotes/types/CreditNotes.types';
|
import { ICreditNoteRefundDTO } from '../CreditNotes/types/CreditNotes.types';
|
||||||
import { CreditNotesRefundsApplication } from './CreditNotesRefundsApplication.service';
|
import { CreditNotesRefundsApplication } from './CreditNotesRefundsApplication.service';
|
||||||
import { RefundCreditNote } from './models/RefundCreditNote';
|
import { RefundCreditNote } from './models/RefundCreditNote';
|
||||||
import { CreditNoteRefundDto } from './dto/CreditNoteRefund.dto';
|
import { CreditNoteRefundDto } from './dto/CreditNoteRefund.dto';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { CreditNoteAction } from '../CreditNotes/types/CreditNotes.types';
|
||||||
|
|
||||||
@Controller('credit-notes')
|
@Controller('credit-notes')
|
||||||
@ApiTags('Credit Note Refunds')
|
@ApiTags('Credit Note Refunds')
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class CreditNoteRefundsController {
|
export class CreditNoteRefundsController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly creditNotesRefundsApplication: CreditNotesRefundsApplication,
|
private readonly creditNotesRefundsApplication: CreditNotesRefundsApplication,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get(':creditNoteId/refunds')
|
@Get(':creditNoteId/refunds')
|
||||||
|
@RequirePermission(CreditNoteAction.View, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Retrieve the credit note graph.' })
|
@ApiOperation({ summary: 'Retrieve the credit note graph.' })
|
||||||
getCreditNoteRefunds(@Param('creditNoteId') creditNoteId: number) {
|
getCreditNoteRefunds(@Param('creditNoteId') creditNoteId: number) {
|
||||||
return this.creditNotesRefundsApplication.getCreditNoteRefunds(
|
return this.creditNotesRefundsApplication.getCreditNoteRefunds(
|
||||||
@@ -29,6 +44,7 @@ export class CreditNoteRefundsController {
|
|||||||
* @returns {Promise<RefundCreditNote>}
|
* @returns {Promise<RefundCreditNote>}
|
||||||
*/
|
*/
|
||||||
@Post(':creditNoteId/refunds')
|
@Post(':creditNoteId/refunds')
|
||||||
|
@RequirePermission(CreditNoteAction.Refund, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Create a refund for the given credit note.' })
|
@ApiOperation({ summary: 'Create a refund for the given credit note.' })
|
||||||
createRefundCreditNote(
|
createRefundCreditNote(
|
||||||
@Param('creditNoteId') creditNoteId: number,
|
@Param('creditNoteId') creditNoteId: number,
|
||||||
@@ -46,6 +62,7 @@ export class CreditNoteRefundsController {
|
|||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
@Delete('refunds/:refundCreditId')
|
@Delete('refunds/:refundCreditId')
|
||||||
|
@RequirePermission(CreditNoteAction.Refund, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Delete a refund for the given credit note.' })
|
@ApiOperation({ summary: 'Delete a refund for the given credit note.' })
|
||||||
deleteRefundCreditNote(
|
deleteRefundCreditNote(
|
||||||
@Param('refundCreditId') refundCreditId: number,
|
@Param('refundCreditId') refundCreditId: number,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
Res,
|
Res,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { CreditNoteApplication } from './CreditNoteApplication.service';
|
import { CreditNoteApplication } from './CreditNoteApplication.service';
|
||||||
import { ICreditNotesQueryDTO } from './types/CreditNotes.types';
|
import { ICreditNotesQueryDTO } from './types/CreditNotes.types';
|
||||||
@@ -30,6 +31,11 @@ import {
|
|||||||
ValidateBulkDeleteResponseDto,
|
ValidateBulkDeleteResponseDto,
|
||||||
} from '@/common/dtos/BulkDelete.dto';
|
} from '@/common/dtos/BulkDelete.dto';
|
||||||
import { AcceptType } from '@/constants/accept-type';
|
import { AcceptType } from '@/constants/accept-type';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { CreditNoteAction } from './types/CreditNotes.types';
|
||||||
|
|
||||||
@Controller('credit-notes')
|
@Controller('credit-notes')
|
||||||
@ApiTags('Credit Notes')
|
@ApiTags('Credit Notes')
|
||||||
@@ -37,6 +43,7 @@ import { AcceptType } from '@/constants/accept-type';
|
|||||||
@ApiExtraModels(PaginatedResponseDto)
|
@ApiExtraModels(PaginatedResponseDto)
|
||||||
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class CreditNotesController {
|
export class CreditNotesController {
|
||||||
/**
|
/**
|
||||||
* @param {CreditNoteApplication} creditNoteApplication - The credit note application service.
|
* @param {CreditNoteApplication} creditNoteApplication - The credit note application service.
|
||||||
@@ -44,6 +51,7 @@ export class CreditNotesController {
|
|||||||
constructor(private creditNoteApplication: CreditNoteApplication) { }
|
constructor(private creditNoteApplication: CreditNoteApplication) { }
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(CreditNoteAction.Create, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Create a new credit note' })
|
@ApiOperation({ summary: 'Create a new credit note' })
|
||||||
@ApiResponse({ status: 201, description: 'Credit note successfully created' })
|
@ApiResponse({ status: 201, description: 'Credit note successfully created' })
|
||||||
@ApiResponse({ status: 400, description: 'Invalid input data' })
|
@ApiResponse({ status: 400, description: 'Invalid input data' })
|
||||||
@@ -52,6 +60,7 @@ export class CreditNotesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('state')
|
@Get('state')
|
||||||
|
@RequirePermission(CreditNoteAction.View, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Get credit note state' })
|
@ApiOperation({ summary: 'Get credit note state' })
|
||||||
@ApiResponse({ status: 200, description: 'Returns the credit note state' })
|
@ApiResponse({ status: 200, description: 'Returns the credit note state' })
|
||||||
getCreditNoteState() {
|
getCreditNoteState() {
|
||||||
@@ -59,6 +68,7 @@ export class CreditNotesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(CreditNoteAction.View, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Get a specific credit note by ID' })
|
@ApiOperation({ summary: 'Get a specific credit note by ID' })
|
||||||
@ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' })
|
@ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
@@ -92,6 +102,7 @@ export class CreditNotesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(CreditNoteAction.View, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Get all credit notes' })
|
@ApiOperation({ summary: 'Get all credit notes' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -115,6 +126,7 @@ export class CreditNotesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(CreditNoteAction.Edit, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Update a credit note' })
|
@ApiOperation({ summary: 'Update a credit note' })
|
||||||
@ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' })
|
@ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' })
|
||||||
@ApiResponse({ status: 200, description: 'Credit note successfully updated' })
|
@ApiResponse({ status: 200, description: 'Credit note successfully updated' })
|
||||||
@@ -131,6 +143,7 @@ export class CreditNotesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/open')
|
@Put(':id/open')
|
||||||
|
@RequirePermission(CreditNoteAction.Edit, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Open a credit note' })
|
@ApiOperation({ summary: 'Open a credit note' })
|
||||||
@ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' })
|
@ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' })
|
||||||
@ApiResponse({ status: 200, description: 'Credit note successfully opened' })
|
@ApiResponse({ status: 200, description: 'Credit note successfully opened' })
|
||||||
@@ -140,6 +153,7 @@ export class CreditNotesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
|
@RequirePermission(CreditNoteAction.Delete, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Validates which credit notes can be deleted and returns the results.',
|
'Validates which credit notes can be deleted and returns the results.',
|
||||||
@@ -161,6 +175,7 @@ export class CreditNotesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
|
@RequirePermission(CreditNoteAction.Delete, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Deletes multiple credit notes.' })
|
@ApiOperation({ summary: 'Deletes multiple credit notes.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -173,6 +188,7 @@ export class CreditNotesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(CreditNoteAction.Delete, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Delete a credit note' })
|
@ApiOperation({ summary: 'Delete a credit note' })
|
||||||
@ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' })
|
@ApiParam({ name: 'id', description: 'Credit note ID', type: 'number' })
|
||||||
@ApiResponse({ status: 200, description: 'Credit note successfully deleted' })
|
@ApiResponse({ status: 200, description: 'Credit note successfully deleted' })
|
||||||
|
|||||||
@@ -1,15 +1,31 @@
|
|||||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { GetCreditNoteAssociatedAppliedInvoices } from './queries/GetCreditNoteAssociatedAppliedInvoices.service';
|
import { GetCreditNoteAssociatedAppliedInvoices } from './queries/GetCreditNoteAssociatedAppliedInvoices.service';
|
||||||
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { CreditNoteAction } from '../CreditNotes/types/CreditNotes.types';
|
||||||
|
|
||||||
@Controller('credit-notes')
|
@Controller('credit-notes')
|
||||||
@ApiTags('Credit Notes Apply Invoice')
|
@ApiTags('Credit Notes Apply Invoice')
|
||||||
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class CreditNotesApplyInvoiceController {
|
export class CreditNotesApplyInvoiceController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly getCreditNoteAssociatedAppliedInvoicesService: GetCreditNoteAssociatedAppliedInvoices,
|
private readonly getCreditNoteAssociatedAppliedInvoicesService: GetCreditNoteAssociatedAppliedInvoices,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get(':creditNoteId/applied-invoices')
|
@Get(':creditNoteId/applied-invoices')
|
||||||
|
@RequirePermission(CreditNoteAction.View, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Applied credit note to invoices' })
|
@ApiOperation({ summary: 'Applied credit note to invoices' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -24,6 +40,7 @@ export class CreditNotesApplyInvoiceController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post(':creditNoteId/apply-invoices')
|
@Post(':creditNoteId/apply-invoices')
|
||||||
|
@RequirePermission(CreditNoteAction.Edit, AbilitySubject.CreditNote)
|
||||||
@ApiOperation({ summary: 'Apply credit note to invoices' })
|
@ApiOperation({ summary: 'Apply credit note to invoices' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { CustomersApplication } from './CustomersApplication.service';
|
import { CustomersApplication } from './CustomersApplication.service';
|
||||||
import { CustomerOpeningBalanceEditDto } from './dtos/CustomerOpeningBalanceEdit.dto';
|
import { CustomerOpeningBalanceEditDto } from './dtos/CustomerOpeningBalanceEdit.dto';
|
||||||
@@ -26,15 +27,22 @@ import {
|
|||||||
ValidateBulkDeleteCustomersResponseDto,
|
ValidateBulkDeleteCustomersResponseDto,
|
||||||
} from './dtos/BulkDeleteCustomers.dto';
|
} from './dtos/BulkDeleteCustomers.dto';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { CustomerAction } from './types/Customers.types';
|
||||||
|
|
||||||
@Controller('customers')
|
@Controller('customers')
|
||||||
@ApiTags('Customers')
|
@ApiTags('Customers')
|
||||||
@ApiExtraModels(CustomerResponseDto)
|
@ApiExtraModels(CustomerResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class CustomersController {
|
export class CustomersController {
|
||||||
constructor(private customersApplication: CustomersApplication) { }
|
constructor(private customersApplication: CustomersApplication) { }
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(CustomerAction.View, AbilitySubject.Customer)
|
||||||
@ApiOperation({ summary: 'Retrieves the customer details.' })
|
@ApiOperation({ summary: 'Retrieves the customer details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -46,6 +54,7 @@ export class CustomersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(CustomerAction.View, AbilitySubject.Customer)
|
||||||
@ApiOperation({ summary: 'Retrieves the customers paginated list.' })
|
@ApiOperation({ summary: 'Retrieves the customers paginated list.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -60,6 +69,7 @@ export class CustomersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(CustomerAction.Create, AbilitySubject.Customer)
|
||||||
@ApiOperation({ summary: 'Create a new customer.' })
|
@ApiOperation({ summary: 'Create a new customer.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 201,
|
status: 201,
|
||||||
@@ -71,6 +81,7 @@ export class CustomersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(CustomerAction.Edit, AbilitySubject.Customer)
|
||||||
@ApiOperation({ summary: 'Edit the given customer.' })
|
@ApiOperation({ summary: 'Edit the given customer.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -85,6 +96,7 @@ export class CustomersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(CustomerAction.Delete, AbilitySubject.Customer)
|
||||||
@ApiOperation({ summary: 'Delete the given customer.' })
|
@ApiOperation({ summary: 'Delete the given customer.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -95,6 +107,7 @@ export class CustomersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/opening-balance')
|
@Put(':id/opening-balance')
|
||||||
|
@RequirePermission(CustomerAction.Edit, AbilitySubject.Customer)
|
||||||
@ApiOperation({ summary: 'Edit the opening balance of the given customer.' })
|
@ApiOperation({ summary: 'Edit the opening balance of the given customer.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -112,6 +125,7 @@ export class CustomersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
|
@RequirePermission(CustomerAction.Delete, AbilitySubject.Customer)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Validates which customers can be deleted and returns counts of deletable and non-deletable customers.',
|
'Validates which customers can be deleted and returns counts of deletable and non-deletable customers.',
|
||||||
@@ -131,6 +145,7 @@ export class CustomersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
|
@RequirePermission(CustomerAction.Delete, AbilitySubject.Customer)
|
||||||
@ApiOperation({ summary: 'Deletes multiple customers in bulk.' })
|
@ApiOperation({ summary: 'Deletes multiple customers in bulk.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ExpensesApplication } from './ExpensesApplication.service';
|
import { ExpensesApplication } from './ExpensesApplication.service';
|
||||||
import { IExpensesFilter } from './Expenses.types';
|
import { IExpensesFilter } from './Expenses.types';
|
||||||
@@ -25,6 +26,11 @@ import {
|
|||||||
BulkDeleteDto,
|
BulkDeleteDto,
|
||||||
ValidateBulkDeleteResponseDto,
|
ValidateBulkDeleteResponseDto,
|
||||||
} from '@/common/dtos/BulkDelete.dto';
|
} from '@/common/dtos/BulkDelete.dto';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { ExpenseAction } from './Expenses.types';
|
||||||
|
|
||||||
@Controller('expenses')
|
@Controller('expenses')
|
||||||
@ApiTags('Expenses')
|
@ApiTags('Expenses')
|
||||||
@@ -34,10 +40,12 @@ import {
|
|||||||
ValidateBulkDeleteResponseDto,
|
ValidateBulkDeleteResponseDto,
|
||||||
)
|
)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class ExpensesController {
|
export class ExpensesController {
|
||||||
constructor(private readonly expensesApplication: ExpensesApplication) { }
|
constructor(private readonly expensesApplication: ExpensesApplication) { }
|
||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
|
@RequirePermission(ExpenseAction.Delete, AbilitySubject.Expense)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Validate which expenses can be deleted and return the results.',
|
summary: 'Validate which expenses can be deleted and return the results.',
|
||||||
})
|
})
|
||||||
@@ -58,6 +66,7 @@ export class ExpensesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
|
@RequirePermission(ExpenseAction.Delete, AbilitySubject.Expense)
|
||||||
@ApiOperation({ summary: 'Deletes multiple expenses.' })
|
@ApiOperation({ summary: 'Deletes multiple expenses.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -76,6 +85,7 @@ export class ExpensesController {
|
|||||||
* @param {IExpenseCreateDTO} expenseDTO
|
* @param {IExpenseCreateDTO} expenseDTO
|
||||||
*/
|
*/
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(ExpenseAction.Create, AbilitySubject.Expense)
|
||||||
@ApiOperation({ summary: 'Create a new expense transaction.' })
|
@ApiOperation({ summary: 'Create a new expense transaction.' })
|
||||||
public createExpense(@Body() expenseDTO: CreateExpenseDto) {
|
public createExpense(@Body() expenseDTO: CreateExpenseDto) {
|
||||||
return this.expensesApplication.createExpense(expenseDTO);
|
return this.expensesApplication.createExpense(expenseDTO);
|
||||||
@@ -87,6 +97,7 @@ export class ExpensesController {
|
|||||||
* @param {IExpenseEditDTO} expenseDTO
|
* @param {IExpenseEditDTO} expenseDTO
|
||||||
*/
|
*/
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(ExpenseAction.Edit, AbilitySubject.Expense)
|
||||||
@ApiOperation({ summary: 'Edit the given expense transaction.' })
|
@ApiOperation({ summary: 'Edit the given expense transaction.' })
|
||||||
public editExpense(
|
public editExpense(
|
||||||
@Param('id') expenseId: number,
|
@Param('id') expenseId: number,
|
||||||
@@ -100,6 +111,7 @@ export class ExpensesController {
|
|||||||
* @param {number} expenseId
|
* @param {number} expenseId
|
||||||
*/
|
*/
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(ExpenseAction.Delete, AbilitySubject.Expense)
|
||||||
@ApiOperation({ summary: 'Delete the given expense transaction.' })
|
@ApiOperation({ summary: 'Delete the given expense transaction.' })
|
||||||
public deleteExpense(@Param('id') expenseId: number) {
|
public deleteExpense(@Param('id') expenseId: number) {
|
||||||
return this.expensesApplication.deleteExpense(expenseId);
|
return this.expensesApplication.deleteExpense(expenseId);
|
||||||
@@ -110,6 +122,7 @@ export class ExpensesController {
|
|||||||
* @param {number} expenseId
|
* @param {number} expenseId
|
||||||
*/
|
*/
|
||||||
@Post(':id/publish')
|
@Post(':id/publish')
|
||||||
|
@RequirePermission(ExpenseAction.Edit, AbilitySubject.Expense)
|
||||||
@ApiOperation({ summary: 'Publish the given expense transaction.' })
|
@ApiOperation({ summary: 'Publish the given expense transaction.' })
|
||||||
public publishExpense(@Param('id') expenseId: number) {
|
public publishExpense(@Param('id') expenseId: number) {
|
||||||
return this.expensesApplication.publishExpense(expenseId);
|
return this.expensesApplication.publishExpense(expenseId);
|
||||||
@@ -119,6 +132,7 @@ export class ExpensesController {
|
|||||||
* Get the expense transaction details.
|
* Get the expense transaction details.
|
||||||
*/
|
*/
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(ExpenseAction.View, AbilitySubject.Expense)
|
||||||
@ApiOperation({ summary: 'Get the expense transactions.' })
|
@ApiOperation({ summary: 'Get the expense transactions.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -146,6 +160,7 @@ export class ExpensesController {
|
|||||||
* @param {number} expenseId
|
* @param {number} expenseId
|
||||||
*/
|
*/
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(ExpenseAction.View, AbilitySubject.Expense)
|
||||||
@ApiOperation({ summary: 'Get the expense transaction details.' })
|
@ApiOperation({ summary: 'Get the expense transaction details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { Controller, Get, Headers, Query, Res } from '@nestjs/common';
|
import { Controller, Get, Headers, Query, Res, UseGuards } from '@nestjs/common';
|
||||||
import { APAgingSummaryApplication } from './APAgingSummaryApplication';
|
import { APAgingSummaryApplication } from './APAgingSummaryApplication';
|
||||||
import { AcceptType } from '@/constants/accept-type';
|
import { AcceptType } from '@/constants/accept-type';
|
||||||
import {
|
import {
|
||||||
@@ -11,14 +11,21 @@ import {
|
|||||||
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
import { APAgingSummaryQueryDto } from './APAgingSummaryQuery.dto';
|
||||||
import { APAgingSummaryResponseExample } from './APAgingSummary.swagger';
|
import { APAgingSummaryResponseExample } from './APAgingSummary.swagger';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { ReportsAction } from '../../types/Report.types';
|
||||||
|
|
||||||
@Controller('reports/payable-aging-summary')
|
@Controller('reports/payable-aging-summary')
|
||||||
@ApiTags('Reports')
|
@ApiTags('Reports')
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class APAgingSummaryController {
|
export class APAgingSummaryController {
|
||||||
constructor(private readonly APAgingSummaryApp: APAgingSummaryApplication) { }
|
constructor(private readonly APAgingSummaryApp: APAgingSummaryApplication) { }
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(ReportsAction.READ_AP_AGING_SUMMARY, AbilitySubject.Report)
|
||||||
@ApiOperation({ summary: 'Get payable aging summary' })
|
@ApiOperation({ summary: 'Get payable aging summary' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Controller, Get, Headers } from '@nestjs/common';
|
import { Controller, Get, Headers, Query, Res, UseGuards } from '@nestjs/common';
|
||||||
import { Query, Res } from '@nestjs/common';
|
|
||||||
import { ARAgingSummaryApplication } from './ARAgingSummaryApplication';
|
import { ARAgingSummaryApplication } from './ARAgingSummaryApplication';
|
||||||
import { AcceptType } from '@/constants/accept-type';
|
import { AcceptType } from '@/constants/accept-type';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
@@ -12,14 +11,21 @@ import {
|
|||||||
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
import { ARAgingSummaryQueryDto } from './ARAgingSummaryQuery.dto';
|
||||||
import { ARAgingSummaryResponseExample } from './ARAgingSummary.swagger';
|
import { ARAgingSummaryResponseExample } from './ARAgingSummary.swagger';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { ReportsAction } from '../../types/Report.types';
|
||||||
|
|
||||||
@Controller('reports/receivable-aging-summary')
|
@Controller('reports/receivable-aging-summary')
|
||||||
@ApiTags('Reports')
|
@ApiTags('Reports')
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class ARAgingSummaryController {
|
export class ARAgingSummaryController {
|
||||||
constructor(private readonly ARAgingSummaryApp: ARAgingSummaryApplication) {}
|
constructor(private readonly ARAgingSummaryApp: ARAgingSummaryApplication) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(ReportsAction.READ_AR_AGING_SUMMARY, AbilitySubject.Report)
|
||||||
@ApiOperation({ summary: 'Get receivable aging summary' })
|
@ApiOperation({ summary: 'Get receivable aging summary' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { Controller, Get, Headers, Query, Res } from '@nestjs/common';
|
import { Controller, Get, Headers, Query, Res, UseGuards } from '@nestjs/common';
|
||||||
import { AcceptType } from '@/constants/accept-type';
|
import { AcceptType } from '@/constants/accept-type';
|
||||||
import { BalanceSheetApplication } from './BalanceSheetApplication';
|
import { BalanceSheetApplication } from './BalanceSheetApplication';
|
||||||
import {
|
import {
|
||||||
@@ -11,10 +11,16 @@ import {
|
|||||||
import { BalanceSheetQueryDto } from './BalanceSheet.dto';
|
import { BalanceSheetQueryDto } from './BalanceSheet.dto';
|
||||||
import { BalanceSheetResponseExample } from './BalanceSheet.swagger';
|
import { BalanceSheetResponseExample } from './BalanceSheet.swagger';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { ReportsAction } from '../../types/Report.types';
|
||||||
|
|
||||||
@Controller('/reports/balance-sheet')
|
@Controller('/reports/balance-sheet')
|
||||||
@ApiTags('Reports')
|
@ApiTags('Reports')
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class BalanceSheetStatementController {
|
export class BalanceSheetStatementController {
|
||||||
constructor(private readonly balanceSheetApp: BalanceSheetApplication) {}
|
constructor(private readonly balanceSheetApp: BalanceSheetApplication) {}
|
||||||
|
|
||||||
@@ -25,6 +31,7 @@ export class BalanceSheetStatementController {
|
|||||||
* @param {string} acceptHeader - Accept header.
|
* @param {string} acceptHeader - Accept header.
|
||||||
*/
|
*/
|
||||||
@Get('')
|
@Get('')
|
||||||
|
@RequirePermission(ReportsAction.READ_BALANCE_SHEET, AbilitySubject.Report)
|
||||||
@ApiOperation({ summary: 'Get balance sheet statement' })
|
@ApiOperation({ summary: 'Get balance sheet statement' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { Controller, Get, Headers, Query, Res } from '@nestjs/common';
|
import { Controller, Get, Headers, Query, Res, UseGuards } from '@nestjs/common';
|
||||||
import { AcceptType } from '@/constants/accept-type';
|
import { AcceptType } from '@/constants/accept-type';
|
||||||
import { CashflowSheetApplication } from './CashflowSheetApplication';
|
import { CashflowSheetApplication } from './CashflowSheetApplication';
|
||||||
import {
|
import {
|
||||||
@@ -11,14 +11,21 @@ import {
|
|||||||
import { CashFlowStatementQueryDto } from './CashFlowStatementQuery.dto';
|
import { CashFlowStatementQueryDto } from './CashFlowStatementQuery.dto';
|
||||||
import { CashflowStatementResponseExample } from './CashflowStatement.swagger';
|
import { CashflowStatementResponseExample } from './CashflowStatement.swagger';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { ReportsAction } from '../../types/Report.types';
|
||||||
|
|
||||||
@Controller('reports/cashflow-statement')
|
@Controller('reports/cashflow-statement')
|
||||||
@ApiTags('Reports')
|
@ApiTags('Reports')
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class CashflowController {
|
export class CashflowController {
|
||||||
constructor(private readonly cashflowSheetApp: CashflowSheetApplication) { }
|
constructor(private readonly cashflowSheetApp: CashflowSheetApplication) { }
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(ReportsAction.READ_CASHFLOW, AbilitySubject.Report)
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
description: 'Cashflow statement report',
|
description: 'Cashflow statement report',
|
||||||
|
|||||||
@@ -5,22 +5,29 @@ import {
|
|||||||
ApiTags,
|
ApiTags,
|
||||||
} from '@nestjs/swagger';
|
} from '@nestjs/swagger';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { Controller, Get, Headers, Query, Res } from '@nestjs/common';
|
import { Controller, Get, Headers, Query, Res, UseGuards } from '@nestjs/common';
|
||||||
import { GeneralLedgerApplication } from './GeneralLedgerApplication';
|
import { GeneralLedgerApplication } from './GeneralLedgerApplication';
|
||||||
import { AcceptType } from '@/constants/accept-type';
|
import { AcceptType } from '@/constants/accept-type';
|
||||||
import { GeneralLedgerQueryDto } from './GeneralLedgerQuery.dto';
|
import { GeneralLedgerQueryDto } from './GeneralLedgerQuery.dto';
|
||||||
import { GeneralLedgerResponseExample } from './GeneralLedger.swagger';
|
import { GeneralLedgerResponseExample } from './GeneralLedger.swagger';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { ReportsAction } from '../../types/Report.types';
|
||||||
|
|
||||||
@Controller('/reports/general-ledger')
|
@Controller('/reports/general-ledger')
|
||||||
@ApiTags('Reports')
|
@ApiTags('Reports')
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class GeneralLedgerController {
|
export class GeneralLedgerController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly generalLedgerApplication: GeneralLedgerApplication,
|
private readonly generalLedgerApplication: GeneralLedgerApplication,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(ReportsAction.READ_GENERAL_LEDGET, AbilitySubject.Report)
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
description: 'General ledger report',
|
description: 'General ledger report',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Controller, Get, Headers, Query, Res } from '@nestjs/common';
|
import { Controller, Get, Headers, Query, Res, UseGuards } from '@nestjs/common';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { AcceptType } from '@/constants/accept-type';
|
import { AcceptType } from '@/constants/accept-type';
|
||||||
import { JournalSheetApplication } from './JournalSheetApplication';
|
import { JournalSheetApplication } from './JournalSheetApplication';
|
||||||
@@ -11,14 +11,21 @@ import {
|
|||||||
import { JournalSheetQueryDto } from './JournalSheetQuery.dto';
|
import { JournalSheetQueryDto } from './JournalSheetQuery.dto';
|
||||||
import { JournalSheetResponseExample } from './JournalSheet.swagger';
|
import { JournalSheetResponseExample } from './JournalSheet.swagger';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { ReportsAction } from '../../types/Report.types';
|
||||||
|
|
||||||
@Controller('/reports/journal')
|
@Controller('/reports/journal')
|
||||||
@ApiTags('Reports')
|
@ApiTags('Reports')
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class JournalSheetController {
|
export class JournalSheetController {
|
||||||
constructor(private readonly journalSheetApp: JournalSheetApplication) {}
|
constructor(private readonly journalSheetApp: JournalSheetApplication) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(ReportsAction.READ_JOURNAL, AbilitySubject.Report)
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
description: 'Journal report',
|
description: 'Journal report',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { Controller, Get, Headers, Query, Res } from '@nestjs/common';
|
import { Controller, Get, Headers, Query, Res, UseGuards } from '@nestjs/common';
|
||||||
import { ProfitLossSheetApplication } from './ProfitLossSheetApplication';
|
import { ProfitLossSheetApplication } from './ProfitLossSheetApplication';
|
||||||
import { AcceptType } from '@/constants/accept-type';
|
import { AcceptType } from '@/constants/accept-type';
|
||||||
import {
|
import {
|
||||||
@@ -11,10 +11,16 @@ import {
|
|||||||
import { ProfitLossSheetQueryDto } from './ProfitLossSheetQuery.dto';
|
import { ProfitLossSheetQueryDto } from './ProfitLossSheetQuery.dto';
|
||||||
import { ProfitLossSheetResponseExample } from './ProfitLossSheet.swagger';
|
import { ProfitLossSheetResponseExample } from './ProfitLossSheet.swagger';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { ReportsAction } from '../../types/Report.types';
|
||||||
|
|
||||||
@Controller('/reports/profit-loss-sheet')
|
@Controller('/reports/profit-loss-sheet')
|
||||||
@ApiTags('Reports')
|
@ApiTags('Reports')
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class ProfitLossSheetController {
|
export class ProfitLossSheetController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly profitLossSheetApp: ProfitLossSheetApplication,
|
private readonly profitLossSheetApp: ProfitLossSheetApplication,
|
||||||
@@ -27,6 +33,7 @@ export class ProfitLossSheetController {
|
|||||||
* @param {string} acceptHeader
|
* @param {string} acceptHeader
|
||||||
*/
|
*/
|
||||||
@Get('/')
|
@Get('/')
|
||||||
|
@RequirePermission(ReportsAction.READ_PROFIT_LOSS, AbilitySubject.Report)
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
description: 'Profit & loss statement',
|
description: 'Profit & loss statement',
|
||||||
|
|||||||
@@ -172,8 +172,11 @@ export class TrialBalanceSheet extends FinancialSheet {
|
|||||||
private filterNoneTransactions = (
|
private filterNoneTransactions = (
|
||||||
accountNode: ITrialBalanceAccount
|
accountNode: ITrialBalanceAccount
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const accountLedger = this.repository.totalAccountsLedger.whereAccountId(
|
const depsAccountsIds =
|
||||||
accountNode.id,
|
this.repository.accountsDepGraph.dependenciesOf(accountNode.id);
|
||||||
|
|
||||||
|
const accountLedger = this.repository.totalAccountsLedger.whereAccountsIds(
|
||||||
|
[accountNode.id, ...depsAccountsIds]
|
||||||
);
|
);
|
||||||
return !accountLedger.isEmpty();
|
return !accountLedger.isEmpty();
|
||||||
};
|
};
|
||||||
@@ -241,8 +244,8 @@ export class TrialBalanceSheet extends FinancialSheet {
|
|||||||
*/
|
*/
|
||||||
private accountsSection(accounts: ModelObject<Account>[]) {
|
private accountsSection(accounts: ModelObject<Account>[]) {
|
||||||
return R.compose(
|
return R.compose(
|
||||||
this.nestedAccountsNode,
|
|
||||||
this.accountsFilter,
|
this.accountsFilter,
|
||||||
|
this.nestedAccountsNode,
|
||||||
this.accountsMapper
|
this.accountsMapper
|
||||||
)(accounts);
|
)(accounts);
|
||||||
}
|
}
|
||||||
@@ -250,7 +253,6 @@ export class TrialBalanceSheet extends FinancialSheet {
|
|||||||
/**
|
/**
|
||||||
* Retrieve trial balance sheet statement data.
|
* Retrieve trial balance sheet statement data.
|
||||||
* Note: Retruns null in case there is no transactions between the given date periods.
|
* Note: Retruns null in case there is no transactions between the given date periods.
|
||||||
*
|
|
||||||
* @return {ITrialBalanceSheetData}
|
* @return {ITrialBalanceSheetData}
|
||||||
*/
|
*/
|
||||||
public reportData(): ITrialBalanceSheetData {
|
public reportData(): ITrialBalanceSheetData {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { InventoryAdjustmentsApplicationService } from './InventoryAdjustmentsApplication.service';
|
import { InventoryAdjustmentsApplicationService } from './InventoryAdjustmentsApplication.service';
|
||||||
import { IInventoryAdjustmentsFilter } from './types/InventoryAdjustments.types';
|
import { IInventoryAdjustmentsFilter } from './types/InventoryAdjustments.types';
|
||||||
@@ -21,17 +22,24 @@ import { InventoryAdjustment } from './models/InventoryAdjustment';
|
|||||||
import { CreateQuickInventoryAdjustmentDto } from './dtos/CreateQuickInventoryAdjustment.dto';
|
import { CreateQuickInventoryAdjustmentDto } from './dtos/CreateQuickInventoryAdjustment.dto';
|
||||||
import { InventoryAdjustmentResponseDto } from './dtos/InventoryAdjustmentResponse.dto';
|
import { InventoryAdjustmentResponseDto } from './dtos/InventoryAdjustmentResponse.dto';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { InventoryAdjustmentAction } from './types/InventoryAdjustments.types';
|
||||||
|
|
||||||
@Controller('inventory-adjustments')
|
@Controller('inventory-adjustments')
|
||||||
@ApiTags('Inventory Adjustments')
|
@ApiTags('Inventory Adjustments')
|
||||||
@ApiExtraModels(InventoryAdjustmentResponseDto)
|
@ApiExtraModels(InventoryAdjustmentResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class InventoryAdjustmentsController {
|
export class InventoryAdjustmentsController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly inventoryAdjustmentsApplicationService: InventoryAdjustmentsApplicationService,
|
private readonly inventoryAdjustmentsApplicationService: InventoryAdjustmentsApplicationService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Post('quick')
|
@Post('quick')
|
||||||
|
@RequirePermission(InventoryAdjustmentAction.CREATE, AbilitySubject.InventoryAdjustment)
|
||||||
@ApiOperation({ summary: 'Create a quick inventory adjustment.' })
|
@ApiOperation({ summary: 'Create a quick inventory adjustment.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -46,6 +54,7 @@ export class InventoryAdjustmentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(InventoryAdjustmentAction.DELETE, AbilitySubject.InventoryAdjustment)
|
||||||
@ApiOperation({ summary: 'Delete the given inventory adjustment.' })
|
@ApiOperation({ summary: 'Delete the given inventory adjustment.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -60,6 +69,7 @@ export class InventoryAdjustmentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(InventoryAdjustmentAction.VIEW, AbilitySubject.InventoryAdjustment)
|
||||||
@ApiOperation({ summary: 'Retrieves the inventory adjustments.' })
|
@ApiOperation({ summary: 'Retrieves the inventory adjustments.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -78,6 +88,7 @@ export class InventoryAdjustmentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(InventoryAdjustmentAction.VIEW, AbilitySubject.InventoryAdjustment)
|
||||||
@ApiOperation({ summary: 'Retrieves the inventory adjustment details.' })
|
@ApiOperation({ summary: 'Retrieves the inventory adjustment details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -94,6 +105,7 @@ export class InventoryAdjustmentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/publish')
|
@Put(':id/publish')
|
||||||
|
@RequirePermission(InventoryAdjustmentAction.EDIT, AbilitySubject.InventoryAdjustment)
|
||||||
@ApiOperation({ summary: 'Publish the given inventory adjustment.' })
|
@ApiOperation({ summary: 'Publish the given inventory adjustment.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
HttpCode,
|
HttpCode,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { TenantController } from '../Tenancy/Tenant.controller';
|
import { TenantController } from '../Tenancy/Tenant.controller';
|
||||||
import { ItemsApplicationService } from './ItemsApplication.service';
|
import { ItemsApplicationService } from './ItemsApplication.service';
|
||||||
@@ -35,6 +36,11 @@ import {
|
|||||||
ValidateBulkDeleteItemsResponseDto,
|
ValidateBulkDeleteItemsResponseDto,
|
||||||
} from './dtos/BulkDeleteItems.dto';
|
} from './dtos/BulkDeleteItems.dto';
|
||||||
import { ItemApiErrorResponseDto } from './dtos/ItemErrorResponse.dto';
|
import { ItemApiErrorResponseDto } from './dtos/ItemErrorResponse.dto';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { ItemAction } from '@/interfaces/Item';
|
||||||
|
|
||||||
@Controller('/items')
|
@Controller('/items')
|
||||||
@ApiTags('Items')
|
@ApiTags('Items')
|
||||||
@@ -48,12 +54,14 @@ import { ItemApiErrorResponseDto } from './dtos/ItemErrorResponse.dto';
|
|||||||
@ApiExtraModels(ValidateBulkDeleteItemsResponseDto)
|
@ApiExtraModels(ValidateBulkDeleteItemsResponseDto)
|
||||||
@ApiExtraModels(ItemApiErrorResponseDto)
|
@ApiExtraModels(ItemApiErrorResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class ItemsController extends TenantController {
|
export class ItemsController extends TenantController {
|
||||||
constructor(private readonly itemsApplication: ItemsApplicationService) {
|
constructor(private readonly itemsApplication: ItemsApplicationService) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(ItemAction.VIEW, AbilitySubject.Item)
|
||||||
@ApiOperation({ summary: 'Retrieves the item list.' })
|
@ApiOperation({ summary: 'Retrieves the item list.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -144,6 +152,7 @@ export class ItemsController extends TenantController {
|
|||||||
* @returns The updated item id.
|
* @returns The updated item id.
|
||||||
*/
|
*/
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(ItemAction.EDIT, AbilitySubject.Item)
|
||||||
@ApiOperation({ summary: 'Edit the given item (product or service).' })
|
@ApiOperation({ summary: 'Edit the given item (product or service).' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -174,6 +183,7 @@ export class ItemsController extends TenantController {
|
|||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
|
@RequirePermission(ItemAction.DELETE, AbilitySubject.Item)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Validates which items can be deleted and returns counts of deletable and non-deletable items.',
|
'Validates which items can be deleted and returns counts of deletable and non-deletable items.',
|
||||||
@@ -194,6 +204,7 @@ export class ItemsController extends TenantController {
|
|||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
|
@RequirePermission(ItemAction.DELETE, AbilitySubject.Item)
|
||||||
@ApiOperation({ summary: 'Deletes multiple items in bulk.' })
|
@ApiOperation({ summary: 'Deletes multiple items in bulk.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -208,6 +219,7 @@ export class ItemsController extends TenantController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(ItemAction.CREATE, AbilitySubject.Item)
|
||||||
@ApiOperation({ summary: 'Create a new item (product or service).' })
|
@ApiOperation({ summary: 'Create a new item (product or service).' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -230,6 +242,7 @@ export class ItemsController extends TenantController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(ItemAction.DELETE, AbilitySubject.Item)
|
||||||
@ApiOperation({ summary: 'Delete the given item (product or service).' })
|
@ApiOperation({ summary: 'Delete the given item (product or service).' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -255,6 +268,7 @@ export class ItemsController extends TenantController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id/inactivate')
|
@Patch(':id/inactivate')
|
||||||
|
@RequirePermission(ItemAction.EDIT, AbilitySubject.Item)
|
||||||
@ApiOperation({ summary: 'Inactivate the given item (product or service).' })
|
@ApiOperation({ summary: 'Inactivate the given item (product or service).' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -273,6 +287,7 @@ export class ItemsController extends TenantController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id/activate')
|
@Patch(':id/activate')
|
||||||
|
@RequirePermission(ItemAction.EDIT, AbilitySubject.Item)
|
||||||
@ApiOperation({ summary: 'Activate the given item (product or service).' })
|
@ApiOperation({ summary: 'Activate the given item (product or service).' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -291,6 +306,7 @@ export class ItemsController extends TenantController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(ItemAction.VIEW, AbilitySubject.Item)
|
||||||
@ApiOperation({ summary: 'Get the given item (product or service).' })
|
@ApiOperation({ summary: 'Get the given item (product or service).' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -312,6 +328,7 @@ export class ItemsController extends TenantController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/invoices')
|
@Get(':id/invoices')
|
||||||
|
@RequirePermission(ItemAction.VIEW, AbilitySubject.Item)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Retrieves the item associated invoices transactions.',
|
summary: 'Retrieves the item associated invoices transactions.',
|
||||||
})
|
})
|
||||||
@@ -337,6 +354,7 @@ export class ItemsController extends TenantController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/bills')
|
@Get(':id/bills')
|
||||||
|
@RequirePermission(ItemAction.VIEW, AbilitySubject.Item)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Retrieves the item associated bills transactions.',
|
summary: 'Retrieves the item associated bills transactions.',
|
||||||
})
|
})
|
||||||
@@ -362,6 +380,7 @@ export class ItemsController extends TenantController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/estimates')
|
@Get(':id/estimates')
|
||||||
|
@RequirePermission(ItemAction.VIEW, AbilitySubject.Item)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Retrieves the item associated estimates transactions.',
|
summary: 'Retrieves the item associated estimates transactions.',
|
||||||
})
|
})
|
||||||
@@ -387,6 +406,7 @@ export class ItemsController extends TenantController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/receipts')
|
@Get(':id/receipts')
|
||||||
|
@RequirePermission(ItemAction.VIEW, AbilitySubject.Item)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Retrieves the item associated receipts transactions.',
|
summary: 'Retrieves the item associated receipts transactions.',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -70,6 +70,16 @@ export class Item extends TenantBaseModel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model search roles.
|
||||||
|
*/
|
||||||
|
static get searchRoles() {
|
||||||
|
return [
|
||||||
|
{ condition: 'or', fieldKey: 'name', comparator: 'contains' },
|
||||||
|
{ condition: 'or', fieldKey: 'code', comparator: 'like' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ManualJournalsApplication } from './ManualJournalsApplication.service';
|
import { ManualJournalsApplication } from './ManualJournalsApplication.service';
|
||||||
import {
|
import {
|
||||||
@@ -29,16 +30,23 @@ import {
|
|||||||
BulkDeleteDto,
|
BulkDeleteDto,
|
||||||
ValidateBulkDeleteResponseDto,
|
ValidateBulkDeleteResponseDto,
|
||||||
} from '@/common/dtos/BulkDelete.dto';
|
} from '@/common/dtos/BulkDelete.dto';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { ManualJournalAction } from './types/ManualJournals.types';
|
||||||
|
|
||||||
@Controller('manual-journals')
|
@Controller('manual-journals')
|
||||||
@ApiTags('Manual Journals')
|
@ApiTags('Manual Journals')
|
||||||
@ApiExtraModels(ManualJournalResponseDto)
|
@ApiExtraModels(ManualJournalResponseDto)
|
||||||
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class ManualJournalsController {
|
export class ManualJournalsController {
|
||||||
constructor(private manualJournalsApplication: ManualJournalsApplication) { }
|
constructor(private manualJournalsApplication: ManualJournalsApplication) { }
|
||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
|
@RequirePermission(ManualJournalAction.Delete, AbilitySubject.ManualJournal)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Validate which manual journals can be deleted and return the results.',
|
'Validate which manual journals can be deleted and return the results.',
|
||||||
@@ -60,6 +68,7 @@ export class ManualJournalsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
|
@RequirePermission(ManualJournalAction.Delete, AbilitySubject.ManualJournal)
|
||||||
@ApiOperation({ summary: 'Deletes multiple manual journals.' })
|
@ApiOperation({ summary: 'Deletes multiple manual journals.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -75,6 +84,7 @@ export class ManualJournalsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(ManualJournalAction.Create, AbilitySubject.ManualJournal)
|
||||||
@ApiOperation({ summary: 'Create a new manual journal.' })
|
@ApiOperation({ summary: 'Create a new manual journal.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 201,
|
status: 201,
|
||||||
@@ -86,6 +96,7 @@ export class ManualJournalsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(ManualJournalAction.Edit, AbilitySubject.ManualJournal)
|
||||||
@ApiOperation({ summary: 'Edit the given manual journal.' })
|
@ApiOperation({ summary: 'Edit the given manual journal.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -110,6 +121,7 @@ export class ManualJournalsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(ManualJournalAction.Delete, AbilitySubject.ManualJournal)
|
||||||
@ApiOperation({ summary: 'Delete the given manual journal.' })
|
@ApiOperation({ summary: 'Delete the given manual journal.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -127,6 +139,7 @@ export class ManualJournalsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id/publish')
|
@Patch(':id/publish')
|
||||||
|
@RequirePermission(ManualJournalAction.Edit, AbilitySubject.ManualJournal)
|
||||||
@ApiOperation({ summary: 'Publish the given manual journal.' })
|
@ApiOperation({ summary: 'Publish the given manual journal.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -147,6 +160,7 @@ export class ManualJournalsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(ManualJournalAction.View, AbilitySubject.ManualJournal)
|
||||||
@ApiOperation({ summary: 'Retrieves the manual journal details.' })
|
@ApiOperation({ summary: 'Retrieves the manual journal details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -167,6 +181,7 @@ export class ManualJournalsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(ManualJournalAction.View, AbilitySubject.ManualJournal)
|
||||||
@ApiOperation({ summary: 'Retrieves the manual journals paginated list.' })
|
@ApiOperation({ summary: 'Retrieves the manual journals paginated list.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
HttpCode,
|
HttpCode,
|
||||||
Param,
|
Param,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { Throttle } from '@nestjs/throttler';
|
||||||
import { BuildOrganizationService } from './commands/BuildOrganization.service';
|
import { BuildOrganizationService } from './commands/BuildOrganization.service';
|
||||||
import {
|
import {
|
||||||
BuildOrganizationDto,
|
BuildOrganizationDto,
|
||||||
@@ -50,7 +51,7 @@ export class OrganizationController {
|
|||||||
private readonly updateOrganizationService: UpdateOrganizationService,
|
private readonly updateOrganizationService: UpdateOrganizationService,
|
||||||
private readonly getBuildOrganizationJobService: GetBuildOrganizationBuildJob,
|
private readonly getBuildOrganizationJobService: GetBuildOrganizationBuildJob,
|
||||||
private readonly orgBaseCurrencyLockingService: OrganizationBaseCurrencyLocking,
|
private readonly orgBaseCurrencyLockingService: OrganizationBaseCurrencyLocking,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
@Post('build')
|
@Post('build')
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
@@ -77,6 +78,7 @@ export class OrganizationController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('build/:buildJobId')
|
@Get('build/:buildJobId')
|
||||||
|
@Throttle({ default: { limit: 300, ttl: 60000 } }) // 300 req/min
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'buildJobId',
|
name: 'buildJobId',
|
||||||
required: true,
|
required: true,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
Res,
|
Res,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { PaymentReceivesApplication } from './PaymentReceived.application';
|
import { PaymentReceivesApplication } from './PaymentReceived.application';
|
||||||
import {
|
import {
|
||||||
@@ -38,6 +39,11 @@ import {
|
|||||||
BulkDeleteDto,
|
BulkDeleteDto,
|
||||||
ValidateBulkDeleteResponseDto,
|
ValidateBulkDeleteResponseDto,
|
||||||
} from '@/common/dtos/BulkDelete.dto';
|
} from '@/common/dtos/BulkDelete.dto';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { PaymentReceiveAction } from './types/PaymentReceived.types';
|
||||||
|
|
||||||
@Controller('payments-received')
|
@Controller('payments-received')
|
||||||
@ApiTags('Payments Received')
|
@ApiTags('Payments Received')
|
||||||
@@ -46,6 +52,7 @@ import {
|
|||||||
@ApiExtraModels(PaymentReceivedStateResponseDto)
|
@ApiExtraModels(PaymentReceivedStateResponseDto)
|
||||||
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class PaymentReceivesController {
|
export class PaymentReceivesController {
|
||||||
constructor(private paymentReceivesApplication: PaymentReceivesApplication) { }
|
constructor(private paymentReceivesApplication: PaymentReceivesApplication) { }
|
||||||
|
|
||||||
@@ -94,6 +101,7 @@ export class PaymentReceivesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(PaymentReceiveAction.Create, AbilitySubject.PaymentReceive)
|
||||||
@ApiOperation({ summary: 'Create a new payment received.' })
|
@ApiOperation({ summary: 'Create a new payment received.' })
|
||||||
public createPaymentReceived(
|
public createPaymentReceived(
|
||||||
@Body() paymentReceiveDTO: CreatePaymentReceivedDto,
|
@Body() paymentReceiveDTO: CreatePaymentReceivedDto,
|
||||||
@@ -104,6 +112,7 @@ export class PaymentReceivesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(PaymentReceiveAction.Edit, AbilitySubject.PaymentReceive)
|
||||||
@ApiOperation({ summary: 'Edit the given payment received.' })
|
@ApiOperation({ summary: 'Edit the given payment received.' })
|
||||||
public editPaymentReceive(
|
public editPaymentReceive(
|
||||||
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
||||||
@@ -116,6 +125,7 @@ export class PaymentReceivesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(PaymentReceiveAction.Delete, AbilitySubject.PaymentReceive)
|
||||||
@ApiOperation({ summary: 'Delete the given payment received.' })
|
@ApiOperation({ summary: 'Delete the given payment received.' })
|
||||||
public deletePaymentReceive(
|
public deletePaymentReceive(
|
||||||
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
@Param('id', ParseIntPipe) paymentReceiveId: number,
|
||||||
@@ -126,6 +136,7 @@ export class PaymentReceivesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(PaymentReceiveAction.View, AbilitySubject.PaymentReceive)
|
||||||
@ApiOperation({ summary: 'Retrieves the payment received list.' })
|
@ApiOperation({ summary: 'Retrieves the payment received list.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -151,6 +162,7 @@ export class PaymentReceivesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
|
@RequirePermission(PaymentReceiveAction.Delete, AbilitySubject.PaymentReceive)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Validates which payments received can be deleted and returns the results.',
|
'Validates which payments received can be deleted and returns the results.',
|
||||||
@@ -172,6 +184,7 @@ export class PaymentReceivesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
|
@RequirePermission(PaymentReceiveAction.Delete, AbilitySubject.PaymentReceive)
|
||||||
@ApiOperation({ summary: 'Deletes multiple payments received.' })
|
@ApiOperation({ summary: 'Deletes multiple payments received.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -187,6 +200,7 @@ export class PaymentReceivesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('state')
|
@Get('state')
|
||||||
|
@RequirePermission(PaymentReceiveAction.View, AbilitySubject.PaymentReceive)
|
||||||
@ApiOperation({ summary: 'Retrieves the payment received state.' })
|
@ApiOperation({ summary: 'Retrieves the payment received state.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -200,6 +214,7 @@ export class PaymentReceivesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/invoices')
|
@Get(':id/invoices')
|
||||||
|
@RequirePermission(PaymentReceiveAction.View, AbilitySubject.PaymentReceive)
|
||||||
@ApiOperation({ summary: 'Retrieves the payment received invoices.' })
|
@ApiOperation({ summary: 'Retrieves the payment received invoices.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -215,6 +230,7 @@ export class PaymentReceivesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(PaymentReceiveAction.View, AbilitySubject.PaymentReceive)
|
||||||
@ApiOperation({ summary: 'Retrieves the payment received details.' })
|
@ApiOperation({ summary: 'Retrieves the payment received details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { BullBoardModule } from '@bull-board/nestjs';
|
import { BullBoardModule } from '@bull-board/nestjs';
|
||||||
import { BullAdapter } from '@bull-board/api/bullAdapter';
|
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
|
||||||
import { BullModule } from '@nestjs/bull';
|
import { BullModule } from '@nestjs/bullmq';
|
||||||
import { PaymentReceivesController } from './PaymentsReceived.controller';
|
import { PaymentReceivesController } from './PaymentsReceived.controller';
|
||||||
import { PaymentReceivesApplication } from './PaymentReceived.application';
|
import { PaymentReceivesApplication } from './PaymentReceived.application';
|
||||||
import { CreatePaymentReceivedService } from './commands/CreatePaymentReceived.serivce';
|
import { CreatePaymentReceivedService } from './commands/CreatePaymentReceived.serivce';
|
||||||
@@ -99,7 +99,7 @@ import { ValidateBulkDeletePaymentReceivedService } from './ValidateBulkDeletePa
|
|||||||
BullModule.registerQueue({ name: SEND_PAYMENT_RECEIVED_MAIL_QUEUE }),
|
BullModule.registerQueue({ name: SEND_PAYMENT_RECEIVED_MAIL_QUEUE }),
|
||||||
BullBoardModule.forFeature({
|
BullBoardModule.forFeature({
|
||||||
name: SEND_PAYMENT_RECEIVED_MAIL_QUEUE,
|
name: SEND_PAYMENT_RECEIVED_MAIL_QUEUE,
|
||||||
adapter: BullAdapter,
|
adapter: BullMQAdapter,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { JOB_REF, Process, Processor } from '@nestjs/bull';
|
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||||
import { Job } from 'bull';
|
import { Job } from 'bullmq';
|
||||||
import { Inject, Scope } from '@nestjs/common';
|
import { Scope } from '@nestjs/common';
|
||||||
import { ClsService, UseCls } from 'nestjs-cls';
|
import { ClsService, UseCls } from 'nestjs-cls';
|
||||||
import {
|
import {
|
||||||
SEND_PAYMENT_RECEIVED_MAIL_JOB,
|
SEND_PAYMENT_RECEIVED_MAIL_JOB,
|
||||||
@@ -13,20 +13,18 @@ import { SendPaymentReceivedMailPayload } from '../types/PaymentReceived.types';
|
|||||||
name: SEND_PAYMENT_RECEIVED_MAIL_QUEUE,
|
name: SEND_PAYMENT_RECEIVED_MAIL_QUEUE,
|
||||||
scope: Scope.REQUEST,
|
scope: Scope.REQUEST,
|
||||||
})
|
})
|
||||||
export class SendPaymentReceivedMailProcessor {
|
export class SendPaymentReceivedMailProcessor extends WorkerHost {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly sendPaymentReceivedMail: SendPaymentReceiveMailNotification,
|
private readonly sendPaymentReceivedMail: SendPaymentReceiveMailNotification,
|
||||||
private readonly clsService: ClsService,
|
private readonly clsService: ClsService,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
@Inject(JOB_REF)
|
|
||||||
private readonly jobRef: Job<SendPaymentReceivedMailPayload>,
|
|
||||||
) { }
|
|
||||||
|
|
||||||
@Process(SEND_PAYMENT_RECEIVED_MAIL_JOB)
|
|
||||||
@UseCls()
|
@UseCls()
|
||||||
async handleSendMail() {
|
async process(job: Job<SendPaymentReceivedMailPayload>) {
|
||||||
const { messageOptions, paymentReceivedId, organizationId, userId } =
|
const { messageOptions, paymentReceivedId, organizationId, userId } =
|
||||||
this.jobRef.data;
|
job.data;
|
||||||
|
|
||||||
this.clsService.set('organizationId', organizationId);
|
this.clsService.set('organizationId', organizationId);
|
||||||
this.clsService.set('userId', userId);
|
this.clsService.set('userId', userId);
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ export class AuthorizationGuard implements CanActivate {
|
|||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
const request = context.switchToHttp().getRequest<Request>();
|
const request = context.switchToHttp().getRequest<Request>();
|
||||||
const { user } = request as any;
|
const { user } = request as any;
|
||||||
|
const userId = this.clsService.get('userId');
|
||||||
|
|
||||||
if (ABILITIES_CACHE.has(user.id)) {
|
if (ABILITIES_CACHE.has(userId)) {
|
||||||
(request as any).ability = ABILITIES_CACHE.get(user.id);
|
(request as any).ability = ABILITIES_CACHE.get(userId);
|
||||||
} else {
|
} else {
|
||||||
const ability = await this.getAbilityForUser();
|
const ability = await this.getAbilityForUser();
|
||||||
(request as any).ability = ability;
|
(request as any).ability = ability;
|
||||||
|
|||||||
67
packages/server/src/modules/Roles/Permission.guard.ts
Normal file
67
packages/server/src/modules/Roles/Permission.guard.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
ForbiddenException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
|
import { Request } from 'express';
|
||||||
|
import { REQUIRED_PERMISSION_KEY, RequiredPermission } from './RequirePermission.decorator';
|
||||||
|
import { AbilitySubject } from './Roles.types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guard that checks if the user has the required permission to access a route.
|
||||||
|
* Uses CASL ability instance attached to the request by AuthorizationGuard.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class PermissionGuard implements CanActivate {
|
||||||
|
constructor(private readonly reflector: Reflector) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the user has the required permission to access the route.
|
||||||
|
* @param context - The execution context
|
||||||
|
* @returns A boolean indicating if the user can access the route
|
||||||
|
* @throws ForbiddenException if the user doesn't have the required permission
|
||||||
|
*/
|
||||||
|
canActivate(context: ExecutionContext): boolean {
|
||||||
|
const requiredPermission = this.reflector.getAllAndOverride<RequiredPermission>(
|
||||||
|
REQUIRED_PERMISSION_KEY,
|
||||||
|
[context.getHandler(), context.getClass()],
|
||||||
|
);
|
||||||
|
|
||||||
|
// If no permission is required, allow access
|
||||||
|
if (!requiredPermission) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = context.switchToHttp().getRequest<Request>();
|
||||||
|
const ability = (request as any).ability;
|
||||||
|
|
||||||
|
// If no ability instance is attached to the request, deny access
|
||||||
|
if (!ability) {
|
||||||
|
throw new ForbiddenException('Ability instance not found. Ensure AuthorizationGuard is applied.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { ability: action, subject } = requiredPermission;
|
||||||
|
|
||||||
|
// Check if the user has the required permission using CASL ability
|
||||||
|
const hasPermission = ability.can(action, subject);
|
||||||
|
|
||||||
|
if (!hasPermission) {
|
||||||
|
throw new ForbiddenException(
|
||||||
|
`You do not have permission to ${action} ${subject}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to check if a subject value is a valid AbilitySubject.
|
||||||
|
* @param subject - The subject value to check
|
||||||
|
* @returns True if the subject is a valid AbilitySubject enum value
|
||||||
|
*/
|
||||||
|
private isValidSubject(subject: string): subject is AbilitySubject {
|
||||||
|
return Object.values(AbilitySubject).includes(subject as AbilitySubject);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { AbilitySubject } from './Roles.types';
|
||||||
|
|
||||||
|
export const REQUIRED_PERMISSION_KEY = 'requiredPermission';
|
||||||
|
|
||||||
|
export interface RequiredPermission {
|
||||||
|
ability: string;
|
||||||
|
subject: AbilitySubject | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorator to specify required ability and subject for a route handler or controller.
|
||||||
|
* @param ability - The ability/action required (e.g., 'Create', 'View', 'Edit', 'Delete')
|
||||||
|
* @param subject - The subject/entity the ability applies to (e.g., AbilitySubject.Item, AbilitySubject.SaleInvoice)
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* @RequirePermission('Create', AbilitySubject.Item)
|
||||||
|
* @Post()
|
||||||
|
* async createItem(@Body() dto: CreateItemDto) { ... }
|
||||||
|
*
|
||||||
|
* @RequirePermission('View', AbilitySubject.SaleInvoice)
|
||||||
|
* @Get(':id')
|
||||||
|
* async getInvoice(@Param('id') id: number) { ... }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const RequirePermission = (
|
||||||
|
ability: string,
|
||||||
|
subject: AbilitySubject | string,
|
||||||
|
) => SetMetadata(REQUIRED_PERMISSION_KEY, { ability, subject });
|
||||||
@@ -10,6 +10,8 @@ import { RolePermission } from './models/RolePermission.model';
|
|||||||
import { RolesController } from './Roles.controller';
|
import { RolesController } from './Roles.controller';
|
||||||
import { RolesApplication } from './Roles.application';
|
import { RolesApplication } from './Roles.application';
|
||||||
import { RolePermissionsSchema } from './queries/RolePermissionsSchema';
|
import { RolePermissionsSchema } from './queries/RolePermissionsSchema';
|
||||||
|
import { AuthorizationGuard } from './Authorization.guard';
|
||||||
|
import { PermissionGuard } from './Permission.guard';
|
||||||
|
|
||||||
const models = [
|
const models = [
|
||||||
RegisterTenancyModel(Role),
|
RegisterTenancyModel(Role),
|
||||||
@@ -25,9 +27,11 @@ const models = [
|
|||||||
GetRoleService,
|
GetRoleService,
|
||||||
GetRolesService,
|
GetRolesService,
|
||||||
RolesApplication,
|
RolesApplication,
|
||||||
RolePermissionsSchema
|
RolePermissionsSchema,
|
||||||
|
AuthorizationGuard,
|
||||||
|
PermissionGuard,
|
||||||
],
|
],
|
||||||
controllers: [RolesController],
|
controllers: [RolesController],
|
||||||
exports: [...models],
|
exports: [...models, AuthorizationGuard, PermissionGuard],
|
||||||
})
|
})
|
||||||
export class RolesModule {}
|
export class RolesModule {}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { IsOptional } from '@/common/decorators/Validators';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
@@ -41,7 +42,7 @@ export class CommandRolePermissionDto {
|
|||||||
export class CreateRolePermissionDto extends CommandRolePermissionDto { }
|
export class CreateRolePermissionDto extends CommandRolePermissionDto { }
|
||||||
export class EditRolePermissionDto extends CommandRolePermissionDto {
|
export class EditRolePermissionDto extends CommandRolePermissionDto {
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsNotEmpty()
|
@IsOptional()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: 1,
|
example: 1,
|
||||||
description: 'The permission ID',
|
description: 'The permission ID',
|
||||||
@@ -59,7 +60,6 @@ class CommandRoleDto {
|
|||||||
roleName: string;
|
roleName: string;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: 'Administrator',
|
example: 'Administrator',
|
||||||
description: 'The description of the role',
|
description: 'The description of the role',
|
||||||
@@ -71,9 +71,9 @@ export class CreateRoleDto extends CommandRoleDto {
|
|||||||
@IsArray()
|
@IsArray()
|
||||||
@ArrayMinSize(1)
|
@ArrayMinSize(1)
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => CommandRolePermissionDto)
|
@Type(() => CreateRolePermissionDto)
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: [CommandRolePermissionDto],
|
type: [CreateRolePermissionDto],
|
||||||
description: 'The permissions of the role',
|
description: 'The permissions of the role',
|
||||||
})
|
})
|
||||||
permissions: Array<CreateRolePermissionDto>;
|
permissions: Array<CreateRolePermissionDto>;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
Res,
|
Res,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { SaleEstimatesApplication } from './SaleEstimates.application';
|
import { SaleEstimatesApplication } from './SaleEstimates.application';
|
||||||
import {
|
import {
|
||||||
@@ -40,6 +41,11 @@ import {
|
|||||||
BulkDeleteDto,
|
BulkDeleteDto,
|
||||||
ValidateBulkDeleteResponseDto,
|
ValidateBulkDeleteResponseDto,
|
||||||
} from '@/common/dtos/BulkDelete.dto';
|
} from '@/common/dtos/BulkDelete.dto';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { SaleEstimateAction } from './types/SaleEstimates.types';
|
||||||
|
|
||||||
@Controller('sale-estimates')
|
@Controller('sale-estimates')
|
||||||
@ApiTags('Sale Estimates')
|
@ApiTags('Sale Estimates')
|
||||||
@@ -48,8 +54,10 @@ import {
|
|||||||
@ApiExtraModels(SaleEstiamteStateResponseDto)
|
@ApiExtraModels(SaleEstiamteStateResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class SaleEstimatesController {
|
export class SaleEstimatesController {
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
|
@RequirePermission(SaleEstimateAction.Delete, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Validates which sale estimates can be deleted and returns the results.',
|
'Validates which sale estimates can be deleted and returns the results.',
|
||||||
@@ -71,6 +79,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
|
@RequirePermission(SaleEstimateAction.Delete, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Deletes multiple sale estimates.' })
|
@ApiOperation({ summary: 'Deletes multiple sale estimates.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -93,6 +102,7 @@ export class SaleEstimatesController {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(SaleEstimateAction.Create, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Create a new sale estimate.' })
|
@ApiOperation({ summary: 'Create a new sale estimate.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -105,6 +115,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Edit the given sale estimate.' })
|
@ApiOperation({ summary: 'Edit the given sale estimate.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -131,6 +142,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(SaleEstimateAction.Delete, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Delete the given sale estimate.' })
|
@ApiOperation({ summary: 'Delete the given sale estimate.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -153,6 +165,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('state')
|
@Get('state')
|
||||||
|
@RequirePermission(SaleEstimateAction.View, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Retrieves the sale estimate state.' })
|
@ApiOperation({ summary: 'Retrieves the sale estimate state.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -166,6 +179,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(SaleEstimateAction.View, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Retrieves the sale estimates.' })
|
@ApiOperation({ summary: 'Retrieves the sale estimates.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -189,6 +203,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post(':id/deliver')
|
@Post(':id/deliver')
|
||||||
|
@RequirePermission(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Deliver the given sale estimate.' })
|
@ApiOperation({ summary: 'Deliver the given sale estimate.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -207,6 +222,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/approve')
|
@Put(':id/approve')
|
||||||
|
@RequirePermission(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Approve the given sale estimate.' })
|
@ApiOperation({ summary: 'Approve the given sale estimate.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'id',
|
name: 'id',
|
||||||
@@ -221,6 +237,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/reject')
|
@Put(':id/reject')
|
||||||
|
@RequirePermission(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Reject the given sale estimate.' })
|
@ApiOperation({ summary: 'Reject the given sale estimate.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'id',
|
name: 'id',
|
||||||
@@ -235,6 +252,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post(':id/notify-sms')
|
@Post(':id/notify-sms')
|
||||||
|
@RequirePermission(SaleEstimateAction.NotifyBySms, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Notify the given sale estimate by SMS.' })
|
@ApiOperation({ summary: 'Notify the given sale estimate by SMS.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'id',
|
name: 'id',
|
||||||
@@ -251,6 +269,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/sms-details')
|
@Get(':id/sms-details')
|
||||||
|
@RequirePermission(SaleEstimateAction.View, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Retrieves the sale estimate SMS details.' })
|
@ApiOperation({ summary: 'Retrieves the sale estimate SMS details.' })
|
||||||
public getSaleEstimateSmsDetails(
|
public getSaleEstimateSmsDetails(
|
||||||
@Param('id', ParseIntPipe) saleEstimateId: number,
|
@Param('id', ParseIntPipe) saleEstimateId: number,
|
||||||
@@ -262,6 +281,7 @@ export class SaleEstimatesController {
|
|||||||
|
|
||||||
@Post(':id/mail')
|
@Post(':id/mail')
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
|
@RequirePermission(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Send the given sale estimate by mail.' })
|
@ApiOperation({ summary: 'Send the given sale estimate by mail.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'id',
|
name: 'id',
|
||||||
@@ -280,6 +300,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/mail')
|
@Get(':id/mail')
|
||||||
|
@RequirePermission(SaleEstimateAction.View, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({ summary: 'Retrieves the sale estimate mail state.' })
|
@ApiOperation({ summary: 'Retrieves the sale estimate mail state.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'id',
|
name: 'id',
|
||||||
@@ -296,6 +317,7 @@ export class SaleEstimatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(SaleEstimateAction.View, AbilitySubject.SaleEstimate)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Retrieves the sale estimate details.',
|
summary: 'Retrieves the sale estimate details.',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { BullBoardModule } from '@bull-board/nestjs';
|
import { BullBoardModule } from '@bull-board/nestjs';
|
||||||
import { BullAdapter } from '@bull-board/api/bullAdapter';
|
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
|
||||||
import { BullModule } from '@nestjs/bull';
|
import { BullModule } from '@nestjs/bullmq';
|
||||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||||
import { TenancyDatabaseModule } from '../Tenancy/TenancyDB/TenancyDB.module';
|
import { TenancyDatabaseModule } from '../Tenancy/TenancyDB/TenancyDB.module';
|
||||||
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
|
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
|
||||||
@@ -58,7 +58,7 @@ import { SendSaleEstimateMailProcess } from './processes/SendSaleEstimateMail.pr
|
|||||||
BullModule.registerQueue({ name: SendSaleEstimateMailQueue }),
|
BullModule.registerQueue({ name: SendSaleEstimateMailQueue }),
|
||||||
BullBoardModule.forFeature({
|
BullBoardModule.forFeature({
|
||||||
name: SendSaleEstimateMailQueue,
|
name: SendSaleEstimateMailQueue,
|
||||||
adapter: BullAdapter,
|
adapter: BullMQAdapter,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
controllers: [SaleEstimatesController],
|
controllers: [SaleEstimatesController],
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { InjectQueue } from '@nestjs/bullmq';
|
import { InjectQueue } from '@nestjs/bullmq';
|
||||||
import { Queue } from 'bull';
|
import { Queue } from 'bullmq';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { ContactMailNotification } from '@/modules/MailNotification/ContactMailNotification';
|
import { ContactMailNotification } from '@/modules/MailNotification/ContactMailNotification';
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Process, Processor } from '@nestjs/bull';
|
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||||
import { Job } from 'bull';
|
import { Job } from 'bullmq';
|
||||||
import { Inject, Scope } from '@nestjs/common';
|
import { Scope } from '@nestjs/common';
|
||||||
import { JOB_REF } from '@nestjs/bull';
|
|
||||||
import {
|
import {
|
||||||
SendSaleEstimateMailJob,
|
SendSaleEstimateMailJob,
|
||||||
SendSaleEstimateMailQueue,
|
SendSaleEstimateMailQueue,
|
||||||
@@ -13,18 +12,17 @@ import { ClsService, UseCls } from 'nestjs-cls';
|
|||||||
name: SendSaleEstimateMailQueue,
|
name: SendSaleEstimateMailQueue,
|
||||||
scope: Scope.REQUEST,
|
scope: Scope.REQUEST,
|
||||||
})
|
})
|
||||||
export class SendSaleEstimateMailProcess {
|
export class SendSaleEstimateMailProcess extends WorkerHost {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly sendEstimateMailService: SendSaleEstimateMail,
|
private readonly sendEstimateMailService: SendSaleEstimateMail,
|
||||||
private readonly clsService: ClsService,
|
private readonly clsService: ClsService,
|
||||||
@Inject(JOB_REF)
|
) {
|
||||||
private readonly jobRef: Job,
|
super();
|
||||||
) { }
|
}
|
||||||
|
|
||||||
@Process(SendSaleEstimateMailJob)
|
|
||||||
@UseCls()
|
@UseCls()
|
||||||
async handleSendMail() {
|
async process(job: Job) {
|
||||||
const { saleEstimateId, messageOptions, organizationId, userId } = this.jobRef.data;
|
const { saleEstimateId, messageOptions, organizationId, userId } = job.data;
|
||||||
|
|
||||||
this.clsService.set('organizationId', organizationId);
|
this.clsService.set('organizationId', organizationId);
|
||||||
this.clsService.set('userId', userId);
|
this.clsService.set('userId', userId);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
Res,
|
Res,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
ISaleInvoiceWriteoffDTO,
|
ISaleInvoiceWriteoffDTO,
|
||||||
@@ -43,6 +44,11 @@ import {
|
|||||||
BulkDeleteDto,
|
BulkDeleteDto,
|
||||||
ValidateBulkDeleteResponseDto,
|
ValidateBulkDeleteResponseDto,
|
||||||
} from '@/common/dtos/BulkDelete.dto';
|
} from '@/common/dtos/BulkDelete.dto';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { SaleInvoiceAction } from './SaleInvoice.types';
|
||||||
|
|
||||||
@Controller('sale-invoices')
|
@Controller('sale-invoices')
|
||||||
@ApiTags('Sale Invoices')
|
@ApiTags('Sale Invoices')
|
||||||
@@ -52,10 +58,12 @@ import {
|
|||||||
@ApiExtraModels(GenerateSaleInvoiceSharableLinkResponseDto)
|
@ApiExtraModels(GenerateSaleInvoiceSharableLinkResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class SaleInvoicesController {
|
export class SaleInvoicesController {
|
||||||
constructor(private saleInvoiceApplication: SaleInvoiceApplication) { }
|
constructor(private saleInvoiceApplication: SaleInvoiceApplication) { }
|
||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
|
@RequirePermission(SaleInvoiceAction.Delete, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Validates which sale invoices can be deleted and returns the results.',
|
'Validates which sale invoices can be deleted and returns the results.',
|
||||||
@@ -77,6 +85,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
|
@RequirePermission(SaleInvoiceAction.Delete, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Deletes multiple sale invoices.' })
|
@ApiOperation({ summary: 'Deletes multiple sale invoices.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -90,6 +99,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(SaleInvoiceAction.Create, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Create a new sale invoice.' })
|
@ApiOperation({ summary: 'Create a new sale invoice.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 201,
|
status: 201,
|
||||||
@@ -121,6 +131,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(SaleInvoiceAction.Edit, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Edit the given sale invoice.' })
|
@ApiOperation({ summary: 'Edit the given sale invoice.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -141,6 +152,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(SaleInvoiceAction.Delete, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Delete the given sale invoice.' })
|
@ApiOperation({ summary: 'Delete the given sale invoice.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -158,6 +170,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('receivable')
|
@Get('receivable')
|
||||||
|
@RequirePermission(SaleInvoiceAction.View, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Retrieves the receivable sale invoices.' })
|
@ApiOperation({ summary: 'Retrieves the receivable sale invoices.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -176,6 +189,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('state')
|
@Get('state')
|
||||||
|
@RequirePermission(SaleInvoiceAction.View, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Retrieves the sale invoice state.' })
|
@ApiOperation({ summary: 'Retrieves the sale invoice state.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -190,6 +204,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(SaleInvoiceAction.View, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Retrieves the sale invoice details.' })
|
@ApiOperation({ summary: 'Retrieves the sale invoice details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -228,6 +243,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(SaleInvoiceAction.View, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Retrieves the sale invoices.' })
|
@ApiOperation({ summary: 'Retrieves the sale invoices.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -251,6 +267,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/deliver')
|
@Put(':id/deliver')
|
||||||
|
@RequirePermission(SaleInvoiceAction.Edit, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Deliver the given sale invoice.' })
|
@ApiOperation({ summary: 'Deliver the given sale invoice.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -269,6 +286,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post(':id/writeoff')
|
@Post(':id/writeoff')
|
||||||
|
@RequirePermission(SaleInvoiceAction.Writeoff, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Write off the given sale invoice.' })
|
@ApiOperation({ summary: 'Write off the given sale invoice.' })
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
@@ -290,6 +308,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post(':id/cancel-writeoff')
|
@Post(':id/cancel-writeoff')
|
||||||
|
@RequirePermission(SaleInvoiceAction.Writeoff, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Cancel the written off sale invoice.' })
|
@ApiOperation({ summary: 'Cancel the written off sale invoice.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -309,6 +328,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/payments')
|
@Get(':id/payments')
|
||||||
|
@RequirePermission(SaleInvoiceAction.View, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Retrieves the sale invoice payments.' })
|
@ApiOperation({ summary: 'Retrieves the sale invoice payments.' })
|
||||||
@ApiResponse({ status: 404, description: 'The sale invoice not found.' })
|
@ApiResponse({ status: 404, description: 'The sale invoice not found.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
@@ -322,6 +342,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/html')
|
@Get(':id/html')
|
||||||
|
@RequirePermission(SaleInvoiceAction.View, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Retrieves the sale invoice HTML.' })
|
@ApiOperation({ summary: 'Retrieves the sale invoice HTML.' })
|
||||||
@ApiResponse({ status: 404, description: 'The sale invoice not found.' })
|
@ApiResponse({ status: 404, description: 'The sale invoice not found.' })
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
@@ -335,6 +356,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/mail')
|
@Get(':id/mail')
|
||||||
|
@RequirePermission(SaleInvoiceAction.View, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({ summary: 'Retrieves the sale invoice mail state.' })
|
@ApiOperation({ summary: 'Retrieves the sale invoice mail state.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -354,6 +376,7 @@ export class SaleInvoicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post(':id/generate-link')
|
@Post(':id/generate-link')
|
||||||
|
@RequirePermission(SaleInvoiceAction.Edit, AbilitySubject.SaleInvoice)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Generate sharable sale invoice link (private or public)',
|
summary: 'Generate sharable sale invoice link (private or public)',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ import { DynamicListModule } from '../DynamicListing/DynamicList.module';
|
|||||||
import { MailNotificationModule } from '../MailNotification/MailNotification.module';
|
import { MailNotificationModule } from '../MailNotification/MailNotification.module';
|
||||||
import { SendSaleInvoiceMailProcessor } from './processors/SendSaleInvoiceMail.processor';
|
import { SendSaleInvoiceMailProcessor } from './processors/SendSaleInvoiceMail.processor';
|
||||||
import { BullBoardModule } from '@bull-board/nestjs';
|
import { BullBoardModule } from '@bull-board/nestjs';
|
||||||
import { BullAdapter } from '@bull-board/api/bullAdapter';
|
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
|
||||||
import { BullModule } from '@nestjs/bull';
|
import { BullModule } from '@nestjs/bullmq';
|
||||||
import { SendSaleInvoiceQueue } from './constants';
|
import { SendSaleInvoiceQueue } from './constants';
|
||||||
import { InvoicePaymentIntegrationSubscriber } from './subscribers/InvoicePaymentIntegrationSubscriber';
|
import { InvoicePaymentIntegrationSubscriber } from './subscribers/InvoicePaymentIntegrationSubscriber';
|
||||||
import { InvoiceChangeStatusOnMailSentSubscriber } from './subscribers/InvoiceChangeStatusOnMailSentSubscriber';
|
import { InvoiceChangeStatusOnMailSentSubscriber } from './subscribers/InvoiceChangeStatusOnMailSentSubscriber';
|
||||||
@@ -85,7 +85,7 @@ import { ValidateBulkDeleteSaleInvoicesService } from './ValidateBulkDeleteSaleI
|
|||||||
BullModule.registerQueue({ name: SendSaleInvoiceQueue }),
|
BullModule.registerQueue({ name: SendSaleInvoiceQueue }),
|
||||||
BullBoardModule.forFeature({
|
BullBoardModule.forFeature({
|
||||||
name: SendSaleInvoiceQueue,
|
name: SendSaleInvoiceQueue,
|
||||||
adapter: BullAdapter,
|
adapter: BullMQAdapter,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
controllers: [SaleInvoicesController],
|
controllers: [SaleInvoicesController],
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { JOB_REF, Process, Processor } from '@nestjs/bull';
|
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||||
import { Job } from 'bull';
|
import { Job } from 'bullmq';
|
||||||
import { SendSaleInvoiceMailJob, SendSaleInvoiceQueue } from '../constants';
|
import { SendSaleInvoiceMailJob, SendSaleInvoiceQueue } from '../constants';
|
||||||
import { SendSaleInvoiceMail } from '../commands/SendSaleInvoiceMail';
|
import { SendSaleInvoiceMail } from '../commands/SendSaleInvoiceMail';
|
||||||
import { Inject, Scope } from '@nestjs/common';
|
import { Scope } from '@nestjs/common';
|
||||||
import { REQUEST } from '@nestjs/core';
|
|
||||||
import { ClsService, UseCls } from 'nestjs-cls';
|
import { ClsService, UseCls } from 'nestjs-cls';
|
||||||
import { SendSaleInvoiceMailJobPayload } from '../SaleInvoice.types';
|
import { SendSaleInvoiceMailJobPayload } from '../SaleInvoice.types';
|
||||||
|
|
||||||
@@ -11,20 +10,18 @@ import { SendSaleInvoiceMailJobPayload } from '../SaleInvoice.types';
|
|||||||
name: SendSaleInvoiceQueue,
|
name: SendSaleInvoiceQueue,
|
||||||
scope: Scope.REQUEST,
|
scope: Scope.REQUEST,
|
||||||
})
|
})
|
||||||
export class SendSaleInvoiceMailProcessor {
|
export class SendSaleInvoiceMailProcessor extends WorkerHost {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly sendSaleInvoiceMail: SendSaleInvoiceMail,
|
private readonly sendSaleInvoiceMail: SendSaleInvoiceMail,
|
||||||
@Inject(REQUEST) private readonly request: Request,
|
|
||||||
@Inject(JOB_REF)
|
|
||||||
private readonly jobRef: Job<SendSaleInvoiceMailJobPayload>,
|
|
||||||
private readonly clsService: ClsService,
|
private readonly clsService: ClsService,
|
||||||
) { }
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
@Process(SendSaleInvoiceMailJob)
|
|
||||||
@UseCls()
|
@UseCls()
|
||||||
async handleSendInvoice() {
|
async process(job: Job<SendSaleInvoiceMailJobPayload>) {
|
||||||
const { messageOptions, saleInvoiceId, organizationId, userId } =
|
const { messageOptions, saleInvoiceId, organizationId, userId } =
|
||||||
this.jobRef.data;
|
job.data;
|
||||||
|
|
||||||
this.clsService.set('organizationId', organizationId);
|
this.clsService.set('organizationId', organizationId);
|
||||||
this.clsService.set('userId', userId);
|
this.clsService.set('userId', userId);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { BullBoardModule } from '@bull-board/nestjs';
|
import { BullBoardModule } from '@bull-board/nestjs';
|
||||||
import { BullAdapter } from '@bull-board/api/bullAdapter';
|
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
|
||||||
import { BullModule } from '@nestjs/bull';
|
import { BullModule } from '@nestjs/bullmq';
|
||||||
import { SaleReceiptApplication } from './SaleReceiptApplication.service';
|
import { SaleReceiptApplication } from './SaleReceiptApplication.service';
|
||||||
import { CreateSaleReceipt } from './commands/CreateSaleReceipt.service';
|
import { CreateSaleReceipt } from './commands/CreateSaleReceipt.service';
|
||||||
import { EditSaleReceipt } from './commands/EditSaleReceipt.service';
|
import { EditSaleReceipt } from './commands/EditSaleReceipt.service';
|
||||||
@@ -66,7 +66,7 @@ import { ValidateBulkDeleteSaleReceiptsService } from './ValidateBulkDeleteSaleR
|
|||||||
BullModule.registerQueue({ name: SendSaleReceiptMailQueue }),
|
BullModule.registerQueue({ name: SendSaleReceiptMailQueue }),
|
||||||
BullBoardModule.forFeature({
|
BullBoardModule.forFeature({
|
||||||
name: SendSaleReceiptMailQueue,
|
name: SendSaleReceiptMailQueue,
|
||||||
adapter: BullAdapter,
|
adapter: BullMQAdapter,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { InjectQueue } from '@nestjs/bull';
|
import { InjectQueue } from '@nestjs/bullmq';
|
||||||
import { Queue } from 'bullmq';
|
import { Queue } from 'bullmq';
|
||||||
import {
|
import {
|
||||||
DEFAULT_RECEIPT_MAIL_CONTENT,
|
DEFAULT_RECEIPT_MAIL_CONTENT,
|
||||||
|
|||||||
@@ -1,30 +1,26 @@
|
|||||||
import { Process, Processor } from '@nestjs/bull';
|
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||||
import { Job } from 'bull';
|
import { Job } from 'bullmq';
|
||||||
import { Inject, Scope } from '@nestjs/common';
|
import { Scope } from '@nestjs/common';
|
||||||
import { JOB_REF } from '@nestjs/bull';
|
|
||||||
import { SendSaleReceiptMailQueue, SendSaleReceiptMailJob } from '../constants';
|
import { SendSaleReceiptMailQueue, SendSaleReceiptMailJob } from '../constants';
|
||||||
import { SaleReceiptMailNotification } from '../commands/SaleReceiptMailNotification';
|
import { SaleReceiptMailNotification } from '../commands/SaleReceiptMailNotification';
|
||||||
import { SaleReceiptSendMailPayload } from '../types/SaleReceipts.types';
|
|
||||||
import { ClsService, UseCls } from 'nestjs-cls';
|
import { ClsService, UseCls } from 'nestjs-cls';
|
||||||
|
|
||||||
@Processor({
|
@Processor({
|
||||||
name: SendSaleReceiptMailQueue,
|
name: SendSaleReceiptMailQueue,
|
||||||
scope: Scope.REQUEST,
|
scope: Scope.REQUEST,
|
||||||
})
|
})
|
||||||
export class SendSaleReceiptMailProcess {
|
export class SendSaleReceiptMailProcess extends WorkerHost {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly saleReceiptMailNotification: SaleReceiptMailNotification,
|
private readonly saleReceiptMailNotification: SaleReceiptMailNotification,
|
||||||
private readonly clsService: ClsService,
|
private readonly clsService: ClsService,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
@Inject(JOB_REF)
|
|
||||||
private readonly jobRef: Job<SaleReceiptSendMailPayload>,
|
|
||||||
) { }
|
|
||||||
|
|
||||||
@Process(SendSaleReceiptMailJob)
|
|
||||||
@UseCls()
|
@UseCls()
|
||||||
async handleSendMailJob() {
|
async process(job: Job) {
|
||||||
const { messageOpts, saleReceiptId, organizationId, userId } =
|
const { messageOpts, saleReceiptId, organizationId, userId } =
|
||||||
this.jobRef.data;
|
job.data;
|
||||||
|
|
||||||
this.clsService.set('organizationId', organizationId);
|
this.clsService.set('organizationId', organizationId);
|
||||||
this.clsService.set('userId', userId);
|
this.clsService.set('userId', userId);
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
import { Body, Controller, Get, Put } from '@nestjs/common';
|
import { Body, Controller, Get, Put, UseGuards } from '@nestjs/common';
|
||||||
import { SettingsApplicationService } from './SettingsApplication.service';
|
import { SettingsApplicationService } from './SettingsApplication.service';
|
||||||
import { ISettingsDTO } from './Settings.types';
|
import { ISettingsDTO, PreferencesAction } from './Settings.types';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
|
||||||
@Controller('settings')
|
@Controller('settings')
|
||||||
@ApiTags('Settings')
|
@ApiTags('Settings')
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class SettingsController {
|
export class SettingsController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly settingsApplicationService: SettingsApplicationService,
|
private readonly settingsApplicationService: SettingsApplicationService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Put()
|
@Put()
|
||||||
|
@RequirePermission(PreferencesAction.Mutate, AbilitySubject.Preferences)
|
||||||
@ApiOperation({ summary: 'Save the given settings.' })
|
@ApiOperation({ summary: 'Save the given settings.' })
|
||||||
async saveSettings(@Body() settingsDTO: ISettingsDTO) {
|
async saveSettings(@Body() settingsDTO: ISettingsDTO) {
|
||||||
return this.settingsApplicationService.saveSettings(settingsDTO);
|
return this.settingsApplicationService.saveSettings(settingsDTO);
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { Controller, Get, Post } from '@nestjs/common';
|
import { Controller, Get, HttpCode } from '@nestjs/common';
|
||||||
|
import { PublicRoute } from '@/modules/Auth/guards/jwt.guard';
|
||||||
|
|
||||||
@Controller('/system_db')
|
@Controller('system_db')
|
||||||
|
@PublicRoute()
|
||||||
export class SystemDatabaseController {
|
export class SystemDatabaseController {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
@Post()
|
|
||||||
@Get()
|
@Get()
|
||||||
ping(){
|
@HttpCode(200)
|
||||||
|
ping() {
|
||||||
|
return { status: 'ok' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
SystemKnexConnectionConfigure,
|
SystemKnexConnectionConfigure,
|
||||||
} from './SystemDB.constants';
|
} from './SystemDB.constants';
|
||||||
import { knexSnakeCaseMappers } from 'objection';
|
import { knexSnakeCaseMappers } from 'objection';
|
||||||
|
import { SystemDatabaseController } from './SystemDB.controller';
|
||||||
|
|
||||||
const providers = [
|
const providers = [
|
||||||
{
|
{
|
||||||
@@ -42,6 +43,7 @@ const providers = [
|
|||||||
|
|
||||||
@Global()
|
@Global()
|
||||||
@Module({
|
@Module({
|
||||||
|
controllers: [SystemDatabaseController],
|
||||||
providers: [...providers],
|
providers: [...providers],
|
||||||
exports: [...providers],
|
exports: [...providers],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -62,10 +62,11 @@ export class TaxRatesApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the tax rates list.
|
* Retrieves the tax rates list.
|
||||||
* @returns {Promise<ITaxRate[]>}
|
* @returns {Promise<{ data: ITaxRate[] }>}
|
||||||
*/
|
*/
|
||||||
public getTaxRates() {
|
public async getTaxRates() {
|
||||||
return this.getTaxRatesService.getTaxRates();
|
const taxRates = await this.getTaxRatesService.getTaxRates();
|
||||||
|
return { data: taxRates };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { TaxRatesApplication } from './TaxRate.application';
|
import { TaxRatesApplication } from './TaxRate.application';
|
||||||
import {
|
import {
|
||||||
@@ -18,15 +19,22 @@ import {
|
|||||||
import { CreateTaxRateDto, EditTaxRateDto } from './dtos/TaxRate.dto';
|
import { CreateTaxRateDto, EditTaxRateDto } from './dtos/TaxRate.dto';
|
||||||
import { TaxRateResponseDto } from './dtos/TaxRateResponse.dto';
|
import { TaxRateResponseDto } from './dtos/TaxRateResponse.dto';
|
||||||
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { TaxRateAction } from './TaxRates.types';
|
||||||
|
|
||||||
@Controller('tax-rates')
|
@Controller('tax-rates')
|
||||||
@ApiTags('Tax Rates')
|
@ApiTags('Tax Rates')
|
||||||
@ApiExtraModels(TaxRateResponseDto)
|
@ApiExtraModels(TaxRateResponseDto)
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class TaxRatesController {
|
export class TaxRatesController {
|
||||||
constructor(private readonly taxRatesApplication: TaxRatesApplication) { }
|
constructor(private readonly taxRatesApplication: TaxRatesApplication) { }
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(TaxRateAction.CREATE, AbilitySubject.TaxRate)
|
||||||
@ApiOperation({ summary: 'Create a new tax rate.' })
|
@ApiOperation({ summary: 'Create a new tax rate.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 201,
|
status: 201,
|
||||||
@@ -38,6 +46,7 @@ export class TaxRatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(TaxRateAction.EDIT, AbilitySubject.TaxRate)
|
||||||
@ApiOperation({ summary: 'Edit the given tax rate.' })
|
@ApiOperation({ summary: 'Edit the given tax rate.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -54,6 +63,7 @@ export class TaxRatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(TaxRateAction.DELETE, AbilitySubject.TaxRate)
|
||||||
@ApiOperation({ summary: 'Delete the given tax rate.' })
|
@ApiOperation({ summary: 'Delete the given tax rate.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -67,6 +77,7 @@ export class TaxRatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(TaxRateAction.VIEW, AbilitySubject.TaxRate)
|
||||||
@ApiOperation({ summary: 'Retrieves the tax rate details.' })
|
@ApiOperation({ summary: 'Retrieves the tax rate details.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -80,14 +91,20 @@ export class TaxRatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(TaxRateAction.VIEW, AbilitySubject.TaxRate)
|
||||||
@ApiOperation({ summary: 'Retrieves the tax rates.' })
|
@ApiOperation({ summary: 'Retrieves the tax rates.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
description: 'The tax rates have been successfully retrieved.',
|
description: 'The tax rates have been successfully retrieved.',
|
||||||
schema: {
|
schema: {
|
||||||
type: 'array',
|
type: 'object',
|
||||||
items: {
|
properties: {
|
||||||
$ref: getSchemaPath(TaxRateResponseDto),
|
data: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
$ref: getSchemaPath(TaxRateResponseDto),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -96,6 +113,7 @@ export class TaxRatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/activate')
|
@Put(':id/activate')
|
||||||
|
@RequirePermission(TaxRateAction.EDIT, AbilitySubject.TaxRate)
|
||||||
@ApiOperation({ summary: 'Activate the given tax rate.' })
|
@ApiOperation({ summary: 'Activate the given tax rate.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -109,6 +127,7 @@ export class TaxRatesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/inactivate')
|
@Put(':id/inactivate')
|
||||||
|
@RequirePermission(TaxRateAction.EDIT, AbilitySubject.TaxRate)
|
||||||
@ApiOperation({ summary: 'Inactivate the given tax rate.' })
|
@ApiOperation({ summary: 'Inactivate the given tax rate.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ToNumber } from '@/common/decorators/Validators';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Transform } from 'class-transformer';
|
import { Transform } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
@@ -30,6 +31,7 @@ export class CommandTaxRateDto {
|
|||||||
*/
|
*/
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
@ToNumber()
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'The rate of the tax rate.',
|
description: 'The rate of the tax rate.',
|
||||||
example: 10,
|
example: 10,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export class UsersController {
|
|||||||
/**
|
/**
|
||||||
* Edit details of the given user.
|
* Edit details of the given user.
|
||||||
*/
|
*/
|
||||||
@Post(':id')
|
@Put(':id')
|
||||||
@ApiOperation({ summary: 'Edit details of the given user.' })
|
@ApiOperation({ summary: 'Edit details of the given user.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { BullModule } from '@nestjs/bullmq';
|
||||||
|
import { BullBoardModule } from '@bull-board/nestjs';
|
||||||
|
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
|
||||||
import { ActivateUserService } from './commands/ActivateUser.service';
|
import { ActivateUserService } from './commands/ActivateUser.service';
|
||||||
import { DeleteUserService } from './commands/DeleteUser.service';
|
import { DeleteUserService } from './commands/DeleteUser.service';
|
||||||
import { EditUserService } from './commands/EditUser.service';
|
import { EditUserService } from './commands/EditUser.service';
|
||||||
@@ -18,11 +21,24 @@ import { AcceptInviteUserService } from './commands/AcceptInviteUser.service';
|
|||||||
import { InviteTenantUserService } from './commands/InviteUser.service';
|
import { InviteTenantUserService } from './commands/InviteUser.service';
|
||||||
import { UsersInviteController } from './UsersInvite.controller';
|
import { UsersInviteController } from './UsersInvite.controller';
|
||||||
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
||||||
|
import { SendInviteUserMailQueue } from './Users.constants';
|
||||||
|
import InviteSendMainNotificationSubscribe from './subscribers/InviteSendMailNotification.subscriber';
|
||||||
|
import { SendInviteUserMailProcessor } from './processors/SendInviteUserMail.processor';
|
||||||
|
import { SendInviteUsersMailMessage } from './commands/SendInviteUsersMailMessage.service';
|
||||||
|
import { MailModule } from '../Mail/Mail.module';
|
||||||
|
|
||||||
const models = [InjectSystemModel(UserInvite)];
|
const models = [InjectSystemModel(UserInvite)];
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TenancyModule],
|
imports: [
|
||||||
|
TenancyModule,
|
||||||
|
MailModule,
|
||||||
|
BullModule.registerQueue({ name: SendInviteUserMailQueue }),
|
||||||
|
BullBoardModule.forFeature({
|
||||||
|
name: SendInviteUserMailQueue,
|
||||||
|
adapter: BullMQAdapter,
|
||||||
|
}),
|
||||||
|
],
|
||||||
exports: [...models],
|
exports: [...models],
|
||||||
providers: [
|
providers: [
|
||||||
...models,
|
...models,
|
||||||
@@ -39,6 +55,9 @@ const models = [InjectSystemModel(UserInvite)];
|
|||||||
SyncTenantUserMutateSubscriber,
|
SyncTenantUserMutateSubscriber,
|
||||||
SyncSystemSendInviteSubscriber,
|
SyncSystemSendInviteSubscriber,
|
||||||
SyncTenantAcceptInviteSubscriber,
|
SyncTenantAcceptInviteSubscriber,
|
||||||
|
InviteSendMainNotificationSubscribe,
|
||||||
|
SendInviteUserMailProcessor,
|
||||||
|
SendInviteUsersMailMessage,
|
||||||
UsersApplication
|
UsersApplication
|
||||||
],
|
],
|
||||||
controllers: [UsersController, UsersInviteController],
|
controllers: [UsersController, UsersInviteController],
|
||||||
|
|||||||
@@ -32,10 +32,12 @@ export interface ITenantUserDeletedPayload {
|
|||||||
export interface IUserInvitedEventPayload {
|
export interface IUserInvitedEventPayload {
|
||||||
inviteToken: string;
|
inviteToken: string;
|
||||||
user: ModelObject<TenantUser>;
|
user: ModelObject<TenantUser>;
|
||||||
|
invitingUser: ModelObject<TenantUser>;
|
||||||
}
|
}
|
||||||
export interface IUserInviteTenantSyncedEventPayload {
|
export interface IUserInviteTenantSyncedEventPayload {
|
||||||
invite: ModelObject<UserInvite>;
|
invite: ModelObject<UserInvite>;
|
||||||
user: ModelObject<TenantUser>;
|
user: ModelObject<TenantUser>;
|
||||||
|
invitingUser: ModelObject<TenantUser>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserInviteResendEventPayload {
|
export interface IUserInviteResendEventPayload {
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ export class InactivateUserService {
|
|||||||
// Throw serivce error if the user is already inactivated.
|
// Throw serivce error if the user is already inactivated.
|
||||||
this.throwErrorIfUserInactive(tenantUser);
|
this.throwErrorIfUserInactive(tenantUser);
|
||||||
|
|
||||||
// Marks the tenant user as active.
|
// Marks the tenant user as inactive.
|
||||||
await this.tenantUserModel()
|
await this.tenantUserModel()
|
||||||
.query()
|
.query()
|
||||||
.findById(userId)
|
.findById(userId)
|
||||||
.update({ active: true });
|
.update({ active: false });
|
||||||
|
|
||||||
// Triggers `onTenantUserActivated` event.
|
// Triggers `onTenantUserActivated` event.
|
||||||
await this.eventEmitter.emitAsync(events.tenantUser.onInactivated, {
|
await this.eventEmitter.emitAsync(events.tenantUser.onInactivated, {
|
||||||
|
|||||||
@@ -15,11 +15,13 @@ import { events } from '@/common/events/events';
|
|||||||
import { Role } from '@/modules/Roles/models/Role.model';
|
import { Role } from '@/modules/Roles/models/Role.model';
|
||||||
import { ModelObject } from 'objection';
|
import { ModelObject } from 'objection';
|
||||||
import { SendInviteUserDto } from '../dtos/InviteUser.dto';
|
import { SendInviteUserDto } from '../dtos/InviteUser.dto';
|
||||||
|
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InviteTenantUserService {
|
export class InviteTenantUserService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
|
private readonly tenancyContext: TenancyContext,
|
||||||
|
|
||||||
@Inject(TenantUser.name)
|
@Inject(TenantUser.name)
|
||||||
private readonly tenantUserModel: TenantModelProxy<typeof TenantUser>,
|
private readonly tenantUserModel: TenantModelProxy<typeof TenantUser>,
|
||||||
@@ -53,10 +55,18 @@ export class InviteTenantUserService {
|
|||||||
active: true,
|
active: true,
|
||||||
invitedAt: new Date(),
|
invitedAt: new Date(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Retrieves the authorized user (inviting user).
|
||||||
|
const authorizedUser = await this.tenancyContext.getSystemUser();
|
||||||
|
const invitingUser = await this.tenantUserModel()
|
||||||
|
.query()
|
||||||
|
.findOne({ systemUserId: authorizedUser.id });
|
||||||
|
|
||||||
// Triggers `onUserSendInvite` event.
|
// Triggers `onUserSendInvite` event.
|
||||||
await this.eventEmitter.emitAsync(events.inviteUser.sendInvite, {
|
await this.eventEmitter.emitAsync(events.inviteUser.sendInvite, {
|
||||||
inviteToken,
|
inviteToken,
|
||||||
user,
|
user,
|
||||||
|
invitingUser,
|
||||||
} as IUserInvitedEventPayload);
|
} as IUserInvitedEventPayload);
|
||||||
|
|
||||||
return { invitedUser: user };
|
return { invitedUser: user };
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export class SendInviteUsersMailMessage {
|
|||||||
invite: ModelObject<UserInvite>,
|
invite: ModelObject<UserInvite>,
|
||||||
) {
|
) {
|
||||||
const tenant = await this.tenancyContext.getTenant(true);
|
const tenant = await this.tenancyContext.getTenant(true);
|
||||||
const root = path.join(global.__views_dir, '/images/bigcapital.png');
|
const root = path.join(global.__images_dirname, '/bigcapital.png');
|
||||||
const baseURL = this.configService.get('baseURL');
|
const baseURL = this.configService.get('baseURL');
|
||||||
|
|
||||||
const mail = new Mail()
|
const mail = new Mail()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { JOB_REF, Process, Processor } from '@nestjs/bull';
|
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||||
import { Job } from 'bull';
|
import { Job } from 'bullmq';
|
||||||
import { Inject, Scope } from '@nestjs/common';
|
import { Scope } from '@nestjs/common';
|
||||||
import { REQUEST } from '@nestjs/core';
|
|
||||||
import { ClsService, UseCls } from 'nestjs-cls';
|
import { ClsService, UseCls } from 'nestjs-cls';
|
||||||
import {
|
import {
|
||||||
SendInviteUserMailJob,
|
SendInviteUserMailJob,
|
||||||
@@ -14,19 +13,17 @@ import { SendInviteUsersMailMessage } from '../commands/SendInviteUsersMailMessa
|
|||||||
name: SendInviteUserMailQueue,
|
name: SendInviteUserMailQueue,
|
||||||
scope: Scope.REQUEST,
|
scope: Scope.REQUEST,
|
||||||
})
|
})
|
||||||
export class SendInviteUserMailProcessor {
|
export class SendInviteUserMailProcessor extends WorkerHost {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly sendInviteUsersMailService: SendInviteUsersMailMessage,
|
private readonly sendInviteUsersMailService: SendInviteUsersMailMessage,
|
||||||
@Inject(REQUEST) private readonly request: Request,
|
|
||||||
@Inject(JOB_REF)
|
|
||||||
private readonly jobRef: Job<SendInviteUserMailJobPayload>,
|
|
||||||
private readonly clsService: ClsService,
|
private readonly clsService: ClsService,
|
||||||
) { }
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
@Process(SendInviteUserMailJob)
|
|
||||||
@UseCls()
|
@UseCls()
|
||||||
async handleSendInviteMail() {
|
async process(job: Job<SendInviteUserMailJobPayload>) {
|
||||||
const { fromUser, invite, organizationId, userId } = this.jobRef.data;
|
const { fromUser, invite, organizationId, userId } = job.data;
|
||||||
|
|
||||||
this.clsService.set('organizationId', organizationId);
|
this.clsService.set('organizationId', organizationId);
|
||||||
this.clsService.set('userId', userId);
|
this.clsService.set('userId', userId);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectQueue } from '@nestjs/bull';
|
import { InjectQueue } from '@nestjs/bullmq';
|
||||||
import { Queue } from 'bull';
|
import { Queue } from 'bullmq';
|
||||||
import { events } from '@/common/events/events';
|
import { events } from '@/common/events/events';
|
||||||
import { OnEvent } from '@nestjs/event-emitter';
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
import {
|
import {
|
||||||
@@ -29,6 +29,7 @@ export default class InviteSendMainNotificationSubscribe {
|
|||||||
async sendMailNotification({
|
async sendMailNotification({
|
||||||
invite,
|
invite,
|
||||||
user,
|
user,
|
||||||
|
invitingUser,
|
||||||
}: IUserInviteTenantSyncedEventPayload) {
|
}: IUserInviteTenantSyncedEventPayload) {
|
||||||
const tenant = await this.tenancyContext.getTenant();
|
const tenant = await this.tenancyContext.getTenant();
|
||||||
const authedUser = await this.tenancyContext.getSystemUser();
|
const authedUser = await this.tenancyContext.getSystemUser();
|
||||||
@@ -37,7 +38,7 @@ export default class InviteSendMainNotificationSubscribe {
|
|||||||
const userId = authedUser.id;
|
const userId = authedUser.id;
|
||||||
|
|
||||||
this.sendInviteMailQueue.add(SendInviteUserMailJob, {
|
this.sendInviteMailQueue.add(SendInviteUserMailJob, {
|
||||||
fromUser: user,
|
fromUser: invitingUser,
|
||||||
invite,
|
invite,
|
||||||
userId,
|
userId,
|
||||||
organizationId,
|
organizationId,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class SyncSystemSendInviteSubscriber {
|
|||||||
* @param {IUserInvitedEventPayload} payload -
|
* @param {IUserInvitedEventPayload} payload -
|
||||||
*/
|
*/
|
||||||
@OnEvent(events.inviteUser.sendInvite)
|
@OnEvent(events.inviteUser.sendInvite)
|
||||||
async syncSendInviteSystem({ inviteToken, user }: IUserInvitedEventPayload) {
|
async syncSendInviteSystem({ inviteToken, user, invitingUser }: IUserInvitedEventPayload) {
|
||||||
const authorizedUser = await this.tenancyContext.getSystemUser();
|
const authorizedUser = await this.tenancyContext.getSystemUser();
|
||||||
const tenantId = authorizedUser.tenantId;
|
const tenantId = authorizedUser.tenantId;
|
||||||
|
|
||||||
@@ -63,6 +63,7 @@ export class SyncSystemSendInviteSubscriber {
|
|||||||
{
|
{
|
||||||
invite,
|
invite,
|
||||||
user,
|
user,
|
||||||
|
invitingUser,
|
||||||
} as IUserInviteTenantSyncedEventPayload,
|
} as IUserInviteTenantSyncedEventPayload,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { VendorCreditsApplicationService } from './VendorCreditsApplication.service';
|
import { VendorCreditsApplicationService } from './VendorCreditsApplication.service';
|
||||||
import { IVendorCreditsQueryDTO } from './types/VendorCredit.types';
|
import { IVendorCreditsQueryDTO } from './types/VendorCredit.types';
|
||||||
@@ -26,17 +27,24 @@ import {
|
|||||||
BulkDeleteDto,
|
BulkDeleteDto,
|
||||||
ValidateBulkDeleteResponseDto,
|
ValidateBulkDeleteResponseDto,
|
||||||
} from '@/common/dtos/BulkDelete.dto';
|
} from '@/common/dtos/BulkDelete.dto';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { VendorCreditAction } from './types/VendorCredit.types';
|
||||||
|
|
||||||
@Controller('vendor-credits')
|
@Controller('vendor-credits')
|
||||||
@ApiTags('Vendor Credits')
|
@ApiTags('Vendor Credits')
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
@ApiExtraModels(ValidateBulkDeleteResponseDto)
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class VendorCreditsController {
|
export class VendorCreditsController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly vendorCreditsApplication: VendorCreditsApplicationService,
|
private readonly vendorCreditsApplication: VendorCreditsApplicationService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
|
@RequirePermission(VendorCreditAction.Delete, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Validates which vendor credits can be deleted and returns the results.',
|
'Validates which vendor credits can be deleted and returns the results.',
|
||||||
@@ -58,6 +66,7 @@ export class VendorCreditsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
|
@RequirePermission(VendorCreditAction.Delete, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({ summary: 'Deletes multiple vendor credits.' })
|
@ApiOperation({ summary: 'Deletes multiple vendor credits.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -73,24 +82,28 @@ export class VendorCreditsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(VendorCreditAction.Create, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({ summary: 'Create a new vendor credit.' })
|
@ApiOperation({ summary: 'Create a new vendor credit.' })
|
||||||
async createVendorCredit(@Body() dto: CreateVendorCreditDto) {
|
async createVendorCredit(@Body() dto: CreateVendorCreditDto) {
|
||||||
return this.vendorCreditsApplication.createVendorCredit(dto);
|
return this.vendorCreditsApplication.createVendorCredit(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/open')
|
@Put(':id/open')
|
||||||
|
@RequirePermission(VendorCreditAction.Edit, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({ summary: 'Open the given vendor credit.' })
|
@ApiOperation({ summary: 'Open the given vendor credit.' })
|
||||||
async openVendorCredit(@Param('id') vendorCreditId: number) {
|
async openVendorCredit(@Param('id') vendorCreditId: number) {
|
||||||
return this.vendorCreditsApplication.openVendorCredit(vendorCreditId);
|
return this.vendorCreditsApplication.openVendorCredit(vendorCreditId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(VendorCreditAction.View, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({ summary: 'Retrieves the vendor credits.' })
|
@ApiOperation({ summary: 'Retrieves the vendor credits.' })
|
||||||
async getVendorCredits(@Query() filterDTO: IVendorCreditsQueryDTO) {
|
async getVendorCredits(@Query() filterDTO: IVendorCreditsQueryDTO) {
|
||||||
return this.vendorCreditsApplication.getVendorCredits(filterDTO);
|
return this.vendorCreditsApplication.getVendorCredits(filterDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(VendorCreditAction.Edit, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({ summary: 'Edit the given vendor credit.' })
|
@ApiOperation({ summary: 'Edit the given vendor credit.' })
|
||||||
async editVendorCredit(
|
async editVendorCredit(
|
||||||
@Param('id') vendorCreditId: number,
|
@Param('id') vendorCreditId: number,
|
||||||
@@ -100,12 +113,14 @@ export class VendorCreditsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(VendorCreditAction.Delete, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({ summary: 'Delete the given vendor credit.' })
|
@ApiOperation({ summary: 'Delete the given vendor credit.' })
|
||||||
async deleteVendorCredit(@Param('id') vendorCreditId: number) {
|
async deleteVendorCredit(@Param('id') vendorCreditId: number) {
|
||||||
return this.vendorCreditsApplication.deleteVendorCredit(vendorCreditId);
|
return this.vendorCreditsApplication.deleteVendorCredit(vendorCreditId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(VendorCreditAction.View, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({ summary: 'Retrieves the vendor credit details.' })
|
@ApiOperation({ summary: 'Retrieves the vendor credit details.' })
|
||||||
async getVendorCredit(@Param('id') vendorCreditId: number) {
|
async getVendorCredit(@Param('id') vendorCreditId: number) {
|
||||||
return this.vendorCreditsApplication.getVendorCredit(vendorCreditId);
|
return this.vendorCreditsApplication.getVendorCredit(vendorCreditId);
|
||||||
|
|||||||
@@ -1,16 +1,33 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { VendorCreditApplyBillsApplicationService } from './VendorCreditApplyBillsApplication.service';
|
import { VendorCreditApplyBillsApplicationService } from './VendorCreditApplyBillsApplication.service';
|
||||||
import { IVendorCreditApplyToInvoicesDTO } from './types/VendorCreditApplyBills.types';
|
import { IVendorCreditApplyToInvoicesDTO } from './types/VendorCreditApplyBills.types';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { VendorCreditAction } from '../VendorCredit/types/VendorCredit.types';
|
||||||
|
|
||||||
@Controller('vendor-credits')
|
@Controller('vendor-credits')
|
||||||
@ApiTags('Vendor Credits Apply Bills')
|
@ApiTags('Vendor Credits Apply Bills')
|
||||||
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class VendorCreditApplyBillsController {
|
export class VendorCreditApplyBillsController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly vendorCreditApplyBillsApplication: VendorCreditApplyBillsApplicationService,
|
private readonly vendorCreditApplyBillsApplication: VendorCreditApplyBillsApplicationService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get(':vendorCreditId/bills-to-apply')
|
@Get(':vendorCreditId/bills-to-apply')
|
||||||
|
@RequirePermission(VendorCreditAction.View, AbilitySubject.VendorCredit)
|
||||||
async getVendorCreditToApplyBills(
|
async getVendorCreditToApplyBills(
|
||||||
@Param('vendorCreditId') vendorCreditId: number,
|
@Param('vendorCreditId') vendorCreditId: number,
|
||||||
) {
|
) {
|
||||||
@@ -20,6 +37,7 @@ export class VendorCreditApplyBillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post(':vendorCreditId/apply-to-bills')
|
@Post(':vendorCreditId/apply-to-bills')
|
||||||
|
@RequirePermission(VendorCreditAction.Edit, AbilitySubject.VendorCredit)
|
||||||
async applyVendorCreditToBills(
|
async applyVendorCreditToBills(
|
||||||
@Param('vendorCreditId') vendorCreditId: number,
|
@Param('vendorCreditId') vendorCreditId: number,
|
||||||
@Body() applyCreditToBillsDTO: IVendorCreditApplyToInvoicesDTO,
|
@Body() applyCreditToBillsDTO: IVendorCreditApplyToInvoicesDTO,
|
||||||
@@ -31,6 +49,7 @@ export class VendorCreditApplyBillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete('applied-bills/:vendorCreditAppliedBillId')
|
@Delete('applied-bills/:vendorCreditAppliedBillId')
|
||||||
|
@RequirePermission(VendorCreditAction.Edit, AbilitySubject.VendorCredit)
|
||||||
async deleteAppliedBillToVendorCredit(
|
async deleteAppliedBillToVendorCredit(
|
||||||
@Param('vendorCreditAppliedBillId') vendorCreditAppliedBillId: number,
|
@Param('vendorCreditAppliedBillId') vendorCreditAppliedBillId: number,
|
||||||
) {
|
) {
|
||||||
@@ -40,6 +59,7 @@ export class VendorCreditApplyBillsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get(':vendorCreditId/applied-bills')
|
@Get(':vendorCreditId/applied-bills')
|
||||||
|
@RequirePermission(VendorCreditAction.View, AbilitySubject.VendorCredit)
|
||||||
async getAppliedBillsToVendorCredit(
|
async getAppliedBillsToVendorCredit(
|
||||||
@Param('vendorCreditId') vendorCreditId: number,
|
@Param('vendorCreditId') vendorCreditId: number,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,11 +1,27 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { VendorCreditsRefundApplication } from './VendorCreditsRefund.application';
|
import { VendorCreditsRefundApplication } from './VendorCreditsRefund.application';
|
||||||
import { RefundVendorCredit } from './models/RefundVendorCredit';
|
import { RefundVendorCredit } from './models/RefundVendorCredit';
|
||||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
import { RefundVendorCreditDto } from './dtos/RefundVendorCredit.dto';
|
import { RefundVendorCreditDto } from './dtos/RefundVendorCredit.dto';
|
||||||
|
import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { VendorCreditAction } from '../VendorCredit/types/VendorCredit.types';
|
||||||
|
|
||||||
@Controller('vendor-credits')
|
@Controller('vendor-credits')
|
||||||
@ApiTags('Vendor Credits Refunds')
|
@ApiTags('Vendor Credits Refunds')
|
||||||
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class VendorCreditsRefundController {
|
export class VendorCreditsRefundController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly vendorCreditsRefundApplication: VendorCreditsRefundApplication,
|
private readonly vendorCreditsRefundApplication: VendorCreditsRefundApplication,
|
||||||
@@ -17,6 +33,7 @@ export class VendorCreditsRefundController {
|
|||||||
* @returns {Promise<IRefundVendorCreditPOJO[]>}
|
* @returns {Promise<IRefundVendorCreditPOJO[]>}
|
||||||
*/
|
*/
|
||||||
@Get(':vendorCreditId/refund')
|
@Get(':vendorCreditId/refund')
|
||||||
|
@RequirePermission(VendorCreditAction.View, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({ summary: 'Retrieve the vendor credit refunds graph.' })
|
@ApiOperation({ summary: 'Retrieve the vendor credit refunds graph.' })
|
||||||
public getVendorCreditRefunds(
|
public getVendorCreditRefunds(
|
||||||
@Param('vendorCreditId') vendorCreditId: string,
|
@Param('vendorCreditId') vendorCreditId: string,
|
||||||
@@ -33,6 +50,7 @@ export class VendorCreditsRefundController {
|
|||||||
* @returns {Promise<RefundVendorCredit>}
|
* @returns {Promise<RefundVendorCredit>}
|
||||||
*/
|
*/
|
||||||
@Post(':vendorCreditId/refund')
|
@Post(':vendorCreditId/refund')
|
||||||
|
@RequirePermission(VendorCreditAction.Refund, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({ summary: 'Create a refund for the given vendor credit.' })
|
@ApiOperation({ summary: 'Create a refund for the given vendor credit.' })
|
||||||
public async createRefundVendorCredit(
|
public async createRefundVendorCredit(
|
||||||
@Param('vendorCreditId') vendorCreditId: string,
|
@Param('vendorCreditId') vendorCreditId: string,
|
||||||
@@ -50,6 +68,7 @@ export class VendorCreditsRefundController {
|
|||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
@Delete('refunds/:refundCreditId')
|
@Delete('refunds/:refundCreditId')
|
||||||
|
@RequirePermission(VendorCreditAction.Refund, AbilitySubject.VendorCredit)
|
||||||
@ApiOperation({ summary: 'Delete a refund for the given vendor credit.' })
|
@ApiOperation({ summary: 'Delete a refund for the given vendor credit.' })
|
||||||
public async deleteRefundVendorCredit(
|
public async deleteRefundVendorCredit(
|
||||||
@Param('refundCreditId') refundCreditId: string,
|
@Param('refundCreditId') refundCreditId: string,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { VendorsApplication } from './VendorsApplication.service';
|
import { VendorsApplication } from './VendorsApplication.service';
|
||||||
import { VendorOpeningBalanceEditDto } from './dtos/VendorOpeningBalanceEdit.dto';
|
import { VendorOpeningBalanceEditDto } from './dtos/VendorOpeningBalanceEdit.dto';
|
||||||
@@ -24,44 +25,56 @@ import {
|
|||||||
BulkDeleteVendorsDto,
|
BulkDeleteVendorsDto,
|
||||||
ValidateBulkDeleteVendorsResponseDto,
|
ValidateBulkDeleteVendorsResponseDto,
|
||||||
} from './dtos/BulkDeleteVendors.dto';
|
} from './dtos/BulkDeleteVendors.dto';
|
||||||
|
import { RequirePermission } from '@/modules/Roles/RequirePermission.decorator';
|
||||||
|
import { PermissionGuard } from '@/modules/Roles/Permission.guard';
|
||||||
|
import { AuthorizationGuard } from '@/modules/Roles/Authorization.guard';
|
||||||
|
import { AbilitySubject } from '@/modules/Roles/Roles.types';
|
||||||
|
import { VendorAction } from '../Customers/types/Customers.types';
|
||||||
|
|
||||||
@Controller('vendors')
|
@Controller('vendors')
|
||||||
@ApiTags('Vendors')
|
@ApiTags('Vendors')
|
||||||
@ApiCommonHeaders()
|
@ApiCommonHeaders()
|
||||||
|
@UseGuards(AuthorizationGuard, PermissionGuard)
|
||||||
export class VendorsController {
|
export class VendorsController {
|
||||||
constructor(private vendorsApplication: VendorsApplication) {}
|
constructor(private vendorsApplication: VendorsApplication) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@RequirePermission(VendorAction.View, AbilitySubject.Vendor)
|
||||||
@ApiOperation({ summary: 'Retrieves the vendors.' })
|
@ApiOperation({ summary: 'Retrieves the vendors.' })
|
||||||
getVendors(@Query() filterDTO: GetVendorsQueryDto) {
|
getVendors(@Query() filterDTO: GetVendorsQueryDto) {
|
||||||
return this.vendorsApplication.getVendors(filterDTO);
|
return this.vendorsApplication.getVendors(filterDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@RequirePermission(VendorAction.View, AbilitySubject.Vendor)
|
||||||
@ApiOperation({ summary: 'Retrieves the vendor details.' })
|
@ApiOperation({ summary: 'Retrieves the vendor details.' })
|
||||||
getVendor(@Param('id') vendorId: number) {
|
getVendor(@Param('id') vendorId: number) {
|
||||||
return this.vendorsApplication.getVendor(vendorId);
|
return this.vendorsApplication.getVendor(vendorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@RequirePermission(VendorAction.Create, AbilitySubject.Vendor)
|
||||||
@ApiOperation({ summary: 'Create a new vendor.' })
|
@ApiOperation({ summary: 'Create a new vendor.' })
|
||||||
createVendor(@Body() vendorDTO: CreateVendorDto) {
|
createVendor(@Body() vendorDTO: CreateVendorDto) {
|
||||||
return this.vendorsApplication.createVendor(vendorDTO);
|
return this.vendorsApplication.createVendor(vendorDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
|
@RequirePermission(VendorAction.Edit, AbilitySubject.Vendor)
|
||||||
@ApiOperation({ summary: 'Edit the given vendor.' })
|
@ApiOperation({ summary: 'Edit the given vendor.' })
|
||||||
editVendor(@Param('id') vendorId: number, @Body() vendorDTO: EditVendorDto) {
|
editVendor(@Param('id') vendorId: number, @Body() vendorDTO: EditVendorDto) {
|
||||||
return this.vendorsApplication.editVendor(vendorId, vendorDTO);
|
return this.vendorsApplication.editVendor(vendorId, vendorDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@RequirePermission(VendorAction.Delete, AbilitySubject.Vendor)
|
||||||
@ApiOperation({ summary: 'Delete the given vendor.' })
|
@ApiOperation({ summary: 'Delete the given vendor.' })
|
||||||
deleteVendor(@Param('id') vendorId: number) {
|
deleteVendor(@Param('id') vendorId: number) {
|
||||||
return this.vendorsApplication.deleteVendor(vendorId);
|
return this.vendorsApplication.deleteVendor(vendorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id/opening-balance')
|
@Put(':id/opening-balance')
|
||||||
|
@RequirePermission(VendorAction.Edit, AbilitySubject.Vendor)
|
||||||
@ApiOperation({ summary: 'Edit the given vendor opening balance.' })
|
@ApiOperation({ summary: 'Edit the given vendor opening balance.' })
|
||||||
editOpeningBalance(
|
editOpeningBalance(
|
||||||
@Param('id') vendorId: number,
|
@Param('id') vendorId: number,
|
||||||
@@ -74,6 +87,7 @@ export class VendorsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('validate-bulk-delete')
|
@Post('validate-bulk-delete')
|
||||||
|
@RequirePermission(VendorAction.Delete, AbilitySubject.Vendor)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary:
|
summary:
|
||||||
'Validates which vendors can be deleted and returns counts of deletable and non-deletable vendors.',
|
'Validates which vendors can be deleted and returns counts of deletable and non-deletable vendors.',
|
||||||
@@ -93,6 +107,7 @@ export class VendorsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('bulk-delete')
|
@Post('bulk-delete')
|
||||||
|
@RequirePermission(VendorAction.Delete, AbilitySubject.Vendor)
|
||||||
@ApiOperation({ summary: 'Deletes multiple vendors in bulk.' })
|
@ApiOperation({ summary: 'Deletes multiple vendors in bulk.' })
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -10,12 +10,17 @@ const TextStatusRoot = styled.span`
|
|||||||
${(props) =>
|
${(props) =>
|
||||||
props.intent === 'warning' &&
|
props.intent === 'warning' &&
|
||||||
`
|
`
|
||||||
color: #ec5b0a;`}
|
color: #c87619;`}
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.intent === 'danger' &&
|
||||||
|
`
|
||||||
|
color: #f17377;`}
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.intent === 'success' &&
|
props.intent === 'success' &&
|
||||||
`
|
`
|
||||||
color: #2ba01d;`}
|
color: #238551;`}
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.intent === 'none' &&
|
props.intent === 'none' &&
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
// @ts-nocheck
|
import React, { KeyboardEvent, ReactNode } from 'react';
|
||||||
import React from 'react';
|
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import {
|
import {
|
||||||
Overlay,
|
Overlay,
|
||||||
@@ -10,11 +8,14 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
Spinner,
|
Spinner,
|
||||||
Intent,
|
Intent,
|
||||||
|
OverlayProps,
|
||||||
|
Button,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { QueryList } from '@blueprintjs/select';
|
import { QueryList, ItemRenderer } from '@blueprintjs/select';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { x } from '@xstyled/emotion';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
import { Icon, If, ListSelect, FormattedMessage as T } from '@/components';
|
import { Icon, If, FormattedMessage as T } from '@/components';
|
||||||
|
import { Select } from '@blueprintjs-formik/select';
|
||||||
import {
|
import {
|
||||||
UniversalSearchProvider,
|
UniversalSearchProvider,
|
||||||
useUniversalSearchContext,
|
useUniversalSearchContext,
|
||||||
@@ -22,59 +23,297 @@ import {
|
|||||||
import { filterItemsByResourceType } from './utils';
|
import { filterItemsByResourceType } from './utils';
|
||||||
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
||||||
|
|
||||||
|
// Resource type from RESOURCES_TYPES constant
|
||||||
|
type ResourceType = string;
|
||||||
|
|
||||||
|
// Search type option item
|
||||||
|
interface SearchTypeOption {
|
||||||
|
key: ResourceType;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Universal search item
|
||||||
|
interface UniversalSearchItem {
|
||||||
|
id: number | string;
|
||||||
|
_type: ResourceType;
|
||||||
|
text: string;
|
||||||
|
subText?: string;
|
||||||
|
label?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSS styles for complex selectors
|
||||||
|
const overlayStyles = css`
|
||||||
|
.bp4-overlay-appear,
|
||||||
|
.bp4-overlay-enter {
|
||||||
|
filter: blur(20px);
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
.bp4-overlay-appear-active,
|
||||||
|
.bp4-overlay-enter-active {
|
||||||
|
filter: blur(0);
|
||||||
|
opacity: 1;
|
||||||
|
transition:
|
||||||
|
filter 0.2s cubic-bezier(0.4, 1, 0.75, 0.9),
|
||||||
|
opacity 0.2s cubic-bezier(0.4, 1, 0.75, 0.9);
|
||||||
|
}
|
||||||
|
.bp4-overlay-exit {
|
||||||
|
filter: blur(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.bp4-overlay-exit-active {
|
||||||
|
filter: blur(20px);
|
||||||
|
opacity: 0.2;
|
||||||
|
transition:
|
||||||
|
filter 0.2s cubic-bezier(0.4, 1, 0.75, 0.9),
|
||||||
|
opacity 0.2s cubic-bezier(0.4, 1, 0.75, 0.9);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const containerStyles = css`
|
||||||
|
position: fixed;
|
||||||
|
filter: blur(0);
|
||||||
|
opacity: 1;
|
||||||
|
background-color: var(--color-universal-search-background);
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px rgba(16, 22, 26, 0.1),
|
||||||
|
0 4px 8px rgba(16, 22, 26, 0.2),
|
||||||
|
0 18px 46px 6px rgba(16, 22, 26, 0.2);
|
||||||
|
left: calc(50% - 250px);
|
||||||
|
top: 20vh;
|
||||||
|
width: 500px;
|
||||||
|
z-index: 20;
|
||||||
|
|
||||||
|
.bp4-input-group {
|
||||||
|
.bp4-icon {
|
||||||
|
margin: 16px;
|
||||||
|
color: var(--color-universal-search-icon);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
stroke: currentColor;
|
||||||
|
fill: none;
|
||||||
|
fill-rule: evenodd;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 2;
|
||||||
|
--text-opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp4-input-group .bp4-input {
|
||||||
|
border: 0;
|
||||||
|
box-shadow: 0 0 0 0;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.bp4-input-group.bp4-large .bp4-input:not(:first-child) {
|
||||||
|
padding-left: 50px !important;
|
||||||
|
}
|
||||||
|
.bp4-input-group.bp4-large .bp4-input:not(:last-child) {
|
||||||
|
padding-right: 130px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp4-menu {
|
||||||
|
border-top: 1px solid var(--color-universal-search-menu-border);
|
||||||
|
max-height: calc(60vh - 20px);
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.bp4-menu-item {
|
||||||
|
.bp4-text-muted {
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
.bp4-icon {
|
||||||
|
color: var(--bp4-gray-600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.bp4-intent-primary {
|
||||||
|
&.bp4-active {
|
||||||
|
background-color: var(--bp4-blue-100);
|
||||||
|
color: var(--bp4-dark-gray-800);
|
||||||
|
|
||||||
|
.bp4-menu-item-label {
|
||||||
|
color: var(--bp4-gray-600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
flex-direction: row;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp4-input-action {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const inputRightElementsStyles = css`
|
||||||
|
display: flex;
|
||||||
|
margin: 10px;
|
||||||
|
|
||||||
|
.bp4-spinner {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const footerStyles = css`
|
||||||
|
padding: 12px 12px;
|
||||||
|
border-top: 1px solid var(--color-universal-search-footer-divider);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const actionBaseStyles = css`
|
||||||
|
&:not(:first-of-type) {
|
||||||
|
margin-left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp4-tag {
|
||||||
|
background: var(--color-universal-search-tag-background);
|
||||||
|
color: var(--color-universal-search-tag-text);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const actionArrowsStyles = css`
|
||||||
|
&:not(:first-of-type) {
|
||||||
|
margin-left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp4-tag {
|
||||||
|
background: var(--color-universal-search-tag-background);
|
||||||
|
color: var(--color-universal-search-tag-text);
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 16px;
|
||||||
|
margin-left: 4px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: var(--color-universal-search-tag-text);
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// UniversalSearchInputRightElements props
|
||||||
|
interface UniversalSearchInputRightElementsProps {
|
||||||
|
/** Callback when search type changes */
|
||||||
|
onSearchTypeChange?: (option: SearchTypeOption) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Universal search input action.
|
* Universal search input action.
|
||||||
*/
|
*/
|
||||||
function UniversalSearchInputRightElements({ onSearchTypeChange }) {
|
function UniversalSearchInputRightElements({
|
||||||
const { isLoading, searchType, defaultSearchResource, searchTypeOptions } =
|
onSearchTypeChange,
|
||||||
|
}: UniversalSearchInputRightElementsProps) {
|
||||||
|
const { isLoading, searchType, searchTypeOptions } =
|
||||||
useUniversalSearchContext();
|
useUniversalSearchContext();
|
||||||
|
|
||||||
|
// Find the currently selected item object.
|
||||||
|
const selectedItem = searchTypeOptions.find(
|
||||||
|
(item) => item.key === searchType,
|
||||||
|
);
|
||||||
|
|
||||||
// Handle search type option change.
|
// Handle search type option change.
|
||||||
const handleSearchTypeChange = (option) => {
|
const handleSearchTypeChange = (option: SearchTypeOption) => {
|
||||||
onSearchTypeChange && onSearchTypeChange(option);
|
onSearchTypeChange?.(option);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Item renderer for the select dropdown.
|
||||||
|
const itemRenderer: ItemRenderer<SearchTypeOption> = (
|
||||||
|
item,
|
||||||
|
{ handleClick },
|
||||||
|
) => {
|
||||||
|
return <MenuItem text={item.label} key={item.key} onClick={handleClick} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={CLASSES.UNIVERSAL_SEARCH_INPUT_RIGHT_ELEMENTS}>
|
<x.div display="flex" m="10px" className={inputRightElementsStyles}>
|
||||||
<If condition={isLoading}>
|
<If condition={isLoading}>
|
||||||
<Spinner tagName="div" intent={Intent.NONE} size={18} value={null} />
|
<Spinner tagName="div" intent={Intent.NONE} size={18} />
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
<ListSelect
|
<Select<SearchTypeOption>
|
||||||
items={searchTypeOptions}
|
items={searchTypeOptions}
|
||||||
|
itemRenderer={itemRenderer}
|
||||||
onItemSelect={handleSearchTypeChange}
|
onItemSelect={handleSearchTypeChange}
|
||||||
|
selectedValue={selectedItem?.key}
|
||||||
|
valueAccessor={'key'}
|
||||||
|
labelAccessor={'label'}
|
||||||
filterable={false}
|
filterable={false}
|
||||||
initialSelectedItem={defaultSearchResource}
|
|
||||||
selectedItem={searchType}
|
|
||||||
selectedItemProp={'key'}
|
|
||||||
textProp={'label'}
|
|
||||||
// defaultText={intl.get('type')}
|
|
||||||
popoverProps={{
|
popoverProps={{
|
||||||
minimal: true,
|
minimal: true,
|
||||||
captureDismiss: true,
|
captureDismiss: true,
|
||||||
className: CLASSES.UNIVERSAL_SEARCH_TYPE_SELECT_OVERLAY,
|
|
||||||
}}
|
|
||||||
buttonProps={{
|
|
||||||
minimal: true,
|
|
||||||
className: CLASSES.UNIVERSAL_SEARCH_TYPE_SELECT_BTN,
|
|
||||||
}}
|
}}
|
||||||
|
input={({ activeItem }) => (
|
||||||
|
<Button minimal={true} text={activeItem?.label} />
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</x.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryList renderer props
|
||||||
|
interface QueryListRendererProps {
|
||||||
|
/** Current query string */
|
||||||
|
query: string;
|
||||||
|
/** Callback when query changes */
|
||||||
|
handleQueryChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
/** Item list element */
|
||||||
|
itemList: ReactNode;
|
||||||
|
/** Class name */
|
||||||
|
className?: string;
|
||||||
|
/** Handle key down */
|
||||||
|
handleKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void;
|
||||||
|
/** Handle key up */
|
||||||
|
handleKeyUp?: (event: KeyboardEvent<HTMLDivElement>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniversalSearchQueryList props
|
||||||
|
interface UniversalSearchQueryListProps {
|
||||||
|
/** Whether the search is open */
|
||||||
|
isOpen: boolean;
|
||||||
|
/** Whether the search is loading */
|
||||||
|
isLoading: boolean;
|
||||||
|
/** Callback when search type changes */
|
||||||
|
onSearchTypeChange?: (option: SearchTypeOption) => void;
|
||||||
|
/** Current search type */
|
||||||
|
searchType: ResourceType;
|
||||||
|
/** Items to display */
|
||||||
|
items: UniversalSearchItem[];
|
||||||
|
/** Renderer for items */
|
||||||
|
itemRenderer?: ItemRenderer<UniversalSearchItem>;
|
||||||
|
/** Callback when an item is selected */
|
||||||
|
onItemSelect?: (item: UniversalSearchItem, event?: any) => void;
|
||||||
|
/** Current query string */
|
||||||
|
query: string;
|
||||||
|
/** Callback when query changes */
|
||||||
|
onQueryChange?: (query: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Universal search query list.
|
* Universal search query list.
|
||||||
*/
|
*/
|
||||||
function UniversalSearchQueryList(props) {
|
function UniversalSearchQueryList({
|
||||||
const { isOpen, isLoading, onSearchTypeChange, searchType, ...restProps } =
|
isOpen,
|
||||||
props;
|
isLoading,
|
||||||
|
onSearchTypeChange,
|
||||||
|
...restProps
|
||||||
|
}: UniversalSearchQueryListProps) {
|
||||||
return (
|
return (
|
||||||
<QueryList
|
<QueryList<UniversalSearchItem>
|
||||||
{...restProps}
|
{...(restProps as any)}
|
||||||
initialContent={null}
|
initialContent={null}
|
||||||
renderer={(listProps) => (
|
renderer={(listProps: QueryListRendererProps) => (
|
||||||
<UniversalSearchBar
|
<UniversalSearchBar
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onSearchTypeChange={onSearchTypeChange}
|
onSearchTypeChange={onSearchTypeChange}
|
||||||
@@ -100,47 +339,53 @@ function UniversalSearchQueryList(props) {
|
|||||||
*/
|
*/
|
||||||
function UniversalQuerySearchActions() {
|
function UniversalQuerySearchActions() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.UNIVERSAL_SEARCH_ACTIONS)}>
|
<x.div display="flex">
|
||||||
<div className={classNames(CLASSES.UNIVERSAL_SEARCH_ACTION_SELECT)}>
|
<x.div className={actionBaseStyles}>
|
||||||
<Tag>ENTER</Tag>
|
<Tag>ENTER</Tag>
|
||||||
<span class={'text'}>{intl.get('universal_search.enter_text')}</span>
|
<x.span ml="6px">{intl.get('universal_search.enter_text')}</x.span>
|
||||||
</div>
|
</x.div>
|
||||||
|
|
||||||
<div className={classNames(CLASSES.UNIVERSAL_SEARCH_ACTION_CLOSE)}>
|
<x.div className={actionBaseStyles}>
|
||||||
<Tag>ESC</Tag>{' '}
|
<Tag>ESC</Tag>{' '}
|
||||||
<span class={'text'}>{intl.get('universal_search.close_text')}</span>
|
<x.span ml="6px">{intl.get('universal_search.close_text')}</x.span>
|
||||||
</div>
|
</x.div>
|
||||||
|
|
||||||
<div className={classNames(CLASSES.UNIVERSAL_SEARCH_ACTION_ARROWS)}>
|
<x.div className={actionArrowsStyles}>
|
||||||
<Tag>
|
<Tag>
|
||||||
<Icon icon={'arrow-up-24'} iconSize={16} />
|
<Icon icon={'arrow-up-24'} iconSize={16} />
|
||||||
</Tag>
|
</Tag>
|
||||||
<Tag>
|
<Tag>
|
||||||
<Icon icon={'arrow-down-24'} iconSize={16} />
|
<Icon icon={'arrow-down-24'} iconSize={16} />
|
||||||
</Tag>
|
</Tag>
|
||||||
<span class="text">{intl.get('universal_seach.navigate_text')}</span>
|
<x.span ml="6px">{intl.get('universal_seach.navigate_text')}</x.span>
|
||||||
</div>
|
</x.div>
|
||||||
</div>
|
</x.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UniversalSearchBar props
|
||||||
|
interface UniversalSearchBarProps extends QueryListRendererProps {
|
||||||
|
/** Whether the search is open */
|
||||||
|
isOpen: boolean;
|
||||||
|
/** Callback when search type changes */
|
||||||
|
onSearchTypeChange?: (option: SearchTypeOption) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Universal search input bar with items list.
|
* Universal search input bar with items list.
|
||||||
*/
|
*/
|
||||||
function UniversalSearchBar({ isOpen, onSearchTypeChange, ...listProps }) {
|
function UniversalSearchBar({
|
||||||
|
isOpen,
|
||||||
|
onSearchTypeChange,
|
||||||
|
...listProps
|
||||||
|
}: UniversalSearchBarProps) {
|
||||||
const { handleKeyDown, handleKeyUp } = listProps;
|
const { handleKeyDown, handleKeyUp } = listProps;
|
||||||
const handlers = isOpen
|
const handlers = isOpen
|
||||||
? { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }
|
? { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<x.div {...handlers}>
|
||||||
className={classNames(
|
|
||||||
CLASSES.UNIVERSAL_SEARCH_OMNIBAR,
|
|
||||||
listProps.className,
|
|
||||||
)}
|
|
||||||
{...handlers}
|
|
||||||
>
|
|
||||||
<InputGroup
|
<InputGroup
|
||||||
large={true}
|
large={true}
|
||||||
leftIcon={<Icon icon={'universal-search'} iconSize={20} />}
|
leftIcon={<Icon icon={'universal-search'} iconSize={20} />}
|
||||||
@@ -155,17 +400,44 @@ function UniversalSearchBar({ isOpen, onSearchTypeChange, ...listProps }) {
|
|||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
/>
|
/>
|
||||||
{listProps.itemList}
|
{listProps.itemList}
|
||||||
</div>
|
</x.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UniversalSearch props
|
||||||
|
export interface UniversalSearchProps {
|
||||||
|
/** Default search resource type */
|
||||||
|
defaultSearchResource?: ResourceType;
|
||||||
|
/** Controlled search resource type */
|
||||||
|
searchResource?: ResourceType;
|
||||||
|
/** Overlay props */
|
||||||
|
overlayProps?: OverlayProps;
|
||||||
|
/** Whether the search overlay is open */
|
||||||
|
isOpen: boolean;
|
||||||
|
/** Whether the search is loading */
|
||||||
|
isLoading: boolean;
|
||||||
|
/** Callback when search type changes */
|
||||||
|
onSearchTypeChange?: (resource: SearchTypeOption) => void;
|
||||||
|
/** Items to display */
|
||||||
|
items: UniversalSearchItem[];
|
||||||
|
/** Available search type options */
|
||||||
|
searchTypeOptions: SearchTypeOption[];
|
||||||
|
/** Renderer for items */
|
||||||
|
itemRenderer?: ItemRenderer<UniversalSearchItem>;
|
||||||
|
/** Callback when an item is selected */
|
||||||
|
onItemSelect?: (item: UniversalSearchItem, event?: any) => void;
|
||||||
|
/** Current query string */
|
||||||
|
query: string;
|
||||||
|
/** Callback when query changes */
|
||||||
|
onQueryChange?: (query: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Universal search.
|
* Universal search.
|
||||||
*/
|
*/
|
||||||
export function UniversalSearch({
|
export function UniversalSearch({
|
||||||
defaultSearchResource,
|
defaultSearchResource,
|
||||||
searchResource,
|
searchResource,
|
||||||
|
|
||||||
overlayProps,
|
overlayProps,
|
||||||
isOpen,
|
isOpen,
|
||||||
isLoading,
|
isLoading,
|
||||||
@@ -173,9 +445,9 @@ export function UniversalSearch({
|
|||||||
items,
|
items,
|
||||||
searchTypeOptions,
|
searchTypeOptions,
|
||||||
...queryListProps
|
...queryListProps
|
||||||
}) {
|
}: UniversalSearchProps) {
|
||||||
// Search type state.
|
// Search type state.
|
||||||
const [searchType, setSearchType] = React.useState(
|
const [searchType, setSearchType] = React.useState<ResourceType>(
|
||||||
defaultSearchResource || RESOURCES_TYPES.CUSTOMER,
|
defaultSearchResource || RESOURCES_TYPES.CUSTOMER,
|
||||||
);
|
);
|
||||||
// Handle search resource type controlled mode.
|
// Handle search resource type controlled mode.
|
||||||
@@ -189,9 +461,9 @@ export function UniversalSearch({
|
|||||||
}, [searchResource, defaultSearchResource]);
|
}, [searchResource, defaultSearchResource]);
|
||||||
|
|
||||||
// Handle search type change.
|
// Handle search type change.
|
||||||
const handleSearchTypeChange = (searchTypeResource) => {
|
const handleSearchTypeChange = (searchTypeResource: SearchTypeOption) => {
|
||||||
setSearchType(searchTypeResource.key);
|
setSearchType(searchTypeResource.key);
|
||||||
onSearchTypeChange && onSearchTypeChange(searchTypeResource);
|
onSearchTypeChange?.(searchTypeResource);
|
||||||
};
|
};
|
||||||
// Filters query list items based on the given search type.
|
// Filters query list items based on the given search type.
|
||||||
const filteredItems = filterItemsByResourceType(items, searchType);
|
const filteredItems = filterItemsByResourceType(items, searchType);
|
||||||
@@ -200,7 +472,7 @@ export function UniversalSearch({
|
|||||||
<Overlay
|
<Overlay
|
||||||
hasBackdrop={true}
|
hasBackdrop={true}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
className={classNames(CLASSES.UNIVERSAL_SEARCH_OVERLAY)}
|
className={overlayStyles}
|
||||||
{...overlayProps}
|
{...overlayProps}
|
||||||
>
|
>
|
||||||
<UniversalSearchProvider
|
<UniversalSearchProvider
|
||||||
@@ -209,7 +481,7 @@ export function UniversalSearch({
|
|||||||
defaultSearchResource={defaultSearchResource}
|
defaultSearchResource={defaultSearchResource}
|
||||||
searchTypeOptions={searchTypeOptions}
|
searchTypeOptions={searchTypeOptions}
|
||||||
>
|
>
|
||||||
<div className={classNames(CLASSES.UNIVERSAL_SEARCH)}>
|
<x.div className={containerStyles}>
|
||||||
<UniversalSearchQueryList
|
<UniversalSearchQueryList
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
@@ -218,10 +490,10 @@ export function UniversalSearch({
|
|||||||
{...queryListProps}
|
{...queryListProps}
|
||||||
items={filteredItems}
|
items={filteredItems}
|
||||||
/>
|
/>
|
||||||
<div className={classNames(CLASSES.UNIVERSAL_SEARCH_FOOTER)}>
|
<x.div className={footerStyles}>
|
||||||
<UniversalQuerySearchActions />
|
<UniversalQuerySearchActions />
|
||||||
</div>
|
</x.div>
|
||||||
</div>
|
</x.div>
|
||||||
</UniversalSearchProvider>
|
</UniversalSearchProvider>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,30 +1,82 @@
|
|||||||
// @ts-nocheck
|
import React, { createContext, ReactNode, useContext } from 'react';
|
||||||
import React, { createContext } from 'react';
|
|
||||||
|
|
||||||
const UniversalSearchContext = createContext();
|
// The resource type value from RESOURCES_TYPES constant
|
||||||
|
type ResourceType = string;
|
||||||
|
|
||||||
|
// Search type option item
|
||||||
|
interface SearchTypeOption {
|
||||||
|
key: ResourceType;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context value type
|
||||||
|
interface UniversalSearchContextValue {
|
||||||
|
/** Whether the search is loading */
|
||||||
|
isLoading: boolean;
|
||||||
|
/** Current search type/resource type */
|
||||||
|
searchType: ResourceType;
|
||||||
|
/** Default search resource type */
|
||||||
|
defaultSearchResource?: ResourceType;
|
||||||
|
/** List of available search type options */
|
||||||
|
searchTypeOptions: SearchTypeOption[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the context with undefined as initial value
|
||||||
|
const UniversalSearchContext = createContext<
|
||||||
|
UniversalSearchContextValue | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
// Provider props interface
|
||||||
|
interface UniversalSearchProviderProps {
|
||||||
|
/** Whether the search is loading */
|
||||||
|
isLoading: boolean;
|
||||||
|
/** Default search resource type */
|
||||||
|
defaultSearchResource?: ResourceType;
|
||||||
|
/** Current search type/resource type */
|
||||||
|
searchType: ResourceType;
|
||||||
|
/** List of available search type options */
|
||||||
|
searchTypeOptions: SearchTypeOption[];
|
||||||
|
/** Child elements */
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Universal search data provider.
|
* Universal search data provider.
|
||||||
*/
|
*/
|
||||||
function UniversalSearchProvider({
|
export function UniversalSearchProvider({
|
||||||
isLoading,
|
isLoading,
|
||||||
defaultSearchResource,
|
defaultSearchResource,
|
||||||
searchType,
|
searchType,
|
||||||
searchTypeOptions,
|
searchTypeOptions,
|
||||||
...props
|
children,
|
||||||
}) {
|
}: UniversalSearchProviderProps) {
|
||||||
// Provider payload.
|
// Provider payload.
|
||||||
const provider = {
|
const provider: UniversalSearchContextValue = {
|
||||||
isLoading,
|
isLoading,
|
||||||
searchType,
|
searchType,
|
||||||
defaultSearchResource,
|
defaultSearchResource,
|
||||||
searchTypeOptions,
|
searchTypeOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
return <UniversalSearchContext.Provider value={provider} {...props} />;
|
return (
|
||||||
|
<UniversalSearchContext.Provider value={provider}>
|
||||||
|
{children}
|
||||||
|
</UniversalSearchContext.Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const useUniversalSearchContext = () =>
|
/**
|
||||||
React.useContext(UniversalSearchContext);
|
* Hook to access the universal search context.
|
||||||
|
* @throws Error if used outside of UniversalSearchProvider
|
||||||
|
*/
|
||||||
|
export const useUniversalSearchContext = (): UniversalSearchContextValue => {
|
||||||
|
const context = useContext(UniversalSearchContext);
|
||||||
|
|
||||||
export { UniversalSearchProvider, useUniversalSearchContext };
|
if (context === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
'useUniversalSearchContext must be used within a UniversalSearchProvider',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
// @ts-nocheck
|
import React, { ReactNode } from 'react';
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
export const If = (props) =>
|
interface IfProps {
|
||||||
props.condition ? (props.render ? props.render() : props.children) : null;
|
condition: boolean;
|
||||||
|
children?: ReactNode;
|
||||||
|
render?: () => ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
If.propTypes = {
|
export const If = (props: IfProps): React.ReactElement | null =>
|
||||||
// condition: PropTypes.bool.isRequired,
|
props.condition ? (props.render ? <>{props.render()}</> : <>{props.children}</>) : null;
|
||||||
children: PropTypes.node,
|
|
||||||
render: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -32,38 +32,38 @@ export default function MakeJournalFloatingAction() {
|
|||||||
|
|
||||||
// Handle submit & publish button click.
|
// Handle submit & publish button click.
|
||||||
const handleSubmitPublishBtnClick = (event) => {
|
const handleSubmitPublishBtnClick = (event) => {
|
||||||
submitForm();
|
|
||||||
setSubmitPayload({ redirect: true, publish: true });
|
setSubmitPayload({ redirect: true, publish: true });
|
||||||
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit, publish & new button click.
|
// Handle submit, publish & new button click.
|
||||||
const handleSubmitPublishAndNewBtnClick = (event) => {
|
const handleSubmitPublishAndNewBtnClick = (event) => {
|
||||||
submitForm();
|
|
||||||
setSubmitPayload({ redirect: false, publish: true, resetForm: true });
|
setSubmitPayload({ redirect: false, publish: true, resetForm: true });
|
||||||
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit, publish & edit button click.
|
// Handle submit, publish & edit button click.
|
||||||
const handleSubmitPublishContinueEditingBtnClick = (event) => {
|
const handleSubmitPublishContinueEditingBtnClick = (event) => {
|
||||||
submitForm();
|
|
||||||
setSubmitPayload({ redirect: false, publish: true });
|
setSubmitPayload({ redirect: false, publish: true });
|
||||||
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit as draft button click.
|
// Handle submit as draft button click.
|
||||||
const handleSubmitDraftBtnClick = (event) => {
|
const handleSubmitDraftBtnClick = (event) => {
|
||||||
submitForm();
|
|
||||||
setSubmitPayload({ redirect: true, publish: false });
|
setSubmitPayload({ redirect: true, publish: false });
|
||||||
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit as draft & new button click.
|
// Handle submit as draft & new button click.
|
||||||
const handleSubmitDraftAndNewBtnClick = (event) => {
|
const handleSubmitDraftAndNewBtnClick = (event) => {
|
||||||
submitForm();
|
|
||||||
setSubmitPayload({ redirect: false, publish: false, resetForm: true });
|
setSubmitPayload({ redirect: false, publish: false, resetForm: true });
|
||||||
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit as draft & continue editing button click.
|
// Handle submit as draft & continue editing button click.
|
||||||
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
||||||
submitForm();
|
|
||||||
setSubmitPayload({ redirect: false, publish: false });
|
setSubmitPayload({ redirect: false, publish: false });
|
||||||
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle cancel button click.
|
// Handle cancel button click.
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ const Schema = Yup.object().shape({
|
|||||||
.label(intl.get('display_name_')),
|
.label(intl.get('display_name_')),
|
||||||
|
|
||||||
email: Yup.string().email().nullable(),
|
email: Yup.string().email().nullable(),
|
||||||
work_phone: Yup.number(),
|
work_phone: Yup.string().nullable(),
|
||||||
personal_phone: Yup.number(),
|
personal_phone: Yup.string().nullable(),
|
||||||
website: Yup.string().url().nullable(),
|
website: Yup.string().url().nullable(),
|
||||||
|
|
||||||
active: Yup.boolean(),
|
active: Yup.boolean(),
|
||||||
@@ -30,7 +30,7 @@ const Schema = Yup.object().shape({
|
|||||||
billing_address_city: Yup.string().trim(),
|
billing_address_city: Yup.string().trim(),
|
||||||
billing_address_state: Yup.string().trim(),
|
billing_address_state: Yup.string().trim(),
|
||||||
billing_address_postcode: Yup.string().nullable(),
|
billing_address_postcode: Yup.string().nullable(),
|
||||||
billing_address_phone: Yup.number(),
|
billing_address_phone: Yup.string().nullable(),
|
||||||
|
|
||||||
shipping_address_country: Yup.string().trim(),
|
shipping_address_country: Yup.string().trim(),
|
||||||
shipping_address_1: Yup.string().trim(),
|
shipping_address_1: Yup.string().trim(),
|
||||||
@@ -38,7 +38,7 @@ const Schema = Yup.object().shape({
|
|||||||
shipping_address_city: Yup.string().trim(),
|
shipping_address_city: Yup.string().trim(),
|
||||||
shipping_address_state: Yup.string().trim(),
|
shipping_address_state: Yup.string().trim(),
|
||||||
shipping_address_postcode: Yup.string().nullable(),
|
shipping_address_postcode: Yup.string().nullable(),
|
||||||
shipping_address_phone: Yup.number(),
|
shipping_address_phone: Yup.string().nullable(),
|
||||||
|
|
||||||
opening_balance: Yup.number().nullable(),
|
opening_balance: Yup.number().nullable(),
|
||||||
currency_code: Yup.string(),
|
currency_code: Yup.string(),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { useContactDetailDrawerContext } from './ContactDetailDrawerProvider';
|
|||||||
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
||||||
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
||||||
|
|
||||||
import { DashboardActionsBar, Icon, FormattedMessage as T } from '@/components';
|
import { DrawerActionsBar, Icon, FormattedMessage as T } from '@/components';
|
||||||
|
|
||||||
import { safeCallback, compose } from '@/utils';
|
import { safeCallback, compose } from '@/utils';
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ function ContactDetailActionsBar({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardActionsBar>
|
<DrawerActionsBar>
|
||||||
<NavbarGroup>
|
<NavbarGroup>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
@@ -63,7 +63,7 @@ function ContactDetailActionsBar({
|
|||||||
onClick={safeCallback(onDeleteContact)}
|
onClick={safeCallback(onDeleteContact)}
|
||||||
/>
|
/>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</DashboardActionsBar>
|
</DrawerActionsBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import { withDialogActions } from '@/containers/Dialog/withDialogActions';
|
|||||||
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DashboardActionsBar,
|
|
||||||
Can,
|
Can,
|
||||||
Icon,
|
Icon,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
If,
|
If,
|
||||||
Icon,
|
Icon,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
DashboardActionsBar,
|
DrawerActionsBar,
|
||||||
Can,
|
Can,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ function VendorCreditDetailActionsBar({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardActionsBar>
|
<DrawerActionsBar>
|
||||||
<NavbarGroup>
|
<NavbarGroup>
|
||||||
<Can I={VendorCreditAction.Edit} a={AbilitySubject.VendorCredit}>
|
<Can I={VendorCreditAction.Edit} a={AbilitySubject.VendorCredit}>
|
||||||
<Button
|
<Button
|
||||||
@@ -105,7 +105,7 @@ function VendorCreditDetailActionsBar({
|
|||||||
</If>
|
</If>
|
||||||
</Can>
|
</Can>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</DashboardActionsBar>
|
</DrawerActionsBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ function GlobalErrors({
|
|||||||
if (globalErrors.access_denied) {
|
if (globalErrors.access_denied) {
|
||||||
toastKeySomethingWrong = AppToaster.show(
|
toastKeySomethingWrong = AppToaster.show(
|
||||||
{
|
{
|
||||||
message: intl.get('global_error.you_dont_have_permissions'),
|
message: globalErrors.access_denied.message || intl.get('global_error.you_dont_have_permissions'),
|
||||||
intent: Intent.DANGER,
|
intent: Intent.DANGER,
|
||||||
onDismiss: () => {
|
onDismiss: () => {
|
||||||
globalErrorsSet({ access_denied: false });
|
globalErrorsSet({ access_denied: false });
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { MenuItem } from '@blueprintjs/core';
|
import { MenuItem, Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
import { formattedAmount } from '@/utils';
|
import { formattedAmount } from '@/utils';
|
||||||
import { T, Icon, Choose, If } from '@/components';
|
import { T, Icon, Choose, If, TextStatus } from '@/components';
|
||||||
|
|
||||||
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
||||||
import { AbilitySubject, BillAction } from '@/constants/abilityOption';
|
import { AbilitySubject, BillAction } from '@/constants/abilityOption';
|
||||||
@@ -41,35 +41,35 @@ export function BillStatus({ bill }) {
|
|||||||
return (
|
return (
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={bill.is_fully_paid && bill.is_open}>
|
<Choose.When condition={bill.is_fully_paid && bill.is_open}>
|
||||||
<span class="fully-paid-text">
|
<TextStatus intent={Intent.SUCCESS}>
|
||||||
<T id={'paid'} />
|
<T id={'paid'} />
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
<Choose.When condition={bill.is_open}>
|
<Choose.When condition={bill.is_open}>
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={bill.is_overdue}>
|
<Choose.When condition={bill.is_overdue}>
|
||||||
<span className={'overdue-status'}>
|
<TextStatus intent={Intent.DANGER}>
|
||||||
{intl.get('overdue_by', { overdue: bill.overdue_days })}
|
{intl.get('overdue_by', { overdue: bill.overdue_days })}
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
<Choose.Otherwise>
|
<Choose.Otherwise>
|
||||||
<span className={'due-status'}>
|
<TextStatus intent={Intent.WARNING}>
|
||||||
{intl.get('due_in', { due: bill.remaining_days })}
|
{intl.get('due_in', { due: bill.remaining_days })}
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
<If condition={bill.is_partially_paid}>
|
<If condition={bill.is_partially_paid}>
|
||||||
<span className="partial-paid">
|
<TextStatus intent={Intent.WARNING}>
|
||||||
{intl.get('day_partially_paid', {
|
{intl.get('day_partially_paid', {
|
||||||
due: formattedAmount(bill.due_amount, bill.currency_code),
|
due: formattedAmount(bill.due_amount, bill.currency_code),
|
||||||
})}
|
})}
|
||||||
</span>
|
</TextStatus>
|
||||||
</If>
|
</If>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
<Choose.Otherwise>
|
<Choose.Otherwise>
|
||||||
<span class="draft">
|
<TextStatus intent={Intent.NONE}>
|
||||||
<T id={'draft'} />
|
<T id={'draft'} />
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { useDrawerActions } from '@/hooks/state';
|
|||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
||||||
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
||||||
|
import { Box } from '@/components';
|
||||||
|
|
||||||
export function CreditNoteCustomizeContent() {
|
export function CreditNoteCustomizeContent() {
|
||||||
const { payload, name } = useDrawerContext();
|
const { payload, name } = useDrawerContext();
|
||||||
@@ -45,7 +46,9 @@ function CreditNoteCustomizeFormContent() {
|
|||||||
return (
|
return (
|
||||||
<ElementCustomizeContent>
|
<ElementCustomizeContent>
|
||||||
<ElementCustomize.PaperTemplate>
|
<ElementCustomize.PaperTemplate>
|
||||||
<CreditNotePaperTemplateFormConnected />
|
<Box overflow="auto" flex="1 1" px={4} py={6}>
|
||||||
|
<CreditNotePaperTemplateFormConnected />
|
||||||
|
</Box>
|
||||||
</ElementCustomize.PaperTemplate>
|
</ElementCustomize.PaperTemplate>
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { useDrawerActions } from '@/hooks/state';
|
|||||||
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
||||||
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
||||||
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
||||||
|
import { Box } from '@/components';
|
||||||
|
|
||||||
export function EstimateCustomizeContent() {
|
export function EstimateCustomizeContent() {
|
||||||
const { payload, name } = useDrawerContext();
|
const { payload, name } = useDrawerContext();
|
||||||
@@ -44,7 +45,9 @@ function EstimateCustomizeFormContent() {
|
|||||||
return (
|
return (
|
||||||
<ElementCustomizeContent>
|
<ElementCustomizeContent>
|
||||||
<ElementCustomize.PaperTemplate>
|
<ElementCustomize.PaperTemplate>
|
||||||
<EstimatePaperTemplateFormConnected />
|
<Box overflow="auto" flex="1 1" px={4} py={6}>
|
||||||
|
<EstimatePaperTemplateFormConnected />
|
||||||
|
</Box>
|
||||||
</ElementCustomize.PaperTemplate>
|
</ElementCustomize.PaperTemplate>
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const estimatePreviewCss = css`
|
|||||||
|
|
||||||
export const EstimateSendMailReceiptPreview = () => {
|
export const EstimateSendMailReceiptPreview = () => {
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack spacing={0}>
|
||||||
<EstimateSendMailPreviewHeader />
|
<EstimateSendMailPreviewHeader />
|
||||||
|
|
||||||
<Stack px={4} py={6}>
|
<Stack px={4} py={6}>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
|||||||
|
|
||||||
export function EstimateSendPdfPreviewConnected() {
|
export function EstimateSendPdfPreviewConnected() {
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack spacing={0}>
|
||||||
<EstimateSendMailPreviewHeader />
|
<EstimateSendMailPreviewHeader />
|
||||||
|
|
||||||
<Stack px={4} py={6}>
|
<Stack px={4} py={6}>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { MenuItem } from '@blueprintjs/core';
|
import { MenuItem, Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
import { Choose, T, Icon } from '@/components';
|
import { Choose, T, Icon, TextStatus } from '@/components';
|
||||||
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
||||||
import { AbilitySubject, SaleEstimateAction } from '@/constants/abilityOption';
|
import { AbilitySubject, SaleEstimateAction } from '@/constants/abilityOption';
|
||||||
|
|
||||||
@@ -37,28 +37,28 @@ export const EstimateUniversalSearchSelect = withDrawerActions(
|
|||||||
export const EstimateStatus = ({ estimate }) => (
|
export const EstimateStatus = ({ estimate }) => (
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={estimate.is_delivered && estimate.is_approved}>
|
<Choose.When condition={estimate.is_delivered && estimate.is_approved}>
|
||||||
<span class="approved">
|
<TextStatus intent={Intent.SUCCESS}>
|
||||||
<T id={'approved'} />
|
<T id={'approved'} />
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
<Choose.When condition={estimate.is_delivered && estimate.is_rejected}>
|
<Choose.When condition={estimate.is_delivered && estimate.is_rejected}>
|
||||||
<span class="reject">
|
<TextStatus intent={Intent.DANGER}>
|
||||||
<T id={'rejected'} />
|
<T id={'rejected'} />
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
<Choose.When
|
<Choose.When
|
||||||
condition={
|
condition={
|
||||||
estimate.is_delivered && !estimate.is_rejected && !estimate.is_approved
|
estimate.is_delivered && !estimate.is_rejected && !estimate.is_approved
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span class="delivered">
|
<TextStatus intent={Intent.SUCCESS}>
|
||||||
<T id={'delivered'} />
|
<T id={'delivered'} />
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
<Choose.Otherwise>
|
<Choose.Otherwise>
|
||||||
<span class="draft">
|
<TextStatus intent={Intent.NONE}>
|
||||||
<T id={'draft'} />
|
<T id={'draft'} />
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { MenuItem } from '@blueprintjs/core';
|
import { MenuItem, Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
import { T, Choose, Icon } from '@/components';
|
import { T, Choose, Icon, TextStatus } from '@/components';
|
||||||
import { highlightText } from '@/utils';
|
import { highlightText } from '@/utils';
|
||||||
|
|
||||||
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
||||||
@@ -39,29 +39,29 @@ function InvoiceStatus({ customer }) {
|
|||||||
return (
|
return (
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={customer.is_fully_paid && customer.is_delivered}>
|
<Choose.When condition={customer.is_fully_paid && customer.is_delivered}>
|
||||||
<span class="status status-success">
|
<TextStatus intent={Intent.SUCCESS}>
|
||||||
<T id={'paid'} />
|
<T id={'paid'} />
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
|
|
||||||
<Choose.When condition={customer.is_delivered}>
|
<Choose.When condition={customer.is_delivered}>
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={customer.is_overdue}>
|
<Choose.When condition={customer.is_overdue}>
|
||||||
<span className={'status status-warning'}>
|
<TextStatus intent={Intent.DANGER}>
|
||||||
{intl.get('overdue_by', { overdue: customer.overdue_days })}
|
{intl.get('overdue_by', { overdue: customer.overdue_days })}
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
<Choose.Otherwise>
|
<Choose.Otherwise>
|
||||||
<span className={'status status-warning'}>
|
<TextStatus intent={Intent.WARNING}>
|
||||||
{intl.get('due_in', { due: customer.remaining_days })}
|
{intl.get('due_in', { due: customer.remaining_days })}
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
<Choose.Otherwise>
|
<Choose.Otherwise>
|
||||||
<span class="status status--gray">
|
<TextStatus intent={Intent.NONE}>
|
||||||
<T id={'draft'} />
|
<T id={'draft'} />
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
);
|
);
|
||||||
@@ -94,7 +94,6 @@ export function InvoiceUniversalSearchItem(
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className={'universal-search__item--invoice'}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { useDrawerActions } from '@/hooks/state';
|
|||||||
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
||||||
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
||||||
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
||||||
|
import { Box } from '@/components';
|
||||||
|
|
||||||
export function PaymentReceivedCustomizeContent() {
|
export function PaymentReceivedCustomizeContent() {
|
||||||
const { payload, name } = useDrawerContext();
|
const { payload, name } = useDrawerContext();
|
||||||
@@ -51,7 +52,9 @@ function PaymentReceivedCustomizeFormContent() {
|
|||||||
return (
|
return (
|
||||||
<ElementCustomizeContent>
|
<ElementCustomizeContent>
|
||||||
<ElementCustomize.PaperTemplate>
|
<ElementCustomize.PaperTemplate>
|
||||||
<PaymentReceivedPaperTemplateFormConnected />
|
<Box overflow="auto" flex="1 1" px={4} py={6}>
|
||||||
|
<PaymentReceivedPaperTemplateFormConnected />
|
||||||
|
</Box>
|
||||||
</ElementCustomize.PaperTemplate>
|
</ElementCustomize.PaperTemplate>
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { PaymentReceivedMailPreviewHeader } from './PaymentReceivedMailPreviewHe
|
|||||||
|
|
||||||
export function PaymentReceivedSendMailPreviewPdf() {
|
export function PaymentReceivedSendMailPreviewPdf() {
|
||||||
return (
|
return (
|
||||||
<Stack flex={1}>
|
<Stack flex={1} spacing={0}>
|
||||||
<PaymentReceivedMailPreviewHeader />
|
<PaymentReceivedMailPreviewHeader />
|
||||||
|
|
||||||
<Stack px={4} py={6}>
|
<Stack px={4} py={6}>
|
||||||
@@ -22,7 +22,6 @@ function PaymentReceivedSendPdfPreviewIframe() {
|
|||||||
const { data, isLoading } = useGetPaymentReceiveHtml(
|
const { data, isLoading } = useGetPaymentReceiveHtml(
|
||||||
payload?.paymentReceivedId,
|
payload?.paymentReceivedId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isLoading && data) {
|
if (isLoading && data) {
|
||||||
return <Spinner size={20} />;
|
return <Spinner size={20} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const mailReceiptCss = css`
|
|||||||
|
|
||||||
export function PaymentReceivedMailPreviewReceipt() {
|
export function PaymentReceivedMailPreviewReceipt() {
|
||||||
return (
|
return (
|
||||||
<Stack flex={1}>
|
<Stack flex={1} spacing={0}>
|
||||||
<PaymentReceivedMailPreviewHeader />
|
<PaymentReceivedMailPreviewHeader />
|
||||||
|
|
||||||
<Stack px={4} py={6}>
|
<Stack px={4} py={6}>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { useDrawerActions } from '@/hooks/state';
|
|||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
||||||
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
||||||
|
import { Box } from '@/components';
|
||||||
|
|
||||||
export function ReceiptCustomizeContent() {
|
export function ReceiptCustomizeContent() {
|
||||||
const { payload, name } = useDrawerContext();
|
const { payload, name } = useDrawerContext();
|
||||||
@@ -44,7 +45,9 @@ function ReceiptCustomizeFormContent() {
|
|||||||
return (
|
return (
|
||||||
<ElementCustomizeContent>
|
<ElementCustomizeContent>
|
||||||
<ElementCustomize.PaperTemplate>
|
<ElementCustomize.PaperTemplate>
|
||||||
<ReceiptPaperTemplateFormConnected />
|
<Box overflow="auto" flex="1 1" px={4} py={6}>
|
||||||
|
<ReceiptPaperTemplateFormConnected />
|
||||||
|
</Box>
|
||||||
</ElementCustomize.PaperTemplate>
|
</ElementCustomize.PaperTemplate>
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { SendMailViewPreviewPdfIframe } from '../../Estimates/SendMailViewDrawer
|
|||||||
|
|
||||||
export function ReceiptSendMailPdfPreview() {
|
export function ReceiptSendMailPdfPreview() {
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack spacing={0}>
|
||||||
<ReceiptSendMailPreviewHeader />
|
<ReceiptSendMailPreviewHeader />
|
||||||
|
|
||||||
<Stack px={4} py={6}>
|
<Stack px={4} py={6}>
|
||||||
@@ -26,7 +26,5 @@ function ReceiptSendPdfPreviewIframe() {
|
|||||||
}
|
}
|
||||||
const iframeSrcDoc = data?.htmlContent;
|
const iframeSrcDoc = data?.htmlContent;
|
||||||
|
|
||||||
console.log(data, 'data');
|
|
||||||
|
|
||||||
return <SendMailViewPreviewPdfIframe srcDoc={iframeSrcDoc} />;
|
return <SendMailViewPreviewPdfIframe srcDoc={iframeSrcDoc} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const receiptPreviewCss = css`
|
|||||||
|
|
||||||
export function ReceiptSendMailPreview() {
|
export function ReceiptSendMailPreview() {
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack spacing={0}>
|
||||||
<ReceiptSendMailPreviewHeader />
|
<ReceiptSendMailPreviewHeader />
|
||||||
|
|
||||||
<Stack px={4} py={6}>
|
<Stack px={4} py={6}>
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { MenuItem } from '@blueprintjs/core';
|
import { MenuItem, Intent } from '@blueprintjs/core';
|
||||||
|
import { Icon, Choose, T, TextStatus } from '@/components';
|
||||||
import { Icon, Choose, T } from '@/components';
|
|
||||||
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
||||||
import { AbilitySubject, SaleReceiptAction } from '@/constants/abilityOption';
|
import { AbilitySubject, SaleReceiptAction } from '@/constants/abilityOption';
|
||||||
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
||||||
@@ -39,15 +38,15 @@ function ReceiptStatus({ receipt }) {
|
|||||||
return (
|
return (
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={receipt.is_closed}>
|
<Choose.When condition={receipt.is_closed}>
|
||||||
<span class="closed">
|
<TextStatus intent={Intent.SUCCESS}>
|
||||||
<T id={'closed'} />
|
<T id={'closed'} />
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
|
|
||||||
<Choose.Otherwise>
|
<Choose.Otherwise>
|
||||||
<span class="draft">
|
<TextStatus intent={Intent.NONE}>
|
||||||
<T id={'draft'} />
|
<T id={'draft'} />
|
||||||
</span>
|
</TextStatus>
|
||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Intent, Tag } from '@blueprintjs/core';
|
import { Intent, Tag, Classes } from '@blueprintjs/core';
|
||||||
import { Align } from '@/constants';
|
import { Align } from '@/constants';
|
||||||
import styled from 'styled-components';
|
import clsx from 'classnames';
|
||||||
|
|
||||||
const codeAccessor = (taxRate) => {
|
const codeAccessor = (taxRate) => {
|
||||||
return (
|
return (
|
||||||
@@ -28,13 +28,17 @@ const nameAccessor = (taxRate) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>{taxRate.name}</span>
|
<span>{taxRate.name}</span>
|
||||||
{!!taxRate.is_compound && <CompoundText>(Compound tax)</CompoundText>}
|
{!!taxRate.is_compound && (
|
||||||
|
<span className={clsx(Classes.TEXT_MUTED)}>(Compound tax)</span>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DescriptionAccessor = (taxRate) => {
|
const DescriptionAccessor = (taxRate) => {
|
||||||
return <DescriptionText>{taxRate.description}</DescriptionText>;
|
return (
|
||||||
|
<span className={clsx(Classes.TEXT_MUTED)}>{taxRate.description}</span>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,11 +76,3 @@ export const useTaxRatesTableColumns = () => {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const CompoundText = styled('span')`
|
|
||||||
color: #738091;
|
|
||||||
margin-left: 5px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const DescriptionText = styled('span')`
|
|
||||||
color: #5f6b7c;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
Position,
|
Position,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { AppToaster, Can, DashboardActionsBar, Icon } from '@/components';
|
import { AppToaster, Can, DrawerActionsBar, Icon } from '@/components';
|
||||||
import { AbilitySubject, TaxRateAction } from '@/constants/abilityOption';
|
import { AbilitySubject, TaxRateAction } from '@/constants/abilityOption';
|
||||||
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
import { withDrawerActions } from '@/containers/Drawer/withDrawerActions';
|
||||||
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
import { withAlertActions } from '@/containers/Alert/withAlertActions';
|
||||||
@@ -83,7 +83,7 @@ function TaxRateDetailsContentActionsBar({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardActionsBar>
|
<DrawerActionsBar>
|
||||||
<NavbarGroup>
|
<NavbarGroup>
|
||||||
<Can I={TaxRateAction.Edit} a={AbilitySubject.TaxRate}>
|
<Can I={TaxRateAction.Edit} a={AbilitySubject.TaxRate}>
|
||||||
<Button
|
<Button
|
||||||
@@ -137,7 +137,7 @@ function TaxRateDetailsContentActionsBar({
|
|||||||
</Popover>
|
</Popover>
|
||||||
</Can>
|
</Can>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</DashboardActionsBar>
|
</DrawerActionsBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,9 +74,13 @@ const TaxRateHeader = styled(`div`)`
|
|||||||
const TaxRateAmount = styled('div')`
|
const TaxRateAmount = styled('div')`
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
color: #565b71;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
color: var(--x-color-amount-text, #565b71);
|
||||||
|
|
||||||
|
.bp4-dark & {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TaxRateActiveTag = styled(Tag)`
|
const TaxRateActiveTag = styled(Tag)`
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ const Schema = Yup.object().shape({
|
|||||||
display_name: Yup.string().trim().required().label(intl.get('display_name_')),
|
display_name: Yup.string().trim().required().label(intl.get('display_name_')),
|
||||||
|
|
||||||
email: Yup.string().email().nullable(),
|
email: Yup.string().email().nullable(),
|
||||||
work_phone: Yup.number(),
|
work_phone: Yup.string().nullable(),
|
||||||
personal_phone: Yup.number(),
|
personal_phone: Yup.string().nullable(),
|
||||||
website: Yup.string().url().nullable(),
|
website: Yup.string().url().nullable(),
|
||||||
|
|
||||||
active: Yup.boolean(),
|
active: Yup.boolean(),
|
||||||
@@ -23,7 +23,7 @@ const Schema = Yup.object().shape({
|
|||||||
billing_address_city: Yup.string().trim(),
|
billing_address_city: Yup.string().trim(),
|
||||||
billing_address_state: Yup.string().trim(),
|
billing_address_state: Yup.string().trim(),
|
||||||
billing_address_postcode: Yup.string().nullable(),
|
billing_address_postcode: Yup.string().nullable(),
|
||||||
billing_address_phone: Yup.number(),
|
billing_address_phone: Yup.string().nullable(),
|
||||||
|
|
||||||
shipping_address_country: Yup.string().trim(),
|
shipping_address_country: Yup.string().trim(),
|
||||||
shipping_address_1: Yup.string().trim(),
|
shipping_address_1: Yup.string().trim(),
|
||||||
@@ -31,7 +31,7 @@ const Schema = Yup.object().shape({
|
|||||||
shipping_address_city: Yup.string().trim(),
|
shipping_address_city: Yup.string().trim(),
|
||||||
shipping_address_state: Yup.string().trim(),
|
shipping_address_state: Yup.string().trim(),
|
||||||
shipping_address_postcode: Yup.string().nullable(),
|
shipping_address_postcode: Yup.string().nullable(),
|
||||||
shipping_address_phone: Yup.number(),
|
shipping_address_phone: Yup.string().nullable(),
|
||||||
|
|
||||||
opening_balance: Yup.number().nullable(),
|
opening_balance: Yup.number().nullable(),
|
||||||
currency_code: Yup.string(),
|
currency_code: Yup.string(),
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user