refactor: migrate to Nestjs

This commit is contained in:
Ahmed Bouhuolia
2024-12-30 15:54:53 +02:00
parent 77bbf6828d
commit 515a984714
23 changed files with 557 additions and 175 deletions

View File

@@ -1,8 +1,8 @@
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { ClsMiddleware } from 'nestjs-cls';
import { AppModule } from './modules/App/App.module';
import './utils/moment-mysql';
import { AppModule } from './modules/App/App.module';
import { ServiceErrorFilter } from './common/filters/service-error.filter';
async function bootstrap() {

View File

@@ -4,8 +4,10 @@ import {
ICreditNoteEditDTO,
ICreditNoteNewDTO,
} from './types/CreditNotes.types';
import { PublicRoute } from '../Auth/Jwt.guard';
@Controller('credit-notes')
@PublicRoute()
export class CreditNotesController {
/**
* @param {CreditNoteApplication} creditNoteApplication - The credit note application service.
@@ -28,7 +30,7 @@ export class CreditNotesController {
);
}
@Post(':id/open')
@Put(':id/open')
openCreditNote(@Param('id') creditNoteId: number) {
return this.creditNoteApplication.openCreditNote(creditNoteId);
}

View File

@@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common';
import moment from 'moment';
import { omit } from 'lodash';
import * as moment from 'moment';
import * as composeAsync from 'async/compose';
import * as R from 'ramda';
import composeAsync from 'async/compose';
import { ERRORS } from '../constants';
import {
ICreditNoteEditDTO,

View File

@@ -287,7 +287,7 @@ export class CreditNote extends BaseModel {
const { Branch } = require('../../Branches/models/Branch.model');
const { Document } = require('../../ChromiumlyTenancy/models/Document');
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
const { PdfTemplate } = require('../../PdfTemplate/models/PdfTemplate');
const { PdfTemplateModel } = require('../../PdfTemplate/models/PdfTemplate');
return {
/**
@@ -384,7 +384,7 @@ export class CreditNote extends BaseModel {
*/
pdfTemplate: {
relation: Model.BelongsToOneRelation,
modelClass: PdfTemplate,
modelClass: PdfTemplateModel,
join: {
from: 'credit_notes.pdfTemplateId',
to: 'pdf_templates.id',

View File

@@ -117,121 +117,121 @@ export class ItemEntry extends BaseModel {
/**
* Item entry relations.
*/
// static get relationMappings() {
// const Item = require('models/Item');
// const BillLandedCostEntry = require('models/BillLandedCostEntry');
// const SaleInvoice = require('models/SaleInvoice');
// const Bill = require('models/Bill');
// const SaleReceipt = require('models/SaleReceipt');
// const SaleEstimate = require('models/SaleEstimate');
// const ProjectTask = require('models/Task');
// const Expense = require('models/Expense');
// const TaxRate = require('models/TaxRate');
static get relationMappings() {
const { Item } = require('../../Items/models/Item');
// const BillLandedCostEntry = require('models/BillLandedCostEntry');
const { SaleInvoice } = require('../../SaleInvoices/models/SaleInvoice');
const { Bill } = require('../../Bills/models/Bill');
const { SaleReceipt } = require('../../SaleReceipts/models/SaleReceipt');
const { SaleEstimate } = require('../../SaleEstimates/models/SaleEstimate');
// const ProjectTask = require('models/Task');
const { Expense } = require('../../Expenses/models/Expense.model');
const { TaxRateModel } = require('../../TaxRates/models/TaxRate.model');
// return {
// item: {
// relation: Model.BelongsToOneRelation,
// modelClass: Item.default,
// join: {
// from: 'items_entries.itemId',
// to: 'items.id',
// },
// },
// allocatedCostEntries: {
// relation: Model.HasManyRelation,
// modelClass: BillLandedCostEntry.default,
// join: {
// from: 'items_entries.referenceId',
// to: 'bill_located_cost_entries.entryId',
// },
// },
return {
item: {
relation: Model.BelongsToOneRelation,
modelClass: Item,
join: {
from: 'items_entries.itemId',
to: 'items.id',
},
},
// allocatedCostEntries: {
// relation: Model.HasManyRelation,
// modelClass: BillLandedCostEntry,
// join: {
// from: 'items_entries.referenceId',
// to: 'bill_located_cost_entries.entryId',
// },
// },
// invoice: {
// relation: Model.BelongsToOneRelation,
// modelClass: SaleInvoice.default,
// join: {
// from: 'items_entries.referenceId',
// to: 'sales_invoices.id',
// },
// },
invoice: {
relation: Model.BelongsToOneRelation,
modelClass: SaleInvoice,
join: {
from: 'items_entries.referenceId',
to: 'sales_invoices.id',
},
},
// bill: {
// relation: Model.BelongsToOneRelation,
// modelClass: Bill.default,
// join: {
// from: 'items_entries.referenceId',
// to: 'bills.id',
// },
// },
bill: {
relation: Model.BelongsToOneRelation,
modelClass: Bill,
join: {
from: 'items_entries.referenceId',
to: 'bills.id',
},
},
// estimate: {
// relation: Model.BelongsToOneRelation,
// modelClass: SaleEstimate.default,
// join: {
// from: 'items_entries.referenceId',
// to: 'sales_estimates.id',
// },
// },
estimate: {
relation: Model.BelongsToOneRelation,
modelClass: SaleEstimate,
join: {
from: 'items_entries.referenceId',
to: 'sales_estimates.id',
},
},
// /**
// * Sale receipt reference.
// */
// receipt: {
// relation: Model.BelongsToOneRelation,
// modelClass: SaleReceipt.default,
// join: {
// from: 'items_entries.referenceId',
// to: 'sales_receipts.id',
// },
// },
/**
* Sale receipt reference.
*/
receipt: {
relation: Model.BelongsToOneRelation,
modelClass: SaleReceipt,
join: {
from: 'items_entries.referenceId',
to: 'sales_receipts.id',
},
},
// /**
// * Project task reference.
// */
// projectTaskRef: {
// relation: Model.HasManyRelation,
// modelClass: ProjectTask.default,
// join: {
// from: 'items_entries.projectRefId',
// to: 'tasks.id',
// },
// },
/**
* Project task reference.
*/
// projectTaskRef: {
// relation: Model.HasManyRelation,
// modelClass: ProjectTask.default,
// join: {
// from: 'items_entries.projectRefId',
// to: 'tasks.id',
// },
// },
// /**
// * Project expense reference.
// */
// projectExpenseRef: {
// relation: Model.HasManyRelation,
// modelClass: Expense.default,
// join: {
// from: 'items_entries.projectRefId',
// to: 'expenses_transactions.id',
// },
// },
/**
* Project expense reference.
*/
// projectExpenseRef: {
// relation: Model.HasManyRelation,
// modelClass: Expense.default,
// join: {
// from: 'items_entries.projectRefId',
// to: 'expenses_transactions.id',
// },
// },
// /**
// * Project bill reference.
// */
// projectBillRef: {
// relation: Model.HasManyRelation,
// modelClass: Bill.default,
// join: {
// from: 'items_entries.projectRefId',
// to: 'bills.id',
// },
// },
/**
* Project bill reference.
*/
// projectBillRef: {
// relation: Model.HasManyRelation,
// modelClass: Bill.default,
// join: {
// from: 'items_entries.projectRefId',
// to: 'bills.id',
// },
// },
// /**
// * Tax rate reference.
// */
// tax: {
// relation: Model.HasOneRelation,
// modelClass: TaxRate.default,
// join: {
// from: 'items_entries.taxRateId',
// to: 'tax_rates.id',
// },
// },
// };
// }
/**
* Tax rate reference.
*/
tax: {
relation: Model.HasOneRelation,
modelClass: TaxRateModel,
join: {
from: 'items_entries.taxRateId',
to: 'tax_rates.id',
},
},
};
}
}

View File

@@ -3,6 +3,7 @@ import {
Controller,
Delete,
Get,
HttpCode,
Param,
ParseIntPipe,
Post,
@@ -57,6 +58,7 @@ export class SaleInvoicesController {
}
@Post(':id/deliver')
@HttpCode(200)
deliverSaleInvoice(@Param('id', ParseIntPipe) id: number) {
return this.saleInvoiceApplication.deliverSaleInvoice(id);
}
@@ -67,6 +69,7 @@ export class SaleInvoicesController {
}
@Post(':id/writeoff')
@HttpCode(200)
writeOff(
@Param('id', ParseIntPipe) id: number,
@Body() writeoffDTO: ISaleInvoiceWriteoffDTO,
@@ -75,6 +78,7 @@ export class SaleInvoicesController {
}
@Post(':id/cancel-writeoff')
@HttpCode(200)
cancelWrittenoff(@Param('id', ParseIntPipe) id: number) {
return this.saleInvoiceApplication.cancelWrittenoff(id);
}

View File

@@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { omit, sumBy } from 'lodash';
import * as R from 'ramda';
import * as moment from 'moment';
import '../../../utils/moment-mysql';
import * as composeAsync from 'async/compose';
import {
ISaleInvoiceCreateDTO,
@@ -23,6 +24,17 @@ import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
@Injectable()
export class CommandSaleInvoiceDTOTransformer {
/**
* @param {BranchTransactionDTOTransformer} branchDTOTransform - Branch transaction DTO transformer.
* @param {WarehouseTransactionDTOTransform} warehouseDTOTransform - Warehouse transaction DTO transformer.
* @param {ItemsEntriesService} itemsEntriesService - Items entries service.
* @param {CommandSaleInvoiceValidators} validators - Command sale invoice validators.
* @param {SaleInvoiceIncrement} invoiceIncrement - Sale invoice increment.
* @param {ItemEntriesTaxTransactions} taxDTOTransformer - Item entries tax transactions.
* @param {BrandingTemplateDTOTransformer} brandingTemplatesTransformer - Branding template DTO transformer.
* @param {TenancyContext} tenancyContext - Tenancy context.
* @param {SaleInvoice} saleInvoiceModel - Sale invoice model.
*/
constructor(
private branchDTOTransform: BranchTransactionDTOTransformer,
private warehouseDTOTransform: WarehouseTransactionDTOTransform,

View File

@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import moment from 'moment';
import * as moment from 'moment';
import {
ISaleInvoiceDeliveringPayload,
ISaleInvoiceEventDeliveredPayload,

View File

@@ -1,6 +1,7 @@
import { mixin, Model, raw } from 'objection';
import { castArray, takeWhile } from 'lodash';
import moment, { MomentInput, unitOfTime } from 'moment';
import * as moment from 'moment';
import { MomentInput, unitOfTime } from 'moment';
// import TenantModel from 'models/TenantModel';
// import ModelSetting from './ModelSetting';
// import SaleInvoiceMeta from './SaleInvoice.Settings';
@@ -8,6 +9,9 @@ import moment, { MomentInput, unitOfTime } from 'moment';
// import { DEFAULT_VIEWS } from '@/services/Sales/Invoices/constants';
// import ModelSearchable from './ModelSearchable';
import { BaseModel } from '@/models/Model';
import { TaxRateTransaction } from '@/modules/TaxRates/models/TaxRateTransaction.model';
import { ItemEntry } from '@/modules/Items/models/ItemEntry';
import { Document } from '@/modules/ChromiumlyTenancy/models/Document';
export class SaleInvoice extends BaseModel {
public taxAmountWithheld: number;
@@ -39,7 +43,9 @@ export class SaleInvoice extends BaseModel {
public branchId: number;
public warehouseId: number;
// public taxes: TaxRateTransaction[];
public taxes: TaxRateTransaction[];
public entries: ItemEntry[];
public attachments: Document[];
/**
* Table name
@@ -438,7 +444,7 @@ export class SaleInvoice extends BaseModel {
const { Branch } = require('../../Branches/models/Branch.model');
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
const { Account } = require('../../Accounts/models/Account.model');
// const TaxRateTransaction = require('../../Tax');
const { TaxRateTransaction } = require('../../TaxRates/models/TaxRateTransaction.model');
const { Document } = require('../../ChromiumlyTenancy/models/Document');
// const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
const {
@@ -561,17 +567,17 @@ export class SaleInvoice extends BaseModel {
/**
* Invoice may has associated tax rate transactions.
*/
// taxes: {
// relation: Model.HasManyRelation,
// modelClass: TaxRateTransaction.default,
// join: {
// from: 'sales_invoices.id',
// to: 'tax_rate_transactions.referenceId',
// },
// filter(builder) {
// builder.where('reference_type', 'SaleInvoice');
// },
// },
taxes: {
relation: Model.HasManyRelation,
modelClass: TaxRateTransaction,
join: {
from: 'sales_invoices.id',
to: 'tax_rate_transactions.referenceId',
},
filter(builder) {
builder.where('reference_type', 'SaleInvoice');
},
},
/**
* Sale invoice transaction may has many attached attachments.

View File

@@ -184,31 +184,31 @@ export class SaleInvoiceTransformer extends Transformer {
* Retrieve the taxes lines of sale invoice.
* @param {ISaleInvoice} invoice
*/
// protected taxes = (invoice: SaleInvoice) => {
// return this.item(invoice.taxes, new SaleInvoiceTaxEntryTransformer(), {
// subtotal: invoice.subtotal,
// isInclusiveTax: invoice.isInclusiveTax,
// currencyCode: invoice.currencyCode,
// });
// };
protected taxes = (invoice: SaleInvoice) => {
return this.item(invoice.taxes, new SaleInvoiceTaxEntryTransformer(), {
subtotal: invoice.subtotal,
isInclusiveTax: invoice.isInclusiveTax,
currencyCode: invoice.currencyCode,
});
};
/**
* Retrieves the entries of the sale invoice.
* @param {ISaleInvoice} invoice
* @returns {}
*/
// protected entries = (invoice: SaleInvoice) => {
// return this.item(invoice.entries, new ItemEntryTransformer(), {
// currencyCode: invoice.currencyCode,
// });
// };
protected entries = (invoice: SaleInvoice) => {
return this.item(invoice.entries, new ItemEntryTransformer(), {
currencyCode: invoice.currencyCode,
});
};
/**
* Retrieves the sale invoice attachments.
* @param {ISaleInvoice} invoice
* @returns
*/
// protected attachments = (invoice: SaleInvoice) => {
// return this.item(invoice.attachments, new AttachmentTransformer());
// };
protected attachments = (invoice: SaleInvoice) => {
return this.item(invoice.attachments, new AttachmentTransformer());
};
}

View File

@@ -0,0 +1,55 @@
import { mixin, Model, raw } from 'objection';
// import TenantModel from 'models/TenantModel';
// import ModelSearchable from './ModelSearchable';
import { BaseModel } from '@/models/Model';
export class TaxRateTransaction extends BaseModel {
/**
* Table name
*/
static get tableName() {
return 'tax_rate_transactions';
}
/**
* Timestamps columns.
*/
get timestamps() {
return [];
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return [];
}
/**
* Model modifiers.
*/
static get modifiers() {
return {};
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const { TaxRateModel } = require('./TaxRate.model');
return {
/**
* Belongs to the tax rate.
*/
taxRate: {
relation: Model.BelongsToOneRelation,
modelClass: TaxRateModel,
join: {
from: 'tax_rate_transactions.taxRateId',
to: 'tax_rates.id',
},
},
};
}
}

View File

@@ -1,8 +1,21 @@
import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common';
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
} from '@nestjs/common';
import { VendorCreditsApplicationService } from './VendorCreditsApplication.service';
import { IVendorCreditCreateDTO, IVendorCreditEditDTO } from './types/VendorCredit.types';
import {
IVendorCreditCreateDTO,
IVendorCreditEditDTO,
} from './types/VendorCredit.types';
import { PublicRoute } from '../Auth/Jwt.guard';
@Controller('vendor-credits')
@PublicRoute()
export class VendorCreditsController {
constructor(
private readonly vendorCreditsApplication: VendorCreditsApplicationService,
@@ -13,7 +26,12 @@ export class VendorCreditsController {
return this.vendorCreditsApplication.createVendorCredit(dto);
}
@Put(':id')
@Put(':id/open')
async openVendorCredit(@Param('id') vendorCreditId: number) {
return this.vendorCreditsApplication.openVendorCredit(vendorCreditId);
}
@Put(':id')
async editVendorCredit(
@Param('id') vendorCreditId: number,
@Body() dto: IVendorCreditEditDTO,

View File

@@ -15,6 +15,7 @@ import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module';
import { BranchesModule } from '../Branches/Branches.module';
import { WarehousesModule } from '../Warehouses/Warehouses.module';
import { VendorCreditsApplicationService } from './VendorCreditsApplication.service';
import { OpenVendorCreditService } from './commands/OpenVendorCredit.service';
@Module({
imports: [
@@ -35,6 +36,7 @@ import { VendorCreditsApplicationService } from './VendorCreditsApplication.serv
GetRefundVendorCreditService,
GetVendorCreditService,
VendorCreditsApplicationService,
OpenVendorCreditService
],
exports: [
CreateVendorCreditService,
@@ -45,6 +47,7 @@ import { VendorCreditsApplicationService } from './VendorCreditsApplication.serv
GetRefundVendorCreditService,
GetVendorCreditService,
VendorCreditsApplicationService,
OpenVendorCreditService
],
controllers: [VendorCreditsController],
})

View File

@@ -6,6 +6,7 @@ import { GetVendorCreditService } from './queries/GetVendorCredit.service';
import { IVendorCreditEditDTO } from './types/VendorCredit.types';
import { IVendorCreditCreateDTO } from './types/VendorCredit.types';
import { Injectable } from '@nestjs/common';
import { OpenVendorCreditService } from './commands/OpenVendorCredit.service';
@Injectable()
export class VendorCreditsApplicationService {
@@ -20,6 +21,7 @@ export class VendorCreditsApplicationService {
private readonly editVendorCreditService: EditVendorCreditService,
private readonly deleteVendorCreditService: DeleteVendorCreditService,
private readonly getVendorCreditService: GetVendorCreditService,
private readonly openVendorCreditService: OpenVendorCreditService,
) {}
/**
@@ -32,6 +34,15 @@ export class VendorCreditsApplicationService {
return this.createVendorCreditService.newVendorCredit(dto, trx);
}
/**
* Opens the given vendor credit.
* @param {number} vendorCreditId - The vendor credit id.
* @returns {Promise<VendorCredit>} The opened vendor credit.
*/
openVendorCredit(vendorCreditId: number) {
return this.openVendorCreditService.openVendorCredit(vendorCreditId);
}
/**
* Edits the given vendor credit.
* @param {number} vendorCreditId - The vendor credit id.

View File

@@ -10,6 +10,7 @@ import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { VendorCredit } from '../models/VendorCredit';
import { events } from '@/common/events/events';
import { ServiceError } from '@/modules/Items/ServiceError';
import { Knex } from 'knex';
@Injectable()
export class OpenVendorCreditService {
@@ -32,6 +33,7 @@ export class OpenVendorCreditService {
*/
public openVendorCredit = async (
vendorCreditId: number,
trx?: Knex.Transaction,
): Promise<VendorCredit> => {
// Retrieve the vendor credit or throw not found service error.
const oldVendorCredit = await this.vendorCreditModel
@@ -75,7 +77,7 @@ export class OpenVendorCreditService {
} as IVendorCreditOpenedPayload);
return vendorCredit;
});
}, trx);
};
/**

View File

@@ -80,10 +80,9 @@ export class VendorCreditDTOTransformService {
}),
};
return R.compose(
VendorCredit.fromJson<VendorCredit>,
this.branchDTOTransform.transformDTO<VendorCredit>,
this.warehouseDTOTransform.transformDTO<VendorCredit>,
)(initialDTO);
)(initialDTO) as VendorCredit;
};
/**

View File

@@ -292,6 +292,9 @@ export class VendorCredit extends BaseModel {
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
return {
/**
* Vendor credit may belongs to vendor.
*/
vendor: {
relation: Model.BelongsToOneRelation,
modelClass: Vendor,
@@ -304,6 +307,9 @@ export class VendorCredit extends BaseModel {
},
},
/**
* Vendor credit may has many item entries.
*/
entries: {
relation: Model.HasManyRelation,
modelClass: ItemEntry,

View File

@@ -4,8 +4,10 @@ import { Body, Controller, Delete, Param, Post } from '@nestjs/common';
import { VendorCreditsRefundApplication } from './VendorCreditsRefund.application';
import { IRefundVendorCreditDTO } from './types/VendorCreditRefund.types';
import { RefundVendorCredit } from './models/RefundVendorCredit';
import { PublicRoute } from '../Auth/Jwt.guard';
@Controller('vendor-credits')
@PublicRoute()
export class VendorCreditsRefundController {
constructor(
private readonly vendorCreditsRefundApplication: VendorCreditsRefundApplication,

View File

@@ -38,7 +38,7 @@ export class RefundVendorCredit extends BaseModel {
* Relationship mapping.
*/
static get relationMappings() {
const { VendorCredit } = require('./VendorCredit');
const { VendorCredit } = require('../../VendorCredit/models/VendorCredit');
const { Account } = require('../../Accounts/models/Account.model');
return {

View File

@@ -0,0 +1,71 @@
import * as request from 'supertest';
import { app } from './init-app-test';
const requestCreditNote = () => ({
customerId: 2,
creditNoteDate: '2020-02-02',
branchId: 1,
warehouseId: 1,
entries: [
{
index: 1,
itemId: 1000,
quantity: 1,
rate: 1000,
description: "It's description here.",
},
],
discount: '100',
discountType: 'amount',
});
describe('Credit Notes (e2e)', () => {
it('/credit-notes (POST)', () => {
return request(app.getHttpServer())
.post('/credit-notes')
.set('organization-id', '4064541lv40nhca')
.send(requestCreditNote())
.expect(201);
});
it('/credit-notes/:id (DELETE)', async () => {
const response = await request(app.getHttpServer())
.post('/credit-notes')
.set('organization-id', '4064541lv40nhca')
.send(requestCreditNote());
const creditNoteId = response.body.id;
return request(app.getHttpServer())
.delete(`/credit-notes/${creditNoteId}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/credit-notes/:id (PUT)', async () => {
const creditNote = requestCreditNote();
const response = await request(app.getHttpServer())
.post('/credit-notes')
.set('organization-id', '4064541lv40nhca')
.send(creditNote);
const creditNoteId = response.body.id;
return request(app.getHttpServer())
.put(`/credit-notes/${creditNoteId}`)
.set('organization-id', '4064541lv40nhca')
.send(creditNote)
.expect(200);
});
it('/credit-notes/:id/open (POST)', async () => {
const response = await request(app.getHttpServer())
.post('/credit-notes')
.set('organization-id', '4064541lv40nhca')
.send(requestCreditNote());
const creditNoteId = response.body.id;
return request(app.getHttpServer())
.put(`/credit-notes/${creditNoteId}/open`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
});

View File

@@ -2,32 +2,148 @@ import * as request from 'supertest';
import { faker } from '@faker-js/faker';
import { app } from './init-app-test';
const requestSaleInvoiceBody = () => ({
customerId: 2,
invoiceDate: '2023-01-01',
dueDate: '2023-02-01',
invoiceNo: faker.string.uuid(),
referenceNo: 'REF-000201',
delivered: true,
discountType: 'percentage',
discount: 10,
branchId: 1,
warehouseId: 1,
entries: [
{
index: 1,
itemId: 1001,
quantity: 2,
rate: 1000,
description: 'Item description...',
},
],
});
describe('Sale Invoices (e2e)', () => {
it('/sale-invoices (POST)', () => {
return request(app.getHttpServer())
.post('/sale-invoices')
.set('organization-id', '4064541lv40nhca')
.send({
customerId: 2,
invoiceDate: '2023-01-01',
dueDate: '2023-02-01',
invoiceNo: 'INV-002005',
referenceNo: 'REF-000201',
delivered: true,
discountType: 'percentage',
discount: 10,
branchId: 1,
warehouseId: 1,
entries: [
{
index: 1,
itemId: 1001,
quantity: 2,
rate: 1000,
description: 'Item description...',
},
],
})
.send(requestSaleInvoiceBody())
.expect(201);
});
it('/sale-invoices/:id (DELETE)', async () => {
const response = await request(app.getHttpServer())
.post('/sale-invoices')
.set('organization-id', '4064541lv40nhca')
.send(requestSaleInvoiceBody());
return request(app.getHttpServer())
.delete(`/sale-invoices/${response.body.id}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/sale-invoices/:id (PUT)', async () => {
const response = await request(app.getHttpServer())
.post('/sale-invoices')
.set('organization-id', '4064541lv40nhca')
.send(requestSaleInvoiceBody());
return request(app.getHttpServer())
.put(`/sale-invoices/${response.body.id}`)
.set('organization-id', '4064541lv40nhca')
.send(requestSaleInvoiceBody())
.expect(200);
});
it('/sale-invoices/:id (GET)', async () => {
const response = await request(app.getHttpServer())
.post('/sale-invoices')
.set('organization-id', '4064541lv40nhca')
.send(requestSaleInvoiceBody());
return request(app.getHttpServer())
.get(`/sale-invoices/${response.body.id}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/sale-invoices/:id/state (GET)', async () => {
const response = await request(app.getHttpServer())
.post('/sale-invoices')
.set('organization-id', '4064541lv40nhca')
.send(requestSaleInvoiceBody());
return request(app.getHttpServer())
.get(`/sale-invoices/${response.body.id}/state`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/sale-invoices/:id/payments (GET)', async () => {
const response = await request(app.getHttpServer())
.post('/sale-invoices')
.set('organization-id', '4064541lv40nhca')
.send(requestSaleInvoiceBody());
return request(app.getHttpServer())
.get(`/sale-invoices/${response.body.id}/payments`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/sale-invoices/:id/writeoff (POST)', async () => {
const response = await request(app.getHttpServer())
.post('/sale-invoices')
.set('organization-id', '4064541lv40nhca')
.send(requestSaleInvoiceBody());
return request(app.getHttpServer())
.post(`/sale-invoices/${response.body.id}/writeoff`)
.set('organization-id', '4064541lv40nhca')
.send({
expenseAccountId: 1024,
date: '2023-01-01',
reason: 'Write off reason',
})
.expect(200);
});
it('/sale-invoices/:id/cancel-writeoff (POST)', async () => {
const response = await request(app.getHttpServer())
.post('/sale-invoices')
.set('organization-id', '4064541lv40nhca')
.send(requestSaleInvoiceBody());
await request(app.getHttpServer())
.post(`/sale-invoices/${response.body.id}/writeoff`)
.set('organization-id', '4064541lv40nhca')
.send({
expenseAccountId: 1024,
date: '2023-01-01',
reason: 'Write off reason',
});
return request(app.getHttpServer())
.post(`/sale-invoices/${response.body.id}/cancel-writeoff`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/sale-invoices/:id/deliver (PUT)', async () => {
const response = await request(app.getHttpServer())
.post('/sale-invoices')
.set('organization-id', '4064541lv40nhca')
.send({
...requestSaleInvoiceBody(),
delivered: false,
});
return request(app.getHttpServer())
.post(`/sale-invoices/${response.body.id}/deliver`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
});

View File

@@ -0,0 +1,73 @@
import * as request from 'supertest';
import { app } from './init-app-test';
import { faker } from '@faker-js/faker';
const requestVendorCredit = () => ({
vendorId: 3,
exchangeRate: 1,
vendorCreditNumber: faker.string.uuid(),
vendorCreditDate: '2025-01-01',
entries: [
{
index: 1,
item_id: 1000,
quantity: 1,
rate: 1000,
description: "It's description here.",
},
],
branchId: 1,
warehouseId: 1,
});
describe('Vendor Credits (e2e)', () => {
it('/vendor-credits (POST)', () => {
return request(app.getHttpServer())
.post('/vendor-credits')
.set('organization-id', '4064541lv40nhca')
.send(requestVendorCredit())
.expect(201);
});
it('/vendor-credits/:id (DELETE)', async () => {
const response = await request(app.getHttpServer())
.post('/vendor-credits')
.set('organization-id', '4064541lv40nhca')
.send(requestVendorCredit());
const vendorCreditId = response.body.id;
return request(app.getHttpServer())
.delete(`/vendor-credits/${vendorCreditId}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/vendor-credits/:id/open (POST)', async () => {
const response = await request(app.getHttpServer())
.post('/vendor-credits')
.set('organization-id', '4064541lv40nhca')
.send(requestVendorCredit());
const vendorCreditId = response.body.id;
return request(app.getHttpServer())
.put(`/vendor-credits/${vendorCreditId}/open`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
it('/vendor-credits/:id (GET)', async () => {
const response = await request(app.getHttpServer())
.post('/vendor-credits')
.set('organization-id', '4064541lv40nhca')
.send(requestVendorCredit());
const vendorCreditId = response.body.id;
return request(app.getHttpServer())
.get(`/vendor-credits/${vendorCreditId}`)
.set('organization-id', '4064541lv40nhca')
.expect(200);
});
});

8
pnpm-lock.yaml generated
View File

@@ -544,6 +544,9 @@ importers:
axios:
specifier: ^1.6.0
version: 1.7.7
bluebird:
specifier: ^3.7.2
version: 3.7.2
bull:
specifier: ^4.16.3
version: 4.16.4
@@ -16906,7 +16909,7 @@ packages:
postcss-modules-values: 4.0.0(postcss@8.4.47)
postcss-value-parser: 4.2.0
semver: 7.6.2
webpack: 5.91.0(esbuild@0.18.20)(webpack-cli@5.1.4)
webpack: 5.91.0(esbuild@0.23.1)
/css-loader@6.11.0(webpack@5.96.1):
resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==}
@@ -32291,7 +32294,7 @@ packages:
peerDependencies:
webpack: ^5.0.0
dependencies:
webpack: 5.91.0(esbuild@0.18.20)(webpack-cli@5.1.4)
webpack: 5.91.0(esbuild@0.23.1)
/style-loader@3.3.4(webpack@5.96.1):
resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==}
@@ -34736,7 +34739,6 @@ packages:
- '@swc/core'
- esbuild
- uglify-js
dev: false
/webpack@5.91.0(esbuild@0.23.1)(webpack-cli@4.10.0):
resolution: {integrity: sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==}