mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
Merge branch 'master' of https://github.com/abouolia/Bigcapital into sales
This commit is contained in:
@@ -3,16 +3,16 @@ exports.up = function(knex) {
|
|||||||
return knex.schema.createTable('inventory_transactions', table => {
|
return knex.schema.createTable('inventory_transactions', table => {
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
table.date('date');
|
table.date('date');
|
||||||
|
|
||||||
table.string('direction');
|
table.string('direction');
|
||||||
|
|
||||||
table.integer('item_id');
|
table.integer('item_id');
|
||||||
table.integer('quantity');
|
table.integer('quantity');
|
||||||
table.decimal('rate', 13, 3);
|
table.decimal('rate', 13, 3);
|
||||||
table.integer('remaining');
|
|
||||||
|
|
||||||
table.string('transaction_type');
|
table.string('transaction_type');
|
||||||
table.integer('transaction_id');
|
table.integer('transaction_id');
|
||||||
|
|
||||||
table.integer('inventory_transaction_id');
|
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
exports.up = function(knex) {
|
||||||
|
return knex.schema.createTable('inventory_cost_lot_tracker', table => {
|
||||||
|
table.increments();
|
||||||
|
table.date('date');
|
||||||
|
|
||||||
|
table.string('direction');
|
||||||
|
|
||||||
|
table.integer('item_id');
|
||||||
|
table.decimal('rate', 13, 3);
|
||||||
|
table.integer('remaining');
|
||||||
|
|
||||||
|
table.string('transaction_type');
|
||||||
|
table.integer('transaction_id');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function(knex) {
|
||||||
|
return knex.schema.dropTableIfExists('inventory_cost_lot_tracker');
|
||||||
|
};
|
||||||
62
server/src/http/controllers/Ping.ts
Normal file
62
server/src/http/controllers/Ping.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { Router, Request, Response } from 'express';
|
||||||
|
import InventoryService from '@/services/Inventory/Inventory';
|
||||||
|
|
||||||
|
export default class Ping {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router constur
|
||||||
|
*/
|
||||||
|
static router() {
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
'/',
|
||||||
|
this.ping,
|
||||||
|
);
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
*/
|
||||||
|
static async ping(req: Request, res: Response) {
|
||||||
|
|
||||||
|
const result = await InventoryService.trackingInventoryLotsCost([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
date: '2020-02-02',
|
||||||
|
direction: 'IN',
|
||||||
|
itemId: 1,
|
||||||
|
quantity: 100,
|
||||||
|
rate: 10,
|
||||||
|
transactionType: 'Bill',
|
||||||
|
transactionId: 1,
|
||||||
|
remaining: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
date: '2020-02-02',
|
||||||
|
direction: 'OUT',
|
||||||
|
itemId: 1,
|
||||||
|
quantity: 80,
|
||||||
|
rate: 10,
|
||||||
|
transactionType: 'SaleInvoice',
|
||||||
|
transactionId: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
date: '2020-02-02',
|
||||||
|
direction: 'OUT',
|
||||||
|
itemId: 2,
|
||||||
|
quantity: 500,
|
||||||
|
rate: 10,
|
||||||
|
transactionType: 'SaleInvoice',
|
||||||
|
transactionId: 2,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return res.status(200).send({ id: 1231231 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -415,6 +415,9 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
return res.status(400).send({ errors: errorReasons });
|
return res.status(400).send({ errors: errorReasons });
|
||||||
}
|
}
|
||||||
const paymentReceives = await PaymentReceive.query().onBuild((builder) => {
|
const paymentReceives = await PaymentReceive.query().onBuild((builder) => {
|
||||||
|
builder.withGraphFetched('customer');
|
||||||
|
builder.withGraphFetched('depositAccount');
|
||||||
|
|
||||||
dynamicListing.buildQuery()(builder);
|
dynamicListing.buildQuery()(builder);
|
||||||
return builder;
|
return builder;
|
||||||
}).pagination(filter.page - 1, filter.page_size);
|
}).pagination(filter.page - 1, filter.page_size);
|
||||||
|
|||||||
@@ -326,6 +326,7 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
|
|
||||||
const salesEstimates = await SaleEstimate.query().onBuild((builder) => {
|
const salesEstimates = await SaleEstimate.query().onBuild((builder) => {
|
||||||
dynamicListing.buildQuery()(builder);
|
dynamicListing.buildQuery()(builder);
|
||||||
|
builder.withGraphFetched('customer');
|
||||||
return builder;
|
return builder;
|
||||||
}).pagination(filter.page - 1, filter.page_size);
|
}).pagination(filter.page - 1, filter.page_size);
|
||||||
|
|
||||||
|
|||||||
@@ -262,6 +262,9 @@ export default class SaleInvoicesController {
|
|||||||
const storedSaleInvoice = await SaleInvoiceService.createSaleInvoice(
|
const storedSaleInvoice = await SaleInvoiceService.createSaleInvoice(
|
||||||
saleInvoice
|
saleInvoice
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// InventoryService.trackingInventoryLotsCost();
|
||||||
|
|
||||||
return res.status(200).send({ id: storedSaleInvoice.id });
|
return res.status(200).send({ id: storedSaleInvoice.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -313,6 +313,8 @@ export default class SalesReceiptsController {
|
|||||||
errorReasons.push(...errors);
|
errorReasons.push(...errors);
|
||||||
}
|
}
|
||||||
const salesReceipts = await SaleReceipt.query().onBuild((builder) => {
|
const salesReceipts = await SaleReceipt.query().onBuild((builder) => {
|
||||||
|
builder.withGraphFetched('customer');
|
||||||
|
builder.withGraphFetched('depositAccount');
|
||||||
builder.withGraphFetched('entries');
|
builder.withGraphFetched('entries');
|
||||||
dynamicListing.buildQuery()(builder);
|
dynamicListing.buildQuery()(builder);
|
||||||
}).pagination(filter.page - 1, filter.page_size);
|
}).pagination(filter.page - 1, filter.page_size);
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ import ExchangeRates from '@/http/controllers/ExchangeRates';
|
|||||||
import Media from '@/http/controllers/Media';
|
import Media from '@/http/controllers/Media';
|
||||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||||
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
||||||
|
import Ping from '@/http/controllers/Ping';
|
||||||
|
|
||||||
export default (app) => {
|
export default (app) => {
|
||||||
// app.use('/api/oauth2', OAuth2.router());
|
// app.use('/api/oauth2', OAuth2.router());
|
||||||
app.use('/api/auth', Authentication.router());
|
app.use('/api/auth', Authentication.router());
|
||||||
app.use('/api/invite', InviteUsers.router());
|
app.use('/api/invite', InviteUsers.router());
|
||||||
|
app.use('/api/ping', Ping.router());
|
||||||
|
|
||||||
const dashboard = express.Router();
|
const dashboard = express.Router();
|
||||||
|
|
||||||
|
|||||||
23
server/src/interfaces/InventoryTransaction.ts
Normal file
23
server/src/interfaces/InventoryTransaction.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export interface IInventoryTransaction {
|
||||||
|
id?: number,
|
||||||
|
date: Date,
|
||||||
|
direction: string,
|
||||||
|
itemId: number,
|
||||||
|
quantity: number,
|
||||||
|
rate: number,
|
||||||
|
transactionType: string,
|
||||||
|
transactionId: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IInventoryLotCost {
|
||||||
|
id?: number,
|
||||||
|
date: Date,
|
||||||
|
direction: string,
|
||||||
|
itemId: number,
|
||||||
|
rate: number,
|
||||||
|
remaining: number,
|
||||||
|
transactionType: string,
|
||||||
|
transactionId: string,
|
||||||
|
}
|
||||||
@@ -44,9 +44,6 @@ export default class Bill extends mixin(TenantModel, [CachableModel]) {
|
|||||||
const ItemEntry = require('@/models/ItemEntry');
|
const ItemEntry = require('@/models/ItemEntry');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
vendor: {
|
vendor: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: this.relationBindKnex(Vendor.default),
|
modelClass: this.relationBindKnex(Vendor.default),
|
||||||
@@ -56,9 +53,6 @@ export default class Bill extends mixin(TenantModel, [CachableModel]) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
entries: {
|
entries: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: this.relationBindKnex(ItemEntry.default),
|
modelClass: this.relationBindKnex(ItemEntry.default),
|
||||||
@@ -66,6 +60,9 @@ export default class Bill extends mixin(TenantModel, [CachableModel]) {
|
|||||||
from: 'bills.id',
|
from: 'bills.id',
|
||||||
to: 'items_entries.referenceId',
|
to: 'items_entries.referenceId',
|
||||||
},
|
},
|
||||||
|
filter(builder) {
|
||||||
|
builder.where('reference_type', 'SaleReceipt');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,11 @@ export default class BillPayment extends mixin(TenantModel, [CachableModel]) {
|
|||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const BillPaymentEntry = require('@/models/BillPaymentEntry');
|
const BillPaymentEntry = require('@/models/BillPaymentEntry');
|
||||||
|
const AccountTransaction = require('@/models/AccountTransaction');
|
||||||
const Vendor = require('@/models/Vendor');
|
const Vendor = require('@/models/Vendor');
|
||||||
const Account = require('@/models/Account');
|
const Account = require('@/models/Account');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
entries: {
|
entries: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: this.relationBindKnex(BillPaymentEntry.default),
|
modelClass: this.relationBindKnex(BillPaymentEntry.default),
|
||||||
@@ -40,9 +38,6 @@ export default class BillPayment extends mixin(TenantModel, [CachableModel]) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
vendor: {
|
vendor: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: this.relationBindKnex(Vendor.default),
|
modelClass: this.relationBindKnex(Vendor.default),
|
||||||
@@ -52,9 +47,6 @@ export default class BillPayment extends mixin(TenantModel, [CachableModel]) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
paymentAccount: {
|
paymentAccount: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: this.relationBindKnex(Account.default),
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
@@ -63,6 +55,18 @@ export default class BillPayment extends mixin(TenantModel, [CachableModel]) {
|
|||||||
to: 'accounts.id',
|
to: 'accounts.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
transactions: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: this.relationBindKnex(AccountTransaction.default),
|
||||||
|
join: {
|
||||||
|
from: 'bills_payments.id',
|
||||||
|
to: 'accounts_transactions.referenceId'
|
||||||
|
},
|
||||||
|
filter(builder) {
|
||||||
|
builder.where('reference_type', 'BillPayment');
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
server/src/models/InventoryLotCostTracker.js
Normal file
18
server/src/models/InventoryLotCostTracker.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Model } from 'objection';
|
||||||
|
import TenantModel from '@/models/TenantModel';
|
||||||
|
|
||||||
|
export default class InventoryLotCostTracker extends TenantModel {
|
||||||
|
/**
|
||||||
|
* Table name
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'inventory_cost_lot_tracker';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model timestamps.
|
||||||
|
*/
|
||||||
|
static get timestamps() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,8 +24,29 @@ export default class PaymentReceive extends mixin(TenantModel, [CachableModel])
|
|||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const PaymentReceiveEntry = require('@/models/PaymentReceiveEntry');
|
const PaymentReceiveEntry = require('@/models/PaymentReceiveEntry');
|
||||||
|
const AccountTransaction = require('@/models/AccountTransaction');
|
||||||
|
const Customer = require('@/models/Customer');
|
||||||
|
const Account = require('@/models/Account');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
customer: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: this.relationBindKnex(Customer.default),
|
||||||
|
join: {
|
||||||
|
from: 'payment_receives.customerId',
|
||||||
|
to: 'customers.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
depositAccount: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
|
join: {
|
||||||
|
from: 'payment_receives.depositAccountId',
|
||||||
|
to: 'accounts.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
entries: {
|
entries: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: this.relationBindKnex(PaymentReceiveEntry.default),
|
modelClass: this.relationBindKnex(PaymentReceiveEntry.default),
|
||||||
@@ -34,6 +55,18 @@ export default class PaymentReceive extends mixin(TenantModel, [CachableModel])
|
|||||||
to: 'payment_receives_entries.paymentReceiveId',
|
to: 'payment_receives_entries.paymentReceiveId',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
transactions: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: this.relationBindKnex(AccountTransaction.default),
|
||||||
|
join: {
|
||||||
|
from: 'payment_receives.id',
|
||||||
|
to: 'accounts_transactions.referenceId'
|
||||||
|
},
|
||||||
|
filter(builder) {
|
||||||
|
builder.where('reference_type', 'PaymentReceive');
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,18 @@ export default class SaleEstimate extends mixin(TenantModel, [CachableModel]) {
|
|||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const ItemEntry = require('@/models/ItemEntry');
|
const ItemEntry = require('@/models/ItemEntry');
|
||||||
|
const Customer = require('@/models/Customer');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
customer: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: this.relationBindKnex(Customer.default),
|
||||||
|
join: {
|
||||||
|
from: 'sales_estimates.customerId',
|
||||||
|
to: 'customers.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
entries: {
|
entries: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: this.relationBindKnex(ItemEntry.default),
|
modelClass: this.relationBindKnex(ItemEntry.default),
|
||||||
@@ -32,6 +42,9 @@ export default class SaleEstimate extends mixin(TenantModel, [CachableModel]) {
|
|||||||
from: 'sales_estimates.id',
|
from: 'sales_estimates.id',
|
||||||
to: 'items_entries.referenceId',
|
to: 'items_entries.referenceId',
|
||||||
},
|
},
|
||||||
|
filter(builder) {
|
||||||
|
builder.where('reference_type', 'SaleEstimate');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,13 +37,11 @@ export default class SaleInvoice extends mixin(TenantModel, [CachableModel]) {
|
|||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
|
const AccountTransaction = require('@/models/AccountTransaction');
|
||||||
const ItemEntry = require('@/models/ItemEntry');
|
const ItemEntry = require('@/models/ItemEntry');
|
||||||
const Customer = require('@/models/Customer');
|
const Customer = require('@/models/Customer');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
entries: {
|
entries: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: this.relationBindKnex(ItemEntry.default),
|
modelClass: this.relationBindKnex(ItemEntry.default),
|
||||||
@@ -51,11 +49,11 @@ export default class SaleInvoice extends mixin(TenantModel, [CachableModel]) {
|
|||||||
from: 'sales_invoices.id',
|
from: 'sales_invoices.id',
|
||||||
to: 'items_entries.referenceId',
|
to: 'items_entries.referenceId',
|
||||||
},
|
},
|
||||||
|
filter(builder) {
|
||||||
|
builder.where('reference_type', 'SaleInvoice');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
customer: {
|
customer: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: this.relationBindKnex(Customer.default),
|
modelClass: this.relationBindKnex(Customer.default),
|
||||||
@@ -64,6 +62,18 @@ export default class SaleInvoice extends mixin(TenantModel, [CachableModel]) {
|
|||||||
to: 'customers.id',
|
to: 'customers.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
transactions: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: this.relationBindKnex(AccountTransaction.default),
|
||||||
|
join: {
|
||||||
|
from: 'sales_invoices.id',
|
||||||
|
to: 'accounts_transactions.referenceId'
|
||||||
|
},
|
||||||
|
filter(builder) {
|
||||||
|
builder.where('reference_type', 'SaleInvoice');
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,17 +23,53 @@ export default class SaleReceipt extends mixin(TenantModel, [CachableModel]) {
|
|||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
|
const Customer = require('@/models/Customer');
|
||||||
|
const Account = require('@/models/Account');
|
||||||
|
const AccountTransaction = require('@/models/AccountTransaction');
|
||||||
const ItemEntry = require('@/models/ItemEntry');
|
const ItemEntry = require('@/models/ItemEntry');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entries: {
|
customer: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: this.relationBindKnex(Customer.default),
|
||||||
|
join: {
|
||||||
|
from: 'sales_receipts.customerId',
|
||||||
|
to: 'customers.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
depositAccount: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: this.relationBindKnex(Account.default),
|
||||||
|
join: {
|
||||||
|
from: 'sales_receipts.depositAccountId',
|
||||||
|
to: 'accounts.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
entries: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
modelClass: this.relationBindKnex(ItemEntry.default),
|
modelClass: this.relationBindKnex(ItemEntry.default),
|
||||||
join: {
|
join: {
|
||||||
from: 'sales_receipts.id',
|
from: 'sales_receipts.id',
|
||||||
to: 'items_entries.referenceId',
|
to: 'items_entries.referenceId',
|
||||||
},
|
},
|
||||||
|
filter(builder) {
|
||||||
|
builder.where('reference_type', 'SaleReceipt');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
transactions: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: this.relationBindKnex(AccountTransaction.default),
|
||||||
|
join: {
|
||||||
|
from: 'sales_receipts.id',
|
||||||
|
to: 'accounts_transactions.referenceId'
|
||||||
|
},
|
||||||
|
filter(builder) {
|
||||||
|
builder.where('reference_type', 'SaleReceipt');
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import { InventoryTransaction } from "../../models";
|
|
||||||
|
|
||||||
|
|
||||||
export default class InventoryService {
|
|
||||||
|
|
||||||
async isInventoryPurchaseSold(transactionType, transactionId) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static deleteTransactions(transactionId, transactionType) {
|
|
||||||
return InventoryTransaction.tenant().query()
|
|
||||||
.where('transaction_type', transactionType)
|
|
||||||
.where('transaction_id', transactionId)
|
|
||||||
.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
149
server/src/services/Inventory/Inventory.ts
Normal file
149
server/src/services/Inventory/Inventory.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import { InventoryTransaction, Item } from '@/models';
|
||||||
|
import InventoryCostLotTracker from './InventoryCostLotTracker';
|
||||||
|
import { IInventoryTransaction, IInventoryLotCost } from '@/interfaces/InventoryTransaction';
|
||||||
|
import { IInventoryLotCost, IInventoryLotCost } from '../../interfaces/InventoryTransaction';
|
||||||
|
import { pick } from 'lodash';
|
||||||
|
|
||||||
|
export default class InventoryService {
|
||||||
|
/**
|
||||||
|
* Records the inventory transactions.
|
||||||
|
* @param {Bill} bill
|
||||||
|
* @param {number} billId
|
||||||
|
*/
|
||||||
|
static async recordInventoryTransactions(
|
||||||
|
entries: [],
|
||||||
|
date: Date,
|
||||||
|
transactionType: string,
|
||||||
|
transactionId: number,
|
||||||
|
) {
|
||||||
|
const storedOpers: any = [];
|
||||||
|
const entriesItemsIds = entries.map((e: any) => e.item_id);
|
||||||
|
const inventoryItems = await Item.tenant()
|
||||||
|
.query()
|
||||||
|
.whereIn('id', entriesItemsIds)
|
||||||
|
.where('type', 'inventory');
|
||||||
|
|
||||||
|
const inventoryItemsIds = inventoryItems.map((i) => i.id);
|
||||||
|
|
||||||
|
// Filter the bill entries that have inventory items.
|
||||||
|
const inventoryEntries = entries.filter(
|
||||||
|
(entry) => inventoryItemsIds.indexOf(entry.item_id) !== -1
|
||||||
|
);
|
||||||
|
inventoryEntries.forEach((entry: any) => {
|
||||||
|
const oper = InventoryTransaction.tenant().query().insert({
|
||||||
|
date,
|
||||||
|
|
||||||
|
item_id: entry.item_id,
|
||||||
|
quantity: entry.quantity,
|
||||||
|
rate: entry.rate,
|
||||||
|
|
||||||
|
transaction_type: transactionType,
|
||||||
|
transaction_id: transactionId,
|
||||||
|
});
|
||||||
|
storedOpers.push(oper);
|
||||||
|
});
|
||||||
|
return Promise.all(storedOpers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given inventory transactions.
|
||||||
|
* @param {string} transactionType
|
||||||
|
* @param {number} transactionId
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
static deleteInventoryTransactions(
|
||||||
|
transactionId: number,
|
||||||
|
transactionType: string,
|
||||||
|
) {
|
||||||
|
return InventoryTransaction.tenant().query()
|
||||||
|
.where('transaction_type', transactionType)
|
||||||
|
.where('transaction_id', transactionId)
|
||||||
|
.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
revertInventoryLotsCost(fromDate?: Date) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records the journal entries transactions.
|
||||||
|
* @param {IInventoryLotCost[]} inventoryTransactions -
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static async recordJournalEntries(inventoryLots: IInventoryLotCost[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracking the given inventory transactions to lots costs transactions.
|
||||||
|
* @param {IInventoryTransaction[]} inventoryTransactions - Inventory transactions.
|
||||||
|
* @return {IInventoryLotCost[]}
|
||||||
|
*/
|
||||||
|
static async trackingInventoryLotsCost(inventoryTransactions: IInventoryTransaction[]) {
|
||||||
|
// Collect cost lots transactions to insert them to the storage in bulk.
|
||||||
|
const costLotsTransactions: IInventoryLotCost[] = [];
|
||||||
|
|
||||||
|
// Collect inventory transactions by item id.
|
||||||
|
const inventoryByItem: any = {};
|
||||||
|
// Collection `IN` inventory tranaction by transaction id.
|
||||||
|
const inventoryINTrans: any = {};
|
||||||
|
|
||||||
|
inventoryTransactions.forEach((transaction: IInventoryTransaction) => {
|
||||||
|
const { itemId, id } = transaction;
|
||||||
|
(inventoryByItem[itemId] || (inventoryByItem[itemId] = []));
|
||||||
|
|
||||||
|
const commonLotTransaction: IInventoryLotCost = {
|
||||||
|
...pick(transaction, [
|
||||||
|
'date', 'rate', 'itemId', 'quantity',
|
||||||
|
'direction', 'transactionType', 'transactionId',
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
// Record inventory `IN` cost lot transaction.
|
||||||
|
if (transaction.direction === 'IN') {
|
||||||
|
inventoryByItem[itemId].push(id);
|
||||||
|
inventoryINTrans[id] = {
|
||||||
|
...commonLotTransaction,
|
||||||
|
remaining: commonLotTransaction.quantity,
|
||||||
|
};
|
||||||
|
costLotsTransactions.push(inventoryINTrans[id]);
|
||||||
|
|
||||||
|
// Record inventory 'OUT' cost lots from 'IN' transactions.
|
||||||
|
} else if (transaction.direction === 'OUT') {
|
||||||
|
let invRemaining = transaction.quantity;
|
||||||
|
|
||||||
|
inventoryByItem?.[itemId]?.forEach((
|
||||||
|
_invTransactionId: number,
|
||||||
|
index: number,
|
||||||
|
) => {
|
||||||
|
const _invINTransaction = inventoryINTrans[_invTransactionId];
|
||||||
|
|
||||||
|
// Detarmines the 'OUT' lot tranasctions whether bigger than 'IN' remaining transaction.
|
||||||
|
const biggerThanRemaining = (_invINTransaction.remaining - transaction.quantity) > 0;
|
||||||
|
const decrement = (biggerThanRemaining) ? transaction.quantity : _invINTransaction.remaining;
|
||||||
|
|
||||||
|
_invINTransaction.remaining = Math.max(
|
||||||
|
_invINTransaction.remaining - decrement, 0,
|
||||||
|
);
|
||||||
|
invRemaining = Math.max(invRemaining - decrement, 0);
|
||||||
|
|
||||||
|
costLotsTransactions.push({
|
||||||
|
...commonLotTransaction,
|
||||||
|
quantity: decrement,
|
||||||
|
});
|
||||||
|
// Pop the 'IN' lots that has zero remaining.
|
||||||
|
if (_invINTransaction.remaining === 0) {
|
||||||
|
inventoryByItem?.[itemId].splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (invRemaining > 0) {
|
||||||
|
costLotsTransactions.push({
|
||||||
|
...commonLotTransaction,
|
||||||
|
quantity: invRemaining,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return costLotsTransactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
11
server/src/services/Inventory/InventoryCostLotTracker.js
Normal file
11
server/src/services/Inventory/InventoryCostLotTracker.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
export default class InventoryCostLotTracker {
|
||||||
|
|
||||||
|
recalcInventoryLotsCost(inventoryTransactions) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTransactionsFromDate(fromDate) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -96,7 +96,7 @@ export default class BillPaymentsService {
|
|||||||
* @param {IBillPayment} oldBillPayment
|
* @param {IBillPayment} oldBillPayment
|
||||||
*/
|
*/
|
||||||
static async editBillPayment(billPaymentId, billPayment, oldBillPayment) {
|
static async editBillPayment(billPaymentId, billPayment, oldBillPayment) {
|
||||||
const amount = sumBy(bilPayment.entries, 'payment_amount');
|
const amount = sumBy(billPayment.entries, 'payment_amount');
|
||||||
const updateBillPayment = await BillPayment.tenant()
|
const updateBillPayment = await BillPayment.tenant()
|
||||||
.query()
|
.query()
|
||||||
.where('id', billPaymentId)
|
.where('id', billPaymentId)
|
||||||
|
|||||||
@@ -54,19 +54,23 @@ export default class BillsService {
|
|||||||
});
|
});
|
||||||
saveEntriesOpers.push(oper);
|
saveEntriesOpers.push(oper);
|
||||||
});
|
});
|
||||||
// Increment vendor balance.
|
// Increments vendor balance.
|
||||||
const incrementOper = Vendor.changeBalance(bill.vendor_id, amount);
|
const incrementOper = Vendor.changeBalance(bill.vendor_id, amount);
|
||||||
|
|
||||||
|
// // Rewrite the inventory transactions for inventory items.
|
||||||
|
// const writeInvTransactionsOper = InventoryService.recordInventoryTransactions(
|
||||||
|
// bill.entries, 'Bill', billId,
|
||||||
|
// );
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
...saveEntriesOpers,
|
...saveEntriesOpers,
|
||||||
incrementOper,
|
incrementOper,
|
||||||
this.recordInventoryTransactions(bill, storedBill.id),
|
// this.recordInventoryTransactions(bill, storedBill.id),
|
||||||
this.recordJournalTransactions({ ...bill, id: storedBill.id }),
|
this.recordJournalTransactions({ ...bill, id: storedBill.id }),
|
||||||
|
// writeInvTransactionsOper,
|
||||||
]);
|
]);
|
||||||
return storedBill;
|
return storedBill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edits details of the given bill id with associated entries.
|
* Edits details of the given bill id with associated entries.
|
||||||
*
|
*
|
||||||
@@ -112,51 +116,23 @@ export default class BillsService {
|
|||||||
amount,
|
amount,
|
||||||
oldBill.amount,
|
oldBill.amount,
|
||||||
);
|
);
|
||||||
// Record bill journal transactions.
|
// // Deletes the old inventory transactions.
|
||||||
const recordTransactionsOper = this.recordJournalTransactions(bill, billId);
|
// const deleteInvTransactionsOper = InventorySevice.deleteInventoryTransactions(
|
||||||
|
// billId, 'Bill',
|
||||||
|
// );
|
||||||
|
// // Re-write the inventory transactions for inventory items.
|
||||||
|
// const writeInvTransactionsOper = InventoryService.recordInventoryTransactions(
|
||||||
|
// bill.entries, 'Bill', billId,
|
||||||
|
// );
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
patchEntriesOper,
|
patchEntriesOper,
|
||||||
recordTransactionsOper,
|
recordTransactionsOper,
|
||||||
changeVendorBalanceOper,
|
changeVendorBalanceOper,
|
||||||
|
// deleteInvTransactionsOper,
|
||||||
|
// writeInvTransactionsOper,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Records inventory transactions.
|
|
||||||
* @param {IBill} bill -
|
|
||||||
* @return {void}
|
|
||||||
*/
|
|
||||||
static async recordInventoryTransactions(bill, billId) {
|
|
||||||
const storeInventoryTransactions = [];
|
|
||||||
const entriesItemsIds = bill.entries.map((e) => e.item_id);
|
|
||||||
const inventoryItems = await Item.tenant()
|
|
||||||
.query()
|
|
||||||
.whereIn('id', entriesItemsIds)
|
|
||||||
.where('type', 'inventory');
|
|
||||||
|
|
||||||
const inventoryItemsIds = inventoryItems.map((i) => i.id);
|
|
||||||
const inventoryEntries = bill.entries.filter(
|
|
||||||
(entry) => inventoryItemsIds.indexOf(entry.item_id) !== -1
|
|
||||||
);
|
|
||||||
inventoryEntries.forEach((entry) => {
|
|
||||||
const oper = InventoryTransaction.tenant().query().insert({
|
|
||||||
direction: 'IN',
|
|
||||||
date: bill.bill_date,
|
|
||||||
|
|
||||||
item_id: entry.item_id,
|
|
||||||
quantity: entry.quantity,
|
|
||||||
rate: entry.rate,
|
|
||||||
remaining: entry.quantity,
|
|
||||||
|
|
||||||
transaction_type: 'Bill',
|
|
||||||
transaction_id: billId,
|
|
||||||
});
|
|
||||||
storeInventoryTransactions.push(oper);
|
|
||||||
});
|
|
||||||
return Promise.all([...storeInventoryTransactions]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records the bill journal transactions.
|
* Records the bill journal transactions.
|
||||||
* @async
|
* @async
|
||||||
@@ -253,9 +229,8 @@ export default class BillsService {
|
|||||||
'Bill'
|
'Bill'
|
||||||
);
|
);
|
||||||
// Delete bill associated inventory transactions.
|
// Delete bill associated inventory transactions.
|
||||||
const deleteInventoryTransOper = InventoryService.deleteTransactions(
|
const deleteInventoryTransOper = InventoryService.deleteInventoryTransactions(
|
||||||
billId,
|
billId, 'Bill'
|
||||||
'Bill'
|
|
||||||
);
|
);
|
||||||
// Revert vendor balance.
|
// Revert vendor balance.
|
||||||
const revertVendorBalance = Vendor.changeBalance(bill.vendorId, bill.amount * -1);
|
const revertVendorBalance = Vendor.changeBalance(bill.vendorId, bill.amount * -1);
|
||||||
|
|||||||
@@ -98,8 +98,8 @@ export default class SaleEstimateService {
|
|||||||
*/
|
*/
|
||||||
static async isEstimateEntriesIDsExists(estimateId: number, estimate: any) {
|
static async isEstimateEntriesIDsExists(estimateId: number, estimate: any) {
|
||||||
const estimateEntriesIds = estimate.entries
|
const estimateEntriesIds = estimate.entries
|
||||||
.filter((e) => e.id)
|
.filter((e: any) => e.id)
|
||||||
.map((e) => e.id);
|
.map((e: any) => e.id);
|
||||||
|
|
||||||
const estimateEntries = await ItemEntry.tenant()
|
const estimateEntries = await ItemEntry.tenant()
|
||||||
.query()
|
.query()
|
||||||
@@ -138,6 +138,7 @@ export default class SaleEstimateService {
|
|||||||
.query()
|
.query()
|
||||||
.where('id', estimateId)
|
.where('id', estimateId)
|
||||||
.withGraphFetched('entries')
|
.withGraphFetched('entries')
|
||||||
|
.withGraphFetched('customer')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
return estimate;
|
return estimate;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { omit, sumBy, difference } from 'lodash';
|
import { omit, sumBy, difference, chain, sum } from 'lodash';
|
||||||
import {
|
import {
|
||||||
SaleInvoice,
|
SaleInvoice,
|
||||||
AccountTransaction,
|
AccountTransaction,
|
||||||
|
InventoryTransaction,
|
||||||
Account,
|
Account,
|
||||||
ItemEntry,
|
ItemEntry,
|
||||||
Customer,
|
Customer,
|
||||||
@@ -9,6 +10,7 @@ import {
|
|||||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||||
import HasItemsEntries from '@/services/Sales/HasItemsEntries';
|
import HasItemsEntries from '@/services/Sales/HasItemsEntries';
|
||||||
import CustomerRepository from '@/repositories/CustomerRepository';
|
import CustomerRepository from '@/repositories/CustomerRepository';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sales invoices service
|
* Sales invoices service
|
||||||
@@ -48,10 +50,40 @@ export default class SaleInvoicesService {
|
|||||||
saleInvoice.customer_id,
|
saleInvoice.customer_id,
|
||||||
balance,
|
balance,
|
||||||
);
|
);
|
||||||
await Promise.all([...opers, incrementOper]);
|
// Records the inventory transactions for inventory items.
|
||||||
|
const recordInventoryTransOpers = this.recordInventoryTransactions(
|
||||||
|
saleInvoice, storedInvoice.id
|
||||||
|
);
|
||||||
|
// Await all async operations.
|
||||||
|
await Promise.all([
|
||||||
|
...opers,
|
||||||
|
incrementOper,
|
||||||
|
recordInventoryTransOpers,
|
||||||
|
]);
|
||||||
return storedInvoice;
|
return storedInvoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records the inventory items transactions.
|
||||||
|
* @param {SaleInvoice} saleInvoice -
|
||||||
|
* @param {number} saleInvoiceId -
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
static async recordInventoryTransactions(saleInvoice, saleInvoiceId) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records the sale invoice journal entries and calculate the items cost
|
||||||
|
* based on the given cost method in the options FIFO, LIFO or average cost rate.
|
||||||
|
*
|
||||||
|
* @param {SaleInvoice} saleInvoice -
|
||||||
|
* @param {Array} inventoryTransactions -
|
||||||
|
*/
|
||||||
|
static async recordJournalEntries(saleInvoice: any, inventoryTransactions: array[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit the given sale invoice.
|
* Edit the given sale invoice.
|
||||||
* @async
|
* @async
|
||||||
@@ -94,6 +126,48 @@ export default class SaleInvoicesService {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async recalcInventoryTransactionsCost(inventoryTransactions: array) {
|
||||||
|
const inventoryTransactionsMap = this.mapInventoryTransByItem(inventoryTransactions);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the inventory transactions.
|
||||||
|
* @param {string} transactionType
|
||||||
|
* @param {number} transactionId
|
||||||
|
*/
|
||||||
|
static async revertInventoryTransactions(inventoryTransactions: array) {
|
||||||
|
const opers = [];
|
||||||
|
|
||||||
|
inventoryTransactions.forEach((trans: any) => {
|
||||||
|
switch(trans.direction) {
|
||||||
|
case 'OUT':
|
||||||
|
if (trans.inventoryTransactionId) {
|
||||||
|
const revertRemaining = InventoryTransaction.tenant()
|
||||||
|
.query()
|
||||||
|
.where('id', trans.inventoryTransactionId)
|
||||||
|
.where('direction', 'OUT')
|
||||||
|
.increment('remaining', trans.quanitity);
|
||||||
|
|
||||||
|
opers.push(revertRemaining);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'IN':
|
||||||
|
const removeRelationOper = InventoryTransaction.tenant()
|
||||||
|
.query()
|
||||||
|
.where('inventory_transaction_id', trans.id)
|
||||||
|
.where('direction', 'IN')
|
||||||
|
.update({
|
||||||
|
inventory_transaction_id: null,
|
||||||
|
});
|
||||||
|
opers.push(removeRelationOper);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Promise.all(opers);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the given sale invoice with associated entries
|
* Deletes the given sale invoice with associated entries
|
||||||
* and journal transactions.
|
* and journal transactions.
|
||||||
@@ -126,10 +200,19 @@ export default class SaleInvoicesService {
|
|||||||
journal.loadEntries(invoiceTransactions);
|
journal.loadEntries(invoiceTransactions);
|
||||||
journal.removeEntries();
|
journal.removeEntries();
|
||||||
|
|
||||||
|
const inventoryTransactions = await InventoryTransaction.tenant()
|
||||||
|
.query()
|
||||||
|
.where('transaction_type', 'SaleInvoice')
|
||||||
|
.where('transaction_id', saleInvoiceId);
|
||||||
|
|
||||||
|
// Revert inventory transactions.
|
||||||
|
const revertInventoryTransactionsOper = this.revertInventoryTransactions(inventoryTransactions);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
journal.deleteEntries(),
|
journal.deleteEntries(),
|
||||||
journal.saveBalance(),
|
journal.saveBalance(),
|
||||||
revertCustomerBalanceOper,
|
revertCustomerBalanceOper,
|
||||||
|
revertInventoryTransactionsOper,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +235,7 @@ export default class SaleInvoicesService {
|
|||||||
return SaleInvoice.tenant().query()
|
return SaleInvoice.tenant().query()
|
||||||
.where('id', saleInvoiceId)
|
.where('id', saleInvoiceId)
|
||||||
.withGraphFetched('entries')
|
.withGraphFetched('entries')
|
||||||
|
.withGraphFetched('customer')
|
||||||
.first();
|
.first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
loginRes
|
loginRes
|
||||||
} from '~/dbInit';
|
} from '~/dbInit';
|
||||||
|
|
||||||
describe('route: `/api/purchases/bills`', () => {
|
describe.only('route: `/api/purchases/bills`', () => {
|
||||||
describe('POST: `/api/purchases/bills`', () => {
|
describe('POST: `/api/purchases/bills`', () => {
|
||||||
it('Should `bill_number` be required.', async () => {
|
it('Should `bill_number` be required.', async () => {
|
||||||
const res = await request()
|
const res = await request()
|
||||||
@@ -207,6 +207,8 @@ describe('route: `/api/purchases/bills`', () => {
|
|||||||
|
|
||||||
expect(res.status).equals(200);
|
expect(res.status).equals(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE: `/api/purchases/bills/:id`', () => {
|
describe('DELETE: `/api/purchases/bills/:id`', () => {
|
||||||
|
|||||||
@@ -426,7 +426,7 @@ describe('route: `/sales/estimates`', () => {
|
|||||||
|
|
||||||
|
|
||||||
describe('GET: `/sales/estimates`', () => {
|
describe('GET: `/sales/estimates`', () => {
|
||||||
it.only('Should retrieve sales estimates.', async () => {
|
it('Should retrieve sales estimates.', async () => {
|
||||||
const res = await request()
|
const res = await request()
|
||||||
.get('/api/sales/estimates')
|
.get('/api/sales/estimates')
|
||||||
.set('x-access-token', loginRes.body.token)
|
.set('x-access-token', loginRes.body.token)
|
||||||
|
|||||||
Reference in New Issue
Block a user