This commit is contained in:
Ahmed Bouhuolia
2026-01-12 01:04:28 +02:00
parent 16f1d57279
commit 3c1273becb
32 changed files with 180 additions and 132 deletions

View File

@@ -8,6 +8,7 @@ import {
Query,
ParseIntPipe,
Put,
HttpCode,
} from '@nestjs/common';
import { AccountsApplication } from './AccountsApplication.service';
import { CreateAccountDTO } from './CreateAccount.dto';
@@ -43,6 +44,7 @@ export class AccountsController {
constructor(private readonly accountsApplication: AccountsApplication) { }
@Post('validate-bulk-delete')
@HttpCode(200)
@ApiOperation({
summary:
'Validates which accounts can be deleted and returns counts of deletable and non-deletable accounts.',
@@ -64,6 +66,7 @@ export class AccountsController {
}
@Post('bulk-delete')
@HttpCode(200)
@ApiOperation({ summary: 'Deletes multiple accounts in bulk.' })
@ApiResponse({
status: 200,
@@ -125,6 +128,7 @@ export class AccountsController {
}
@Post(':id/activate')
@HttpCode(200)
@ApiOperation({ summary: 'Activate the given account.' })
@ApiResponse({
status: 200,
@@ -142,6 +146,7 @@ export class AccountsController {
}
@Post(':id/inactivate')
@HttpCode(200)
@ApiOperation({ summary: 'Inactivate the given account.' })
@ApiResponse({
status: 200,

View File

@@ -18,7 +18,7 @@ export class DeleteAccount {
private eventEmitter: EventEmitter2,
private uow: UnitOfWork,
private validator: CommandAccountValidators,
) {}
) { }
/**
* Authorize account delete.
@@ -57,7 +57,10 @@ export class DeleteAccount {
trx?: Knex.Transaction,
): Promise<void> => {
// Retrieve account or not found service error.
const oldAccount = await this.accountModel().query().findById(accountId);
const oldAccount = await this.accountModel()
.query()
.findById(accountId)
.throwIfNotFound();
// Authorize before delete account.
await this.authorize(accountId, oldAccount);

View File

@@ -3,7 +3,7 @@ import {
Get,
Query,
Param,
Post,
Patch,
ParseIntPipe,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiParam } from '@nestjs/swagger';
@@ -27,7 +27,7 @@ export class ContactsController {
return this.getAutoCompleteService.autocompleteContacts(query);
}
@Post(':id/activate')
@Patch(':id/activate')
@ApiOperation({ summary: 'Activate a contact' })
@ApiParam({ name: 'id', type: 'number', description: 'Contact ID' })
async activateContact(@Param('id', ParseIntPipe) contactId: number) {
@@ -35,7 +35,7 @@ export class ContactsController {
return { id: contactId, activated: true };
}
@Post(':id/inactivate')
@Patch(':id/inactivate')
@ApiOperation({ summary: 'Inactivate a contact' })
@ApiParam({ name: 'id', type: 'number', description: 'Contact ID' })
async inactivateContact(@Param('id', ParseIntPipe) contactId: number) {

View File

@@ -16,7 +16,7 @@ import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
@ApiTags('Reports')
@ApiCommonHeaders()
export class APAgingSummaryController {
constructor(private readonly APAgingSummaryApp: APAgingSummaryApplication) {}
constructor(private readonly APAgingSummaryApp: APAgingSummaryApplication) { }
@Get()
@ApiOperation({ summary: 'Get payable aging summary' })
@@ -37,12 +37,13 @@ export class APAgingSummaryController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the json table format.
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.APAgingSummaryApp.table(filter);
// Retrieves the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const csv = await this.APAgingSummaryApp.csv(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -50,7 +51,7 @@ export class APAgingSummaryController {
res.send(csv);
// Retrieves the xlsx format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.APAgingSummaryApp.xlsx(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -60,7 +61,7 @@ export class APAgingSummaryController {
);
res.send(buffer);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.APAgingSummaryApp.pdf(filter);
res.set({

View File

@@ -38,8 +38,9 @@ export class ARAgingSummaryController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the xlsx format.
if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.ARAgingSummaryApp.xlsx(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -49,11 +50,11 @@ export class ARAgingSummaryController {
);
res.send(buffer);
// Retrieves the table format.
} else if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
} else if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.ARAgingSummaryApp.table(filter);
// Retrieves the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.ARAgingSummaryApp.csv(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -61,7 +62,7 @@ export class ARAgingSummaryController {
res.send(buffer);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.ARAgingSummaryApp.pdf(filter);
res.set({

View File

@@ -43,13 +43,14 @@ export class BalanceSheetStatementController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the json table format.
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
if (accept.includes(AcceptType.ApplicationJsonTable)) {
const table = await this.balanceSheetApp.table(query);
return table;
// Retrieves the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.balanceSheetApp.csv(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -57,7 +58,7 @@ export class BalanceSheetStatementController {
res.send(buffer);
// Retrieves the xlsx format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.balanceSheetApp.xlsx(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -67,7 +68,7 @@ export class BalanceSheetStatementController {
);
res.send(buffer);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.balanceSheetApp.pdf(query);
res.set({

View File

@@ -16,7 +16,7 @@ import { ApiCommonHeaders } from '@/common/decorators/ApiCommonHeaders';
@ApiTags('Reports')
@ApiCommonHeaders()
export class CashflowController {
constructor(private readonly cashflowSheetApp: CashflowSheetApplication) {}
constructor(private readonly cashflowSheetApp: CashflowSheetApplication) { }
@Get()
@ApiResponse({
@@ -37,11 +37,12 @@ export class CashflowController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the json table format.
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.cashflowSheetApp.table(query);
// Retrieves the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.cashflowSheetApp.csv(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -49,7 +50,7 @@ export class CashflowController {
res.status(200).send(buffer);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.cashflowSheetApp.xlsx(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -59,7 +60,7 @@ export class CashflowController {
);
res.send(buffer);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.cashflowSheetApp.pdf(query);
res.set({

View File

@@ -34,8 +34,9 @@ export class CustomerBalanceSummaryController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the xlsx format.
if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.customerBalanceSummaryApp.xlsx(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
res.setHeader(
@@ -44,18 +45,18 @@ export class CustomerBalanceSummaryController {
);
res.send(buffer);
// Retrieves the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.customerBalanceSummaryApp.csv(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
res.setHeader('Content-Type', 'text/csv');
res.send(buffer);
// Retrieves the json table format.
} else if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
} else if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.customerBalanceSummaryApp.table(filter);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const buffer = await this.customerBalanceSummaryApp.pdf(filter);
res.set({

View File

@@ -39,11 +39,12 @@ export class GeneralLedgerController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the table format.
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.generalLedgerApplication.table(query);
// Retrieves the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.generalLedgerApplication.csv(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -51,7 +52,7 @@ export class GeneralLedgerController {
res.send(buffer);
// Retrieves the xlsx format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.generalLedgerApplication.xlsx(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -61,7 +62,7 @@ export class GeneralLedgerController {
);
res.send(buffer);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.generalLedgerApplication.pdf(query);
res.set({
'Content-Type': 'application/pdf',

View File

@@ -21,7 +21,8 @@ export class InventoryItemDetailsController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
const accept = acceptHeader || '';
if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.inventoryItemDetailsApp.csv(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -29,7 +30,7 @@ export class InventoryItemDetailsController {
res.send(buffer);
// Retrieves the xlsx format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.inventoryItemDetailsApp.xlsx(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -39,10 +40,10 @@ export class InventoryItemDetailsController {
);
res.send(buffer);
// Retrieves the json table format.
} else if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
} else if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.inventoryItemDetailsApp.table(query);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const buffer = await this.inventoryItemDetailsApp.pdf(query);
res.set({

View File

@@ -37,11 +37,12 @@ export class InventoryValuationController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the json table format.
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.inventoryValuationApp.table(query);
// Retrieves the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.inventoryValuationApp.csv(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -49,7 +50,7 @@ export class InventoryValuationController {
res.send(buffer);
// Retrieves the xslx buffer format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.inventoryValuationApp.xlsx(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -59,7 +60,7 @@ export class InventoryValuationController {
);
res.send(buffer);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.inventoryValuationApp.pdf(query);
res.set({

View File

@@ -37,12 +37,13 @@ export class JournalSheetController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the json table format.
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.journalSheetApp.table(query);
// Retrieves the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.journalSheetApp.csv(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -50,7 +51,7 @@ export class JournalSheetController {
res.send(buffer);
// Retrieves the xlsx format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.journalSheetApp.xlsx(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -60,7 +61,7 @@ export class JournalSheetController {
);
res.send(buffer);
// Retrieves the json format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.journalSheetApp.pdf(query);
res.set({

View File

@@ -45,8 +45,9 @@ export class ProfitLossSheetController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the csv format.
if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
if (accept.includes(AcceptType.ApplicationCsv)) {
const sheet = await this.profitLossSheetApp.csv(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -54,11 +55,11 @@ export class ProfitLossSheetController {
res.send(sheet);
// Retrieves the json table format.
} else if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
} else if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.profitLossSheetApp.table(query);
// Retrieves the xlsx format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const sheet = await this.profitLossSheetApp.xlsx(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -68,7 +69,7 @@ export class ProfitLossSheetController {
);
res.send(sheet);
// Retrieves the json format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.profitLossSheetApp.pdf(query);
res.set({

View File

@@ -22,11 +22,12 @@ export class PurchasesByItemReportController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// JSON table response format.
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.purchasesByItemsApp.table(filter);
// CSV response format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.purchasesByItemsApp.csv(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -34,7 +35,7 @@ export class PurchasesByItemReportController {
res.send(buffer);
// Xlsx response format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.purchasesByItemsApp.xlsx(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -44,7 +45,7 @@ export class PurchasesByItemReportController {
);
res.send(buffer);
// PDF response format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.purchasesByItemsApp.pdf(filter);
res.set({

View File

@@ -31,8 +31,9 @@ export class SalesByItemsController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the csv format.
if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.salesByItemsApp.csv(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -40,10 +41,10 @@ export class SalesByItemsController {
res.send(buffer);
// Retrieves the json table format.
} else if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
} else if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.salesByItemsApp.table(filter);
// Retrieves the xlsx format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = this.salesByItemsApp.xlsx(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -53,7 +54,7 @@ export class SalesByItemsController {
);
res.send(buffer);
// Retrieves the json format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.salesByItemsApp.pdf(filter);
res.set({

View File

@@ -37,11 +37,12 @@ export class SalesTaxLiabilitySummaryController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the json table format.
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.salesTaxLiabilitySummaryApp.table(query);
// Retrieves the xlsx format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.salesTaxLiabilitySummaryApp.xlsx(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -51,14 +52,14 @@ export class SalesTaxLiabilitySummaryController {
);
res.send(buffer);
// Retrieves the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.salesTaxLiabilitySummaryApp.csv(query);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
res.setHeader('Content-Type', 'text/csv');
res.send(buffer);
// Retrieves the json format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.salesTaxLiabilitySummaryApp.pdf(query);
res.set({
'Content-Type': 'application/pdf',

View File

@@ -9,17 +9,18 @@ import { TaxRateModel } from '@/modules/TaxRates/models/TaxRate.model';
import { AccountTransaction } from '@/modules/Accounts/models/AccountTransaction.model';
import { Account } from '@/modules/Accounts/models/Account.model';
import { ModelObject } from 'objection';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable({ scope: Scope.TRANSIENT })
export class SalesTaxLiabilitySummaryRepository {
@Inject(TaxRateModel.name)
private readonly taxRateModel: typeof TaxRateModel;
private readonly taxRateModel: TenantModelProxy<typeof TaxRateModel>;
@Inject(AccountTransaction.name)
private readonly accountTransactionModel: typeof AccountTransaction;
private readonly accountTransactionModel: TenantModelProxy<typeof AccountTransaction>;
@Inject(Account.name)
private readonly accountModel: typeof Account;
private readonly accountModel: TenantModelProxy<typeof Account>;
/**
* @param {SalesTaxLiabilitySummarySalesById}
@@ -77,7 +78,7 @@ export class SalesTaxLiabilitySummaryRepository {
* @returns {Promise<TaxRate[]>}
*/
public getTaxRates = () => {
return this.taxRateModel.query().orderBy('name', 'desc');
return this.taxRateModel().query().orderBy('name', 'desc');
};
/**
@@ -86,13 +87,13 @@ export class SalesTaxLiabilitySummaryRepository {
*/
public async getTaxesPayableSumGroupedByRateId(): Promise<SalesTaxLiabilitySummaryPayableById> {
// Retrieves tax payable accounts.
const taxPayableAccounts = await this.accountModel
const taxPayableAccounts = await this.accountModel()
.query()
.whereIn('accountType', [ACCOUNT_TYPE.TAX_PAYABLE]);
const payableAccountsIds = taxPayableAccounts.map((account) => account.id);
const groupedTaxesById = await this.accountTransactionModel
const groupedTaxesById = await this.accountTransactionModel()
.query()
.whereIn('account_id', payableAccountsIds)
.whereNot('tax_rate_id', null)
@@ -110,7 +111,7 @@ export class SalesTaxLiabilitySummaryRepository {
*/
public taxesSalesSumGroupedByRateId =
async (): Promise<SalesTaxLiabilitySummarySalesById> => {
const incomeAccounts = await this.accountModel
const incomeAccounts = await this.accountModel()
.query()
.whereIn('accountType', [
ACCOUNT_TYPE.INCOME,
@@ -118,7 +119,7 @@ export class SalesTaxLiabilitySummaryRepository {
]);
const incomeAccountsIds = incomeAccounts.map((account) => account.id);
const groupedTaxesById = await this.accountTransactionModel
const groupedTaxesById = await this.accountTransactionModel()
.query()
.whereIn('account_id', incomeAccountsIds)
.whereNot('tax_rate_id', null)

View File

@@ -23,12 +23,13 @@ export class TransactionsByCustomerController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the json table format.
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.transactionsByCustomersApp.table(filter);
// Retrieve the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const csv = await this.transactionsByCustomersApp.csv(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -37,7 +38,7 @@ export class TransactionsByCustomerController {
res.send(csv);
// Retrieve the xlsx format.
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.transactionsByCustomersApp.xlsx(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
res.setHeader(
@@ -47,7 +48,7 @@ export class TransactionsByCustomerController {
res.send(buffer);
// Retrieve the json format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.transactionsByCustomersApp.pdf(filter);
res.set({
'Content-Type': 'application/pdf',

View File

@@ -24,6 +24,7 @@ export class TransactionsByReferenceRepository {
): Promise<Array<ModelObject<AccountTransaction>>> {
return this.accountTransactionModel()
.query()
.skipUndefined()
.where('reference_id', referenceId)
.where('reference_type', referenceType)
.withGraphFetched('account');

View File

@@ -23,8 +23,9 @@ export class TransactionsByVendorController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the xlsx format.
if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.transactionsByVendorsApp.xlsx(filter);
res.setHeader('Content-Type', 'application/vnd.openxmlformats');
@@ -32,7 +33,7 @@ export class TransactionsByVendorController {
res.send(buffer);
// Retrieves the csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.transactionsByVendorsApp.csv(filter);
res.setHeader('Content-Type', 'text/csv');
@@ -40,10 +41,10 @@ export class TransactionsByVendorController {
res.send(buffer);
// Retrieves the json table format.
} else if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
} else if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.transactionsByVendorsApp.table(filter);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.transactionsByVendorsApp.pdf(filter);
res.set({
'Content-Type': 'application/pdf',

View File

@@ -40,15 +40,16 @@ export class TrialBalanceSheetController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
const filter = {
...query,
accountIds: castArray(query.accountIds),
};
// Retrieves in json table format.
if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.trialBalanceSheetApp.table(filter);
// Retrieves in xlsx format
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.trialBalanceSheetApp.xlsx(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -58,7 +59,7 @@ export class TrialBalanceSheetController {
);
res.send(buffer);
// Retrieves in csv format.
} else if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
} else if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.trialBalanceSheetApp.csv(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
@@ -66,7 +67,7 @@ export class TrialBalanceSheetController {
res.send(buffer);
// Retrieves in pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.trialBalanceSheetApp.pdf(filter);
res.set({

View File

@@ -35,15 +35,16 @@ export class VendorBalanceSummaryController {
@Res({ passthrough: true }) res: Response,
@Headers('accept') acceptHeader: string,
) {
const accept = acceptHeader || '';
// Retrieves the csv format.
if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
if (accept.includes(AcceptType.ApplicationCsv)) {
const buffer = await this.vendorBalanceSummaryApp.csv(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
res.setHeader('Content-Type', 'text/csv');
res.send(buffer);
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
} else if (accept.includes(AcceptType.ApplicationXlsx)) {
const buffer = await this.vendorBalanceSummaryApp.xlsx(filter);
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
@@ -52,10 +53,10 @@ export class VendorBalanceSummaryController {
res.send(buffer);
// Retrieves the json table format.
} else if (acceptHeader.includes(AcceptType.ApplicationJsonTable)) {
} else if (accept.includes(AcceptType.ApplicationJsonTable)) {
return this.vendorBalanceSummaryApp.table(filter);
// Retrieves the pdf format.
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
} else if (accept.includes(AcceptType.ApplicationPdf)) {
const pdfContent = await this.vendorBalanceSummaryApp.pdf(filter);
res.set({

View File

@@ -25,7 +25,7 @@ export class DeleteItemService {
@Inject(Item.name)
private readonly itemModel: TenantModelProxy<typeof Item>,
) {}
) { }
/**
* Delete the given item from the storage.

View File

@@ -8,6 +8,7 @@ import {
Get,
Put,
Query,
HttpCode,
} from '@nestjs/common';
import { TenantController } from '../Tenancy/Tenant.controller';
import { ItemsApplicationService } from './ItemsApplication.service';
@@ -156,9 +157,10 @@ export class ItemsController extends TenantController {
async editItem(
@Param('id') id: string,
@Body() editItemDto: EditItemDto,
): Promise<number> {
): Promise<{ id: number; message: string }> {
const itemId = parseInt(id, 10);
return this.itemsApplication.editItem(itemId, editItemDto);
await this.itemsApplication.editItem(itemId, editItemDto);
return { id: itemId, message: 'The item has been successfully updated.' };
}
@Post()
@@ -168,8 +170,12 @@ export class ItemsController extends TenantController {
description: 'The item has been successfully created.',
})
// @UsePipes(new ZodValidationPipe(createItemSchema))
async createItem(@Body() createItemDto: CreateItemDto): Promise<number> {
return this.itemsApplication.createItem(createItemDto);
async createItem(
@Body() createItemDto: CreateItemDto,
): Promise<{ id: number; message: string }> {
const itemId = await this.itemsApplication.createItem(createItemDto);
return { id: itemId, message: 'The item has been successfully created.' };
}
@Delete(':id')
@@ -347,6 +353,7 @@ export class ItemsController extends TenantController {
}
@Post('validate-bulk-delete')
@HttpCode(200)
@ApiOperation({
summary:
'Validates which items can be deleted and returns counts of deletable and non-deletable items.',

View File

@@ -28,7 +28,7 @@ export class ItemsEntriesService {
@Inject(ItemEntry.name)
private readonly itemEntryModel: TenantModelProxy<typeof ItemEntry>,
) {}
) { }
/**
* Retrieve the inventory items entries of the reference id and type.
@@ -226,6 +226,10 @@ export class ItemsEntriesService {
return entries.map((entry) => {
const item = items.find((i) => i.id === entry.itemId);
if (!item) {
throw new Error(`Item with id ${entry.itemId} not found`);
}
return {
...entry,
sellAccountId: entry.sellAccountId || item.sellAccountId,

View File

@@ -7,12 +7,12 @@ import {
} from './init-app-test';
const makeAccountRequest = () => ({
name: faker.finance.accountName(),
accountType: 'asset',
name: `${faker.finance.accountName()} ${Date.now()}-${faker.string.alphanumeric({ length: 4 })}`,
accountType: 'cash',
code: faker.string.alphanumeric({ length: 6 }).toUpperCase(),
});
describe('Accounts (e2e)', () => {
describe.only('Accounts (e2e)', () => {
it('/accounts (POST)', () => {
return request(app.getHttpServer())
.post('/accounts')
@@ -35,7 +35,9 @@ describe('Accounts (e2e)', () => {
.post('/accounts')
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.send(makeAccountRequest());
.send(makeAccountRequest())
.expect(201);
const accountId = response.body.id;
return request(app.getHttpServer())
@@ -50,17 +52,15 @@ describe('Accounts (e2e)', () => {
.post('/accounts')
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.send(makeAccountRequest());
.send(makeAccountRequest())
.expect(201);
const accountId = response.body.id;
return request(app.getHttpServer())
.put(`/accounts/${accountId}`)
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.send({
name: faker.finance.accountName(),
accountType: 'asset',
})
.send(makeAccountRequest())
.expect(200);
});
@@ -69,7 +69,8 @@ describe('Accounts (e2e)', () => {
.post('/accounts')
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.send(makeAccountRequest());
.send(makeAccountRequest())
.expect(201);
const accountId = response.body.id;
return request(app.getHttpServer())
@@ -87,7 +88,8 @@ describe('Accounts (e2e)', () => {
.send({
...makeAccountRequest(),
active: false,
});
})
.expect(201);
const accountId = response.body.id;
return request(app.getHttpServer())
@@ -105,7 +107,8 @@ describe('Accounts (e2e)', () => {
.send({
...makeAccountRequest(),
active: true,
});
})
.expect(201);
const accountId = response.body.id;
return request(app.getHttpServer())
@@ -128,14 +131,16 @@ describe('Accounts (e2e)', () => {
.post('/accounts')
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.send(makeAccountRequest());
.send(makeAccountRequest())
.expect(201);
const accountId1 = response1.body.id;
const response2 = await request(app.getHttpServer())
.post('/accounts')
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.send(makeAccountRequest());
.send(makeAccountRequest())
.expect(201);
const accountId2 = response2.body.id;
return request(app.getHttpServer())
@@ -153,14 +158,16 @@ describe('Accounts (e2e)', () => {
.post('/accounts')
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.send(makeAccountRequest());
.send(makeAccountRequest())
.expect(201);
const accountId1 = response1.body.id;
const response2 = await request(app.getHttpServer())
.post('/accounts')
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.send(makeAccountRequest());
.send(makeAccountRequest())
.expect(201);
const accountId2 = response2.body.id;
return request(app.getHttpServer())

View File

@@ -2,19 +2,19 @@ import * as request from 'supertest';
import { app, AuthorizationHeader, orgainzationId } from './init-app-test';
describe('Banking Accounts (e2e)', () => {
it('/banking/accounts (GET)', () => {
return request(app.getHttpServer())
.get('/banking/accounts')
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.expect(200);
});
// it('/banking/accounts (GET)', () => {
// return request(app.getHttpServer())
// .get('/banking/accounts')
// .set('organization-id', orgainzationId)
// .set('Authorization', AuthorizationHeader)
// .expect(200);
// });
it('/banking/accounts/:bankAccountId/summary (GET)', () => {
return request(app.getHttpServer())
.get('/banking/accounts/1/summary')
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.expect(200);
});
// it('/banking/accounts/:bankAccountId/summary (GET)', () => {
// return request(app.getHttpServer())
// .get('/banking/accounts/1/summary')
// .set('organization-id', orgainzationId)
// .set('Authorization', AuthorizationHeader)
// .expect(200);
// });
});

View File

@@ -32,17 +32,17 @@ describe('Contacts (e2e)', () => {
.expect(200);
});
it('/contacts/:id/activate (POST)', () => {
it('/contacts/:id/activate (PATCH)', () => {
return request(app.getHttpServer())
.post(`/contacts/${customerId}/activate`)
.patch(`/contacts/${customerId}/activate`)
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.expect(200);
});
it('/contacts/:id/inactivate (POST)', () => {
it('/contacts/:id/inactivate (PATCH)', () => {
return request(app.getHttpServer())
.post(`/contacts/${vendorId}/inactivate`)
.patch(`/contacts/${vendorId}/inactivate`)
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.expect(200);

View File

@@ -5,10 +5,10 @@ import { AppModule } from '../src/modules/App/App.module';
let app: INestApplication;
const email = 'ahmed@sa1234dsad.com';
const email = 'big@big.com';
const password = '123123123';
let orgainzationId = 'hpgpqfqom8zic574';
let orgainzationId = '';
let authenticationToken = '';
let AuthorizationHeader = '';
@@ -22,11 +22,13 @@ beforeAll(async () => {
const signinResponse = await request(app.getHttpServer())
.post('/auth/signin')
.send({ email, password })
.expect(201);
.send({ email, password });
console.log(signinResponse.body);
authenticationToken = signinResponse.body.access_token;
AuthorizationHeader = `Bearer ${authenticationToken}`;
orgainzationId = signinResponse.body.organization_id;
});
afterAll(async () => {

View File

@@ -12,12 +12,12 @@ const makeItemRequest = () => ({
type: 'service',
});
describe('Items (e2e)', () => {
describe.only('Items (e2e)', () => {
it('/items (POST)', () => {
return request(app.getHttpServer())
.post('/items')
.set('organization-id', orgainzationId)
.set('Authorization', `Bearer ${authenticationToken}`)
.set('Authorization', AuthorizationHeader)
.send(makeItemRequest())
.expect(201);
});
@@ -28,6 +28,7 @@ describe('Items (e2e)', () => {
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.send(makeItemRequest());
const itemId = response.body.id;
return request(app.getHttpServer())

View File

@@ -42,7 +42,7 @@ describe('Sale Invoices (e2e)', () => {
.set('organization-id', orgainzationId)
.set('Authorization', AuthorizationHeader)
.send({
name: faker.commerce.productName(),
name: `${faker.commerce.productName()} ${Date.now()}-${faker.string.alphanumeric({ length: 4 })}`,
sellable: true,
purchasable: true,
sellAccountId: 1026,

View File

@@ -52,7 +52,7 @@ export function useActivateContact(props) {
const queryClient = useQueryClient();
const apiRequest = useApiRequest();
return useMutation((id) => apiRequest.post(`contacts/${id}/activate`), {
return useMutation((id) => apiRequest.patch(`contacts/${id}/activate`), {
onSuccess: (res, id) => {
// Invalidate specific contact.
queryClient.invalidateQueries([t.CONTACT, id]);
@@ -71,7 +71,7 @@ export function useInactivateContact(props) {
const queryClient = useQueryClient();
const apiRequest = useApiRequest();
return useMutation((id) => apiRequest.post(`contacts/${id}/inactivate`), {
return useMutation((id) => apiRequest.patch(`contacts/${id}/inactivate`), {
onSuccess: (res, id) => {
// Invalidate specific item.
queryClient.invalidateQueries([t.CONTACT, id]);