mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 12:50:38 +00:00
fix: refactoring invoice calc cost service.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { query, ValidationChain } from 'express-validator';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import BaseController from '../BaseController';
|
||||
@@ -48,7 +48,7 @@ export default class GeneralLedgerReportController extends BaseController{
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
*/
|
||||
async generalLedger(req: Request, res: Response) {
|
||||
async generalLedger(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, settings } = req;
|
||||
const filter = this.matchedQueryData(req);
|
||||
|
||||
@@ -68,7 +68,7 @@ export default class GeneralLedgerReportController extends BaseController{
|
||||
query: this.transfromToResponse(query),
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Request, Response, Router } from 'express';
|
||||
import { Request, Response, Router, NextFunction } from 'express';
|
||||
import { castArray } from 'lodash';
|
||||
import { query, oneOf } from 'express-validator';
|
||||
import JournalSheetService from 'services/FinancialStatements/JournalSheet/JournalSheetService';
|
||||
@@ -55,7 +55,7 @@ export default class JournalSheetController extends BaseController {
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
*/
|
||||
async journal(req: Request, res: Response) {
|
||||
async journal(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, settings } = req;
|
||||
let filter = this.matchedQueryData(req);
|
||||
|
||||
@@ -76,7 +76,7 @@ export default class JournalSheetController extends BaseController {
|
||||
query: this.transfromToResponse(query),
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ export default class ProfitLossSheetController extends BaseController {
|
||||
query: this.transfromToResponse(query),
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,6 @@ export default class BillsPayments extends BaseController {
|
||||
message: 'Payment made has been created successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,12 +248,13 @@ export default class SaleInvoicesController extends BaseController {
|
||||
*/
|
||||
async getSaleInvoice(req: Request, res: Response, next: NextFunction) {
|
||||
const { id: saleInvoiceId } = req.params;
|
||||
const { tenantId } = req;
|
||||
const { tenantId, user } = req;
|
||||
|
||||
try {
|
||||
const saleInvoice = await this.saleInvoiceService.getSaleInvoice(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
saleInvoiceId,
|
||||
user
|
||||
);
|
||||
return res.status(200).send({ sale_invoice: saleInvoice });
|
||||
} catch (error) {
|
||||
|
||||
@@ -11,7 +11,6 @@ exports.up = function(knex) {
|
||||
table.string('contact_type').nullable().index();
|
||||
table.integer('contact_id').unsigned().nullable().index();
|
||||
table.string('note');
|
||||
table.boolean('draft').defaultTo(false);
|
||||
table.integer('user_id').unsigned().index();
|
||||
table.integer('index').unsigned();
|
||||
table.date('date').index();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
|
||||
export type TInventoryTransactionDirection = 'IN' | 'OUT';
|
||||
|
||||
export interface IInventoryTransaction {
|
||||
id?: number,
|
||||
date: Date,
|
||||
direction: string,
|
||||
direction: TInventoryTransactionDirection,
|
||||
itemId: number,
|
||||
quantity: number,
|
||||
rate: number,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
export type IItemEntryTransactionType = 'SaleInvoice' | 'Bill' | 'SaleReceipt';
|
||||
|
||||
export interface IItemEntry {
|
||||
id?: number,
|
||||
|
||||
@@ -1,40 +1,50 @@
|
||||
import { ISalesInvoicesFilter } from "./SaleInvoice";
|
||||
|
||||
import { ISalesInvoicesFilter } from './SaleInvoice';
|
||||
|
||||
export interface ISaleReceipt {
|
||||
id?: number,
|
||||
customerId: number,
|
||||
depositAccountId: number,
|
||||
receiptDate: Date,
|
||||
sendToEmail: string,
|
||||
referenceNo: string,
|
||||
receiptMessage: string,
|
||||
receiptNumber: string,
|
||||
statement: string,
|
||||
closedAt: Date|string,
|
||||
entries: any[],
|
||||
};
|
||||
id?: number;
|
||||
customerId: number;
|
||||
depositAccountId: number;
|
||||
receiptDate: Date;
|
||||
sendToEmail: string;
|
||||
referenceNo: string;
|
||||
receiptMessage: string;
|
||||
receiptNumber: string;
|
||||
amount: number;
|
||||
statement: string;
|
||||
closedAt: Date | string;
|
||||
entries: any[];
|
||||
}
|
||||
|
||||
export interface ISalesReceiptsFilter {
|
||||
|
||||
};
|
||||
export interface ISalesReceiptsFilter {}
|
||||
|
||||
export interface ISaleReceiptDTO {
|
||||
customerId: number,
|
||||
depositAccountId: number,
|
||||
receiptDate: Date,
|
||||
sendToEmail: string,
|
||||
referenceNo: string,
|
||||
receiptMessage: string,
|
||||
statement: string,
|
||||
closed: boolean,
|
||||
entries: any[],
|
||||
};
|
||||
customerId: number;
|
||||
depositAccountId: number;
|
||||
receiptDate: Date;
|
||||
sendToEmail: string;
|
||||
referenceNo: string;
|
||||
receiptMessage: string;
|
||||
statement: string;
|
||||
closed: boolean;
|
||||
entries: any[];
|
||||
}
|
||||
|
||||
export interface ISalesReceiptService {
|
||||
createSaleReceipt(tenantId: number, saleReceiptDTO: ISaleReceiptDTO): Promise<void>;
|
||||
createSaleReceipt(
|
||||
tenantId: number,
|
||||
saleReceiptDTO: ISaleReceiptDTO
|
||||
): Promise<void>;
|
||||
|
||||
editSaleReceipt(tenantId: number, saleReceiptId: number): Promise<void>;
|
||||
|
||||
deleteSaleReceipt(tenantId: number, saleReceiptId: number): Promise<void>;
|
||||
salesReceiptsList(tennatid: number, salesReceiptsFilter: ISalesReceiptsFilter): Promise<{ salesReceipts: ISaleReceipt[], pagination: IPaginationMeta, filterMeta: IFilterMeta }>;
|
||||
};
|
||||
|
||||
salesReceiptsList(
|
||||
tennatid: number,
|
||||
salesReceiptsFilter: ISalesReceiptsFilter
|
||||
): Promise<{
|
||||
salesReceipts: ISaleReceipt[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ export default class ComputeItemCostJob {
|
||||
|
||||
/**
|
||||
* The job handler.
|
||||
* @param {} -
|
||||
*/
|
||||
public async handler(job, done: Function): Promise<void> {
|
||||
const Logger = Container.get('logger');
|
||||
@@ -51,7 +50,6 @@ export default class ComputeItemCostJob {
|
||||
|
||||
/**
|
||||
* Handle the job started.
|
||||
* @param {Job} job - .
|
||||
*/
|
||||
async onJobStart(job) {
|
||||
const { startingDate, itemId, tenantId } = job.attrs.data;
|
||||
|
||||
@@ -12,15 +12,22 @@ export default class MailNotificationSubscribeEnd {
|
||||
const subscriptionService = Container.get(SubscriptionService);
|
||||
const Logger = Container.get('logger');
|
||||
|
||||
Logger.info(`Send mail notification subscription end soon - started: ${job.attrs.data}`);
|
||||
Logger.info(
|
||||
`Send mail notification subscription end soon - started: ${job.attrs.data}`
|
||||
);
|
||||
|
||||
try {
|
||||
subscriptionService.mailMessages.sendRemainingTrialPeriod(
|
||||
phoneNumber, remainingDays,
|
||||
phoneNumber,
|
||||
remainingDays
|
||||
);
|
||||
Logger.info(
|
||||
`Send mail notification subscription end soon - finished: ${job.attrs.data}`
|
||||
);
|
||||
} catch (error) {
|
||||
Logger.info(
|
||||
`Send mail notification subscription end soon - failed: ${job.attrs.data}, error: ${e}`
|
||||
);
|
||||
Logger.info(`Send mail notification subscription end soon - finished: ${job.attrs.data}`);
|
||||
} catch(error) {
|
||||
Logger.info(`Send mail notification subscription end soon - failed: ${job.attrs.data}, error: ${e}`);
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import SubscriptionService from 'services/Subscription/Subscription';
|
||||
|
||||
export default class MailNotificationTrialEnd {
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {Job} job -
|
||||
*/
|
||||
handler(job) {
|
||||
@@ -12,16 +12,23 @@ export default class MailNotificationTrialEnd {
|
||||
const subscriptionService = Container.get(SubscriptionService);
|
||||
const Logger = Container.get('logger');
|
||||
|
||||
Logger.info(`Send mail notification subscription end soon - started: ${job.attrs.data}`);
|
||||
Logger.info(
|
||||
`Send mail notification subscription end soon - started: ${job.attrs.data}`
|
||||
);
|
||||
|
||||
try {
|
||||
subscriptionService.mailMessages.sendRemainingTrialPeriod(
|
||||
phoneNumber, remainingDays,
|
||||
phoneNumber,
|
||||
remainingDays
|
||||
);
|
||||
Logger.info(
|
||||
`Send mail notification subscription end soon - finished: ${job.attrs.data}`
|
||||
);
|
||||
} catch (error) {
|
||||
Logger.info(
|
||||
`Send mail notification subscription end soon - failed: ${job.attrs.data}, error: ${e}`
|
||||
);
|
||||
Logger.info(`Send mail notification subscription end soon - finished: ${job.attrs.data}`);
|
||||
} catch(error) {
|
||||
Logger.info(`Send mail notification subscription end soon - failed: ${job.attrs.data}, error: ${e}`);
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,28 @@ import 'subscribers/organization';
|
||||
import 'subscribers/inviteUser';
|
||||
import 'subscribers/manualJournals';
|
||||
import 'subscribers/expenses';
|
||||
import 'subscribers/bills';
|
||||
import 'subscribers/saleInvoices';
|
||||
|
||||
import 'subscribers/Bills';
|
||||
import 'subscribers/Bills/SyncItemsQuantity';
|
||||
import 'subscribers/Bills/SyncVendorsBalances';
|
||||
import 'subscribers/Bills/WriteJournalEntries';
|
||||
import 'subscribers/Bills/WriteInventoryTransactions';
|
||||
|
||||
import 'subscribers/SaleInvoices';
|
||||
import 'subscribers/SaleInvoices/SyncCustomersBalance';
|
||||
import 'subscribers/SaleInvoices/SyncItemsQuantity';
|
||||
import 'subscribers/SaleInvoices/WriteInventoryTransactions';
|
||||
import 'subscribers/SaleInvoices/WriteJournalEntries';
|
||||
|
||||
import 'subscribers/SaleReceipt';
|
||||
import 'subscribers/SaleReceipt/SyncItemsQuantity';
|
||||
import 'subscribers/SaleReceipt/WriteInventoryTransactions';
|
||||
import 'subscribers/SaleReceipt/WriteJournalEntries';
|
||||
|
||||
import 'subscribers/customers';
|
||||
import 'subscribers/vendors';
|
||||
import 'subscribers/paymentMades';
|
||||
import 'subscribers/paymentReceives';
|
||||
import 'subscribers/saleEstimates';
|
||||
import 'subscribers/saleReceipts';
|
||||
import 'subscribers/inventory';
|
||||
import 'subscribers/items';
|
||||
@@ -129,8 +129,6 @@ export default class Vendor extends TenantModel {
|
||||
{ key: 'unpaid', label: 'Unpaid' },
|
||||
],
|
||||
query: (query, role) => {
|
||||
console.log(role);
|
||||
|
||||
switch(role.value) {
|
||||
case 'active':
|
||||
query.modify('active');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { sumBy, chain } from 'lodash';
|
||||
import moment, { LongDateFormatKey } from 'moment';
|
||||
import { IBill, IManualJournalEntry, ISystemUser } from 'interfaces';
|
||||
import { IBill, IManualJournalEntry, ISaleReceipt, ISystemUser } from 'interfaces';
|
||||
import JournalPoster from './JournalPoster';
|
||||
import JournalEntry from './JournalEntry';
|
||||
import { AccountTransaction } from 'models';
|
||||
@@ -425,4 +425,51 @@ export default class JournalCommands {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the sale invoice income journal entries.
|
||||
* -----
|
||||
* - Deposit account -> Debit -> XXXX
|
||||
* - Income -> Credit -> XXXX
|
||||
*
|
||||
* @param {ISaleInvoice} saleInvoice
|
||||
* @param {number} receivableAccountsId
|
||||
* @param {number} authorizedUserId
|
||||
*/
|
||||
async saleReceiptIncomeEntries(
|
||||
saleReceipt: ISaleReceipt & {
|
||||
entries: IItemEntry & { item: IItem };
|
||||
},
|
||||
): Promise<void> {
|
||||
const commonEntry = {
|
||||
referenceType: 'SaleReceipt',
|
||||
referenceId: saleReceipt.id,
|
||||
date: saleReceipt.receiptDate,
|
||||
userId: saleReceipt.userId,
|
||||
};
|
||||
// XXX Debit - Deposit account.
|
||||
const depositEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
debit: saleReceipt.amount,
|
||||
account: saleReceipt.depositAccountId,
|
||||
index: 1,
|
||||
});
|
||||
this.journal.debit(depositEntry);
|
||||
|
||||
saleReceipt.entries.forEach(
|
||||
(entry: IItemEntry & { item: IItem }, index: number) => {
|
||||
const income: number = entry.quantity * entry.rate;
|
||||
|
||||
// XXX Credit - Income account.
|
||||
const incomeEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
credit: income,
|
||||
account: entry.item.sellAccountId,
|
||||
note: entry.description,
|
||||
index: index + 2,
|
||||
});
|
||||
this.journal.credit(incomeEntry);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,15 @@ import {
|
||||
import {
|
||||
IInventoryLotCost,
|
||||
IInventoryTransaction,
|
||||
IItem,
|
||||
TInventoryTransactionDirection,
|
||||
IItemEntry,
|
||||
IItemEntryTransactionType,
|
||||
} from 'interfaces';
|
||||
import InventoryAverageCost from 'services/Inventory/InventoryAverageCost';
|
||||
import InventoryCostLotTracker from 'services/Inventory/InventoryCostLotTracker';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import events from 'subscribers/events';
|
||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
|
||||
type TCostMethod = 'FIFO' | 'LIFO' | 'AVG';
|
||||
|
||||
@@ -26,22 +28,23 @@ export default class InventoryService {
|
||||
@EventDispatcher()
|
||||
eventDispatcher: EventDispatcherInterface;
|
||||
|
||||
@Inject()
|
||||
itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
/**
|
||||
* Transforms the items entries to inventory transactions.
|
||||
*/
|
||||
transformItemEntriesToInventory(
|
||||
itemEntries: IItemEntry[],
|
||||
transactionType: string,
|
||||
transactionId: number,
|
||||
direction: 'IN' | 'OUT',
|
||||
direction: TInventoryTransactionDirection,
|
||||
date: Date | string,
|
||||
lotNumber: number
|
||||
) {
|
||||
return itemEntries.map((entry: IItemEntry) => ({
|
||||
...pick(entry, ['itemId', 'quantity', 'rate']),
|
||||
lotNumber,
|
||||
transactionType,
|
||||
transactionId,
|
||||
transactionType: entry.referenceType,
|
||||
transactionId: entry.referenceId,
|
||||
direction,
|
||||
date,
|
||||
entryId: entry.id,
|
||||
@@ -130,7 +133,6 @@ export default class InventoryService {
|
||||
tenantId,
|
||||
}
|
||||
);
|
||||
|
||||
// Triggers `onComputeItemCostJobScheduled` event.
|
||||
await this.eventDispatcher.dispatch(
|
||||
events.inventory.onComputeItemCostJobScheduled,
|
||||
@@ -157,10 +159,12 @@ export default class InventoryService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {IInventoryTransaction} inventoryEntry
|
||||
* @param {boolean} deleteOld
|
||||
* Writes the inventory transactiosn on the storage from the given
|
||||
* inventory transactions entries.
|
||||
*
|
||||
* @param {number} tenantId -
|
||||
* @param {IInventoryTransaction} inventoryEntry -
|
||||
* @param {boolean} deleteOld -
|
||||
*/
|
||||
async recordInventoryTransaction(
|
||||
tenantId: number,
|
||||
@@ -182,24 +186,104 @@ export default class InventoryService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the inventory transactions from items entries that have (inventory) type.
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {number} transactionId
|
||||
* @param {string} transactionType
|
||||
* @param {Date|string} transactionDate
|
||||
* @param {boolean} override
|
||||
*/
|
||||
async recordInventoryTransactionsFromItemsEntries(
|
||||
tenantId: number,
|
||||
transactionId: number,
|
||||
transactionType: IItemEntryTransactionType,
|
||||
transactionDate: Date | string,
|
||||
transactionDirection: TInventoryTransactionDirection,
|
||||
override: boolean = false
|
||||
): Promise<void> {
|
||||
// Gets the next inventory lot number.
|
||||
const lotNumber = this.getNextLotNumber(tenantId);
|
||||
|
||||
// Loads the inventory items entries of the given sale invoice.
|
||||
const inventoryEntries = await this.itemsEntriesService.getInventoryEntries(
|
||||
tenantId,
|
||||
transactionType,
|
||||
transactionId
|
||||
);
|
||||
// Can't continue if there is no entries has inventory items in the invoice.
|
||||
if (inventoryEntries.length <= 0) {
|
||||
return;
|
||||
}
|
||||
// Inventory transactions.
|
||||
const inventoryTranscations = this.transformItemEntriesToInventory(
|
||||
inventoryEntries,
|
||||
transactionDirection,
|
||||
transactionDate,
|
||||
lotNumber
|
||||
);
|
||||
// Records the inventory transactions of the given sale invoice.
|
||||
await this.recordInventoryTransactions(
|
||||
tenantId,
|
||||
inventoryTranscations,
|
||||
override
|
||||
);
|
||||
// Increment and save the next lot number settings.
|
||||
await this.incrementNextLotNumber(tenantId);
|
||||
|
||||
// Triggers `onInventoryTransactionsCreated` event.
|
||||
this.eventDispatcher.dispatch(
|
||||
events.inventory.onInventoryTransactionsCreated,
|
||||
{
|
||||
tenantId,
|
||||
inventoryEntries,
|
||||
transactionId,
|
||||
transactionType,
|
||||
transactionDate,
|
||||
transactionDirection,
|
||||
override,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given inventory transactions.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {string} transactionType
|
||||
* @param {number} transactionId
|
||||
* @return {Promise}
|
||||
* @return {Promise<{
|
||||
* oldInventoryTransactions: IInventoryTransaction[]
|
||||
* }>}
|
||||
*/
|
||||
async deleteInventoryTransactions(
|
||||
tenantId: number,
|
||||
transactionId: number,
|
||||
transactionType: string
|
||||
): Promise<void> {
|
||||
const { InventoryTransaction } = this.tenancy.models(tenantId);
|
||||
): Promise<{ oldInventoryTransactions: IInventoryTransaction[] }> {
|
||||
const { inventoryTransactionRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
await InventoryTransaction.query()
|
||||
.where('transaction_type', transactionType)
|
||||
.where('transaction_id', transactionId)
|
||||
.delete();
|
||||
// Retrieve the inventory transactions of the given sale invoice.
|
||||
const oldInventoryTransactions = await inventoryTransactionRepository.find({
|
||||
transactionId,
|
||||
transactionType,
|
||||
});
|
||||
// Deletes the inventory transactions by the given transaction type and id.
|
||||
await inventoryTransactionRepository.deleteBy({
|
||||
transactionType,
|
||||
transactionId,
|
||||
});
|
||||
// Triggers `onInventoryTransactionsDeleted` event.
|
||||
this.eventDispatcher.dispatch(
|
||||
events.inventory.onInventoryTransactionsDeleted,
|
||||
{
|
||||
tenantId,
|
||||
oldInventoryTransactions,
|
||||
transactionId,
|
||||
transactionType,
|
||||
}
|
||||
);
|
||||
return { oldInventoryTransactions };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { omit, sumBy, pick, map } from 'lodash';
|
||||
import { omit, sumBy } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
@@ -16,19 +16,17 @@ import { formatDateFields } from 'utils';
|
||||
import {
|
||||
IBillDTO,
|
||||
IBill,
|
||||
IItem,
|
||||
ISystemUser,
|
||||
IBillEditDTO,
|
||||
IPaginationMeta,
|
||||
IFilterMeta,
|
||||
IBillsFilter,
|
||||
IItemEntry,
|
||||
IInventoryTransaction,
|
||||
} from 'interfaces';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import ItemsService from 'services/Items/ItemsService';
|
||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
import JournalCommands from 'services/Accounting/JournalCommands';
|
||||
import JournalPosterService from 'services/Sales/JournalPosterService';
|
||||
|
||||
const ERRORS = {
|
||||
BILL_NOT_FOUND: 'BILL_NOT_FOUND',
|
||||
@@ -71,6 +69,9 @@ export default class BillsService extends SalesInvoicesCost {
|
||||
@Inject()
|
||||
itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
@Inject()
|
||||
journalPosterService: JournalPosterService;
|
||||
|
||||
/**
|
||||
* Validates whether the vendor is exist.
|
||||
* @async
|
||||
@@ -362,106 +363,6 @@ export default class BillsService extends SalesInvoicesCost {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the inventory transactions from the given bill input.
|
||||
* @param {Bill} bill - Bill model object.
|
||||
* @param {number} billId - Bill id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async recordInventoryTransactions(
|
||||
tenantId: number,
|
||||
billId: number,
|
||||
billDate: Date,
|
||||
override?: boolean
|
||||
): Promise<void> {
|
||||
// Retrieve the next inventory lot number.
|
||||
const lotNumber = this.inventoryService.getNextLotNumber(tenantId);
|
||||
|
||||
const inventoryEntries = await this.itemsEntriesService.getInventoryEntries(
|
||||
tenantId,
|
||||
'Bill',
|
||||
billId
|
||||
);
|
||||
// Can't continue if there is no entries has inventory items in the bill.
|
||||
if (inventoryEntries.length <= 0) return;
|
||||
|
||||
// Inventory transactions.
|
||||
const inventoryTranscations = this.inventoryService.transformItemEntriesToInventory(
|
||||
inventoryEntries,
|
||||
'Bill',
|
||||
billId,
|
||||
'IN',
|
||||
billDate,
|
||||
lotNumber
|
||||
);
|
||||
// Records the inventory transactions.
|
||||
await this.inventoryService.recordInventoryTransactions(
|
||||
tenantId,
|
||||
inventoryTranscations,
|
||||
override
|
||||
);
|
||||
// Save the next lot number settings.
|
||||
await this.inventoryService.incrementNextLotNumber(tenantId);
|
||||
|
||||
// Triggers `onInventoryTransactionsCreated` event.
|
||||
this.eventDispatcher.dispatch(events.bill.onInventoryTransactionsCreated, {
|
||||
tenantId,
|
||||
billId,
|
||||
billDate,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts the inventory transactions of the given bill id.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} billId - Bill id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async revertInventoryTransactions(tenantId: number, billId: number) {
|
||||
const { inventoryTransactionRepository } = this.tenancy.repositories(
|
||||
tenantId
|
||||
);
|
||||
|
||||
// Retrieve the inventory transactions of the given sale invoice.
|
||||
const oldInventoryTransactions = await inventoryTransactionRepository.find({
|
||||
transactionId: billId,
|
||||
transactionType: 'Bill',
|
||||
});
|
||||
await this.inventoryService.deleteInventoryTransactions(
|
||||
tenantId,
|
||||
billId,
|
||||
'Bill'
|
||||
);
|
||||
// Triggers 'onInventoryTransactionsDeleted' event.
|
||||
this.eventDispatcher.dispatch(
|
||||
events.bill.onInventoryTransactionsDeleted,
|
||||
{ tenantId, billId, oldInventoryTransactions }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the bill journal transactions.
|
||||
* @async
|
||||
* @param {IBill} bill
|
||||
* @param {Integer} billId
|
||||
*/
|
||||
public async recordJournalTransactions(
|
||||
tenantId: number,
|
||||
bill: IBill,
|
||||
override: boolean = false
|
||||
) {
|
||||
const journal = new JournalPoster(tenantId);
|
||||
const journalCommands = new JournalCommands(journal);
|
||||
|
||||
await journalCommands.bill(bill, override);
|
||||
|
||||
return Promise.all([
|
||||
journal.deleteEntries(),
|
||||
journal.saveEntries(),
|
||||
journal.saveBalance(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve bills data table list.
|
||||
* @param {number} tenantId -
|
||||
@@ -544,36 +445,6 @@ export default class BillsService extends SalesInvoicesCost {
|
||||
return bill;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules compute bill items cost based on each item cost method.
|
||||
* @param {number} tenantId -
|
||||
* @param {IBill} bill -
|
||||
* @return {Promise}
|
||||
*/
|
||||
public async scheduleComputeBillItemsCost(tenantId: number, billId: number) {
|
||||
const { Item, Bill } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the bill with associated entries.
|
||||
const bill = await Bill.query()
|
||||
.findById(billId)
|
||||
.withGraphFetched('entries');
|
||||
|
||||
// Retrieves inventory items only.
|
||||
const inventoryItems = await Item.query()
|
||||
.whereIn('id', map(bill.entries, 'itemId'))
|
||||
.where('type', 'inventory');
|
||||
|
||||
const inventoryItemsIds = map(inventoryItems, 'id');
|
||||
|
||||
if (inventoryItemsIds.length > 0) {
|
||||
await this.scheduleComputeItemsCost(
|
||||
tenantId,
|
||||
inventoryItemsIds,
|
||||
bill.billDate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the bill as open.
|
||||
* @param {number} tenantId
|
||||
@@ -588,10 +459,89 @@ export default class BillsService extends SalesInvoicesCost {
|
||||
if (oldBill.isOpen) {
|
||||
throw new ServiceError(ERRORS.BILL_ALREADY_OPEN);
|
||||
}
|
||||
|
||||
// Record the bill opened at on the storage.
|
||||
await Bill.query().findById(billId).patch({
|
||||
openedAt: moment().toMySqlDateTime(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the inventory transactions from the given bill input.
|
||||
* @param {Bill} bill - Bill model object.
|
||||
* @param {number} billId - Bill id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async recordInventoryTransactions(
|
||||
tenantId: number,
|
||||
bill: IBill,
|
||||
override?: boolean
|
||||
): Promise<void> {
|
||||
await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
|
||||
tenantId,
|
||||
bill.id,
|
||||
'Bill',
|
||||
bill.billDate,
|
||||
'IN',
|
||||
override
|
||||
);
|
||||
// Triggers `onInventoryTransactionsCreated` event.
|
||||
this.eventDispatcher.dispatch(events.bill.onInventoryTransactionsCreated, {
|
||||
tenantId,
|
||||
bill,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts the inventory transactions of the given bill id.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} billId - Bill id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async revertInventoryTransactions(tenantId: number, billId: number) {
|
||||
// Deletes the inventory transactions by the given reference id and type.
|
||||
await this.inventoryService.deleteInventoryTransactions(
|
||||
tenantId,
|
||||
billId,
|
||||
'Bill'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the bill journal transactions.
|
||||
* @async
|
||||
* @param {IBill} bill
|
||||
* @param {Integer} billId
|
||||
*/
|
||||
public async recordJournalTransactions(
|
||||
tenantId: number,
|
||||
bill: IBill,
|
||||
override: boolean = false
|
||||
) {
|
||||
const journal = new JournalPoster(tenantId);
|
||||
const journalCommands = new JournalCommands(journal);
|
||||
|
||||
await journalCommands.bill(bill, override);
|
||||
|
||||
return Promise.all([
|
||||
journal.deleteEntries(),
|
||||
journal.saveEntries(),
|
||||
journal.saveBalance(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts the bill journal entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} billId
|
||||
*/
|
||||
public async revertJournalEntries(
|
||||
tenantId: number,
|
||||
billId: number
|
||||
): Promise<void> {
|
||||
await this.journalPosterService.revertJournalTransactions(
|
||||
tenantId,
|
||||
billId,
|
||||
'Bill'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { omit, sumBy } from 'lodash';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IEstimatesFilter, IFilterMeta, IPaginationMeta, ISaleEstimate, ISaleEstimateDTO } from 'interfaces';
|
||||
import {
|
||||
IEstimatesFilter,
|
||||
IFilterMeta,
|
||||
IPaginationMeta,
|
||||
ISaleEstimate,
|
||||
ISaleEstimateDTO,
|
||||
} from 'interfaces';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -14,7 +20,6 @@ import { ServiceError } from 'exceptions';
|
||||
import CustomersService from 'services/Contacts/CustomersService';
|
||||
import moment from 'moment';
|
||||
|
||||
|
||||
const ERRORS = {
|
||||
SALE_ESTIMATE_NOT_FOUND: 'SALE_ESTIMATE_NOT_FOUND',
|
||||
CUSTOMER_NOT_FOUND: 'CUSTOMER_NOT_FOUND',
|
||||
@@ -24,8 +29,9 @@ const ERRORS = {
|
||||
SALE_ESTIMATE_CONVERTED_TO_INVOICE: 'SALE_ESTIMATE_CONVERTED_TO_INVOICE',
|
||||
SALE_ESTIMATE_ALREADY_REJECTED: 'SALE_ESTIMATE_ALREADY_REJECTED',
|
||||
SALE_ESTIMATE_ALREADY_APPROVED: 'SALE_ESTIMATE_ALREADY_APPROVED',
|
||||
SALE_ESTIMATE_NOT_DELIVERED: 'SALE_ESTIMATE_NOT_DELIVERED'
|
||||
SALE_ESTIMATE_NOT_DELIVERED: 'SALE_ESTIMATE_NOT_DELIVERED',
|
||||
};
|
||||
|
||||
/**
|
||||
* Sale estimate service.
|
||||
* @Service
|
||||
@@ -52,26 +58,32 @@ export default class SaleEstimateService {
|
||||
|
||||
/**
|
||||
* Retrieve sale estimate or throw service error.
|
||||
* @param {number} tenantId
|
||||
* @param {number} tenantId
|
||||
* @return {ISaleEstimate}
|
||||
*/
|
||||
async getSaleEstimateOrThrowError(tenantId: number, saleEstimateId: number) {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
const foundSaleEstimate = await SaleEstimate.query().findById(saleEstimateId);
|
||||
|
||||
const foundSaleEstimate = await SaleEstimate.query().findById(
|
||||
saleEstimateId
|
||||
);
|
||||
|
||||
if (!foundSaleEstimate) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_FOUND);
|
||||
}
|
||||
return foundSaleEstimate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate the estimate number unique on the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {Function} next
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {Function} next
|
||||
*/
|
||||
async validateEstimateNumberExistance(tenantId: number, estimateNumber: string, notEstimateId?: number) {
|
||||
async validateEstimateNumberExistance(
|
||||
tenantId: number,
|
||||
estimateNumber: string,
|
||||
notEstimateId?: number
|
||||
) {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
const foundSaleEstimate = await SaleEstimate.query()
|
||||
@@ -88,34 +100,35 @@ export default class SaleEstimateService {
|
||||
|
||||
/**
|
||||
* Transform DTO object ot model object.
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleEstimateDTO} saleEstimateDTO
|
||||
* @param {ISaleEstimate} oldSaleEstimate
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleEstimateDTO} saleEstimateDTO
|
||||
* @param {ISaleEstimate} oldSaleEstimate
|
||||
* @return {ISaleEstimate}
|
||||
*/
|
||||
transformDTOToModel(
|
||||
tenantId: number,
|
||||
estimateDTO: ISaleEstimateDTO,
|
||||
oldSaleEstimate?: ISaleEstimate,
|
||||
oldSaleEstimate?: ISaleEstimate
|
||||
): ISaleEstimate {
|
||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||
const amount = sumBy(estimateDTO.entries, e => ItemEntry.calcAmount(e));
|
||||
const amount = sumBy(estimateDTO.entries, (e) => ItemEntry.calcAmount(e));
|
||||
|
||||
return {
|
||||
amount,
|
||||
...formatDateFields(
|
||||
omit(estimateDTO, ['delivered', 'entries']),
|
||||
['estimateDate', 'expirationDate']
|
||||
),
|
||||
entries: estimateDTO.entries.map((entry) => ({
|
||||
...formatDateFields(omit(estimateDTO, ['delivered', 'entries']), [
|
||||
'estimateDate',
|
||||
'expirationDate',
|
||||
]),
|
||||
entries: estimateDTO.entries.map((entry) => ({
|
||||
reference_type: 'SaleEstimate',
|
||||
...omit(entry, ['total', 'amount', 'id']),
|
||||
})),
|
||||
|
||||
// Avoid rewrite the deliver date in edit mode when already published.
|
||||
...(estimateDTO.delivered && (!oldSaleEstimate?.deliveredAt)) && ({
|
||||
deliveredAt: moment().toMySqlDateTime(),
|
||||
}),
|
||||
...(estimateDTO.delivered &&
|
||||
!oldSaleEstimate?.deliveredAt && {
|
||||
deliveredAt: moment().toMySqlDateTime(),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,23 +152,35 @@ export default class SaleEstimateService {
|
||||
|
||||
// Validate estimate number uniquiness on the storage.
|
||||
if (estimateDTO.estimateNumber) {
|
||||
await this.validateEstimateNumberExistance(tenantId, estimateDTO.estimateNumber);
|
||||
await this.validateEstimateNumberExistance(
|
||||
tenantId,
|
||||
estimateDTO.estimateNumber
|
||||
);
|
||||
}
|
||||
// Retrieve the given customer or throw not found service error.
|
||||
await this.customersService.getCustomer(tenantId, estimateDTO.customerId);
|
||||
|
||||
// Validate items IDs existance on the storage.
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(tenantId, estimateDTO.entries);
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(
|
||||
tenantId,
|
||||
estimateDTO.entries
|
||||
);
|
||||
|
||||
// Validate non-sellable items.
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(tenantId, estimateDTO.entries);
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||
tenantId,
|
||||
estimateDTO.entries
|
||||
);
|
||||
|
||||
const saleEstimate = await SaleEstimate.query()
|
||||
.upsertGraphAndFetch({ ...estimateObj });
|
||||
const saleEstimate = await SaleEstimate.query().upsertGraphAndFetch({
|
||||
...estimateObj,
|
||||
});
|
||||
|
||||
this.logger.info('[sale_estimate] insert sale estimated success.');
|
||||
await this.eventDispatcher.dispatch(events.saleEstimate.onCreated, {
|
||||
tenantId, saleEstimate, saleEstimateId: saleEstimate.id,
|
||||
tenantId,
|
||||
saleEstimate,
|
||||
saleEstimateId: saleEstimate.id,
|
||||
});
|
||||
|
||||
return saleEstimate;
|
||||
@@ -175,38 +200,61 @@ export default class SaleEstimateService {
|
||||
estimateDTO: ISaleEstimateDTO
|
||||
): Promise<ISaleEstimate> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
const oldSaleEstimate = await this.getSaleEstimateOrThrowError(tenantId, estimateId);
|
||||
|
||||
const oldSaleEstimate = await this.getSaleEstimateOrThrowError(
|
||||
tenantId,
|
||||
estimateId
|
||||
);
|
||||
// Transform DTO object ot model object.
|
||||
const estimateObj = this.transformDTOToModel(tenantId, estimateDTO, oldSaleEstimate);
|
||||
|
||||
const estimateObj = this.transformDTOToModel(
|
||||
tenantId,
|
||||
estimateDTO,
|
||||
oldSaleEstimate
|
||||
);
|
||||
// Validate estimate number uniquiness on the storage.
|
||||
if (estimateDTO.estimateNumber) {
|
||||
await this.validateEstimateNumberExistance(tenantId, estimateDTO.estimateNumber, estimateId);
|
||||
await this.validateEstimateNumberExistance(
|
||||
tenantId,
|
||||
estimateDTO.estimateNumber,
|
||||
estimateId
|
||||
);
|
||||
}
|
||||
// Retrieve the given customer or throw not found service error.
|
||||
await this.customersService.getCustomer(tenantId, estimateDTO.customerId);
|
||||
|
||||
// Validate sale estimate entries existance.
|
||||
await this.itemsEntriesService.validateEntriesIdsExistance(tenantId, estimateId, 'SaleEstiamte', estimateDTO.entries);
|
||||
|
||||
await this.itemsEntriesService.validateEntriesIdsExistance(
|
||||
tenantId,
|
||||
estimateId,
|
||||
'SaleEstiamte',
|
||||
estimateDTO.entries
|
||||
);
|
||||
// Validate items IDs existance on the storage.
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(tenantId, estimateDTO.entries);
|
||||
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(
|
||||
tenantId,
|
||||
estimateDTO.entries
|
||||
);
|
||||
// Validate non-sellable items.
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(tenantId, estimateDTO.entries);
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||
tenantId,
|
||||
estimateDTO.entries
|
||||
);
|
||||
|
||||
this.logger.info('[sale_estimate] editing sale estimate on the storage.');
|
||||
const saleEstimate = await SaleEstimate.query()
|
||||
.upsertGraphAndFetch({
|
||||
id: estimateId,
|
||||
...estimateObj
|
||||
});
|
||||
const saleEstimate = await SaleEstimate.query().upsertGraphAndFetch({
|
||||
id: estimateId,
|
||||
...estimateObj,
|
||||
});
|
||||
|
||||
await this.eventDispatcher.dispatch(events.saleEstimate.onEdited, {
|
||||
tenantId, estimateId, saleEstimate, oldSaleEstimate,
|
||||
tenantId,
|
||||
estimateId,
|
||||
saleEstimate,
|
||||
oldSaleEstimate,
|
||||
});
|
||||
this.logger.info('[sale_estiamte] edited successfully', {
|
||||
tenantId,
|
||||
estimateId,
|
||||
});
|
||||
this.logger.info('[sale_estiamte] edited successfully', { tenantId, estimateId });
|
||||
|
||||
return saleEstimate;
|
||||
}
|
||||
@@ -218,28 +266,41 @@ export default class SaleEstimateService {
|
||||
* @param {IEstimate} estimateId
|
||||
* @return {void}
|
||||
*/
|
||||
public async deleteEstimate(tenantId: number, estimateId: number): Promise<void> {
|
||||
public async deleteEstimate(
|
||||
tenantId: number,
|
||||
estimateId: number
|
||||
): Promise<void> {
|
||||
const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve sale estimate or throw not found service error.
|
||||
const oldSaleEstimate = await this.getSaleEstimateOrThrowError(tenantId, estimateId);
|
||||
const oldSaleEstimate = await this.getSaleEstimateOrThrowError(
|
||||
tenantId,
|
||||
estimateId
|
||||
);
|
||||
|
||||
// Throw error if the sale estimate converted to sale invoice.
|
||||
if (oldSaleEstimate.convertedToInvoiceId) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_CONVERTED_TO_INVOICE);
|
||||
}
|
||||
|
||||
this.logger.info('[sale_estimate] delete sale estimate and associated entries from the storage.');
|
||||
this.logger.info(
|
||||
'[sale_estimate] delete sale estimate and associated entries from the storage.'
|
||||
);
|
||||
await ItemEntry.query()
|
||||
.where('reference_id', estimateId)
|
||||
.where('reference_type', 'SaleEstimate')
|
||||
.delete();
|
||||
|
||||
await SaleEstimate.query().where('id', estimateId).delete();
|
||||
this.logger.info('[sale_estimate] deleted successfully.', { tenantId, estimateId });
|
||||
this.logger.info('[sale_estimate] deleted successfully.', {
|
||||
tenantId,
|
||||
estimateId,
|
||||
});
|
||||
|
||||
await this.eventDispatcher.dispatch(events.saleEstimate.onDeleted, {
|
||||
tenantId, saleEstimateId: estimateId, oldSaleEstimate,
|
||||
tenantId,
|
||||
saleEstimateId: estimateId,
|
||||
oldSaleEstimate,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -255,7 +316,7 @@ export default class SaleEstimateService {
|
||||
.findById(estimateId)
|
||||
.withGraphFetched('entries')
|
||||
.withGraphFetched('customer');
|
||||
|
||||
|
||||
if (!estimate) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_FOUND);
|
||||
}
|
||||
@@ -270,19 +331,26 @@ export default class SaleEstimateService {
|
||||
public async estimatesList(
|
||||
tenantId: number,
|
||||
estimatesFilter: IEstimatesFilter
|
||||
): Promise<{ salesEstimates: ISaleEstimate[], pagination: IPaginationMeta, filterMeta: IFilterMeta }> {
|
||||
): Promise<{
|
||||
salesEstimates: ISaleEstimate[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(tenantId, SaleEstimate, estimatesFilter);
|
||||
|
||||
const { results, pagination } = await SaleEstimate.query().onBuild(builder => {
|
||||
builder.withGraphFetched('customer');
|
||||
builder.withGraphFetched('entries');
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
}).pagination(
|
||||
estimatesFilter.page - 1,
|
||||
estimatesFilter.pageSize,
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
SaleEstimate,
|
||||
estimatesFilter
|
||||
);
|
||||
|
||||
const { results, pagination } = await SaleEstimate.query()
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('customer');
|
||||
builder.withGraphFetched('entries');
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
})
|
||||
.pagination(estimatesFilter.page - 1, estimatesFilter.pageSize);
|
||||
|
||||
return {
|
||||
salesEstimates: results,
|
||||
pagination,
|
||||
@@ -299,12 +367,15 @@ export default class SaleEstimateService {
|
||||
async convertEstimateToInvoice(
|
||||
tenantId: number,
|
||||
estimateId: number,
|
||||
invoiceId: number,
|
||||
invoiceId: number
|
||||
): Promise<void> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve details of the given sale estimate.
|
||||
const saleEstimate = await this.getSaleEstimateOrThrowError(tenantId, estimateId);
|
||||
const saleEstimate = await this.getSaleEstimateOrThrowError(
|
||||
tenantId,
|
||||
estimateId
|
||||
);
|
||||
|
||||
await SaleEstimate.query().where('id', estimateId).patch({
|
||||
convertedToInvoiceId: invoiceId,
|
||||
@@ -320,16 +391,18 @@ export default class SaleEstimateService {
|
||||
*/
|
||||
async unlinkConvertedEstimateFromInvoice(
|
||||
tenantId: number,
|
||||
invoiceId: number,
|
||||
invoiceId: number
|
||||
): Promise<void> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
await SaleEstimate.query().where({
|
||||
convertedToInvoiceId: invoiceId,
|
||||
}).patch({
|
||||
convertedToInvoiceId: null,
|
||||
convertedToInvoiceAt: null,
|
||||
});
|
||||
await SaleEstimate.query()
|
||||
.where({
|
||||
convertedToInvoiceId: invoiceId,
|
||||
})
|
||||
.patch({
|
||||
convertedToInvoiceId: null,
|
||||
convertedToInvoiceAt: null,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,12 +412,15 @@ export default class SaleEstimateService {
|
||||
*/
|
||||
public async deliverSaleEstimate(
|
||||
tenantId: number,
|
||||
saleEstimateId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<void> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve details of the given sale estimate id.
|
||||
const saleEstimate = await this.getSaleEstimateOrThrowError(tenantId, saleEstimateId);
|
||||
const saleEstimate = await this.getSaleEstimateOrThrowError(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
);
|
||||
|
||||
// Throws error in case the sale estimate already published.
|
||||
if (saleEstimate.isDelivered) {
|
||||
@@ -352,29 +428,32 @@ export default class SaleEstimateService {
|
||||
}
|
||||
// Record the delivered at on the storage.
|
||||
await SaleEstimate.query().where('id', saleEstimateId).patch({
|
||||
deliveredAt: moment().toMySqlDateTime()
|
||||
deliveredAt: moment().toMySqlDateTime(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the sale estimate as approved from the customer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
*/
|
||||
public async approveSaleEstimate(
|
||||
tenantId: number,
|
||||
saleEstimateId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<void> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve details of the given sale estimate id.
|
||||
const saleEstimate = await this.getSaleEstimateOrThrowError(tenantId, saleEstimateId);
|
||||
const saleEstimate = await this.getSaleEstimateOrThrowError(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
);
|
||||
|
||||
// Throws error in case the sale estimate still not delivered to customer.
|
||||
if (!saleEstimate.isDelivered) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_DELIVERED);
|
||||
}
|
||||
// Throws error in case the sale estimate already approved.
|
||||
// Throws error in case the sale estimate already approved.
|
||||
if (saleEstimate.isApproved) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_ALREADY_APPROVED);
|
||||
}
|
||||
@@ -386,17 +465,20 @@ export default class SaleEstimateService {
|
||||
|
||||
/**
|
||||
* Mark the sale estimate as rejected from the customer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
*/
|
||||
public async rejectSaleEstimate(
|
||||
tenantId: number,
|
||||
saleEstimateId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<void> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve details of the given sale estimate id.
|
||||
const saleEstimate = await this.getSaleEstimateOrThrowError(tenantId, saleEstimateId);
|
||||
const saleEstimate = await this.getSaleEstimateOrThrowError(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
);
|
||||
|
||||
// Throws error in case the sale estimate still not delivered to customer.
|
||||
if (!saleEstimate.isDelivered) {
|
||||
@@ -412,4 +494,4 @@ export default class SaleEstimateService {
|
||||
approvedAt: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ export default class SaleInvoicesService {
|
||||
}
|
||||
// Record the delivered at on the storage.
|
||||
await saleInvoiceRepository.update(
|
||||
{ deliveredAt: moment().toMySqlDateTime(), },
|
||||
{ deliveredAt: moment().toMySqlDateTime() },
|
||||
{ id: saleInvoiceId }
|
||||
);
|
||||
// Triggers `onSaleInvoiceDelivered` event.
|
||||
@@ -416,48 +416,17 @@ export default class SaleInvoicesService {
|
||||
*/
|
||||
public async recordInventoryTranscactions(
|
||||
tenantId: number,
|
||||
saleInvoiceId: number,
|
||||
saleInvoiceDate: Date,
|
||||
saleInvoice: ISaleInvoice,
|
||||
override?: boolean
|
||||
): Promise<void> {
|
||||
// Gets the next inventory lot number.
|
||||
const lotNumber = this.inventoryService.getNextLotNumber(tenantId);
|
||||
|
||||
// Loads the inventory items entries of the given sale invoice.
|
||||
const inventoryEntries = await this.itemsEntriesService.getInventoryEntries(
|
||||
await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
|
||||
tenantId,
|
||||
saleInvoice.id,
|
||||
'SaleInvoice',
|
||||
saleInvoiceId
|
||||
);
|
||||
// Can't continue if there is no entries has inventory items in the invoice.
|
||||
if (inventoryEntries.length <= 0) return;
|
||||
|
||||
// Inventory transactions.
|
||||
const inventoryTranscations = this.inventoryService.transformItemEntriesToInventory(
|
||||
inventoryEntries,
|
||||
'SaleInvoice',
|
||||
saleInvoiceId,
|
||||
saleInvoice.invoiceDate,
|
||||
'OUT',
|
||||
saleInvoiceDate,
|
||||
lotNumber
|
||||
);
|
||||
// Records the inventory transactions of the given sale invoice.
|
||||
await this.inventoryService.recordInventoryTransactions(
|
||||
tenantId,
|
||||
inventoryTranscations,
|
||||
override
|
||||
);
|
||||
// Increment and save the next lot number settings.
|
||||
await this.inventoryService.incrementNextLotNumber(tenantId);
|
||||
|
||||
// Triggers `onInventoryTransactionsCreated` event.
|
||||
await this.eventDispatcher.dispatch(
|
||||
events.saleInvoice.onInventoryTransactionsCreated,
|
||||
{
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -491,7 +460,7 @@ export default class SaleInvoicesService {
|
||||
await Promise.all([
|
||||
journal.deleteEntries(),
|
||||
journal.saveBalance(),
|
||||
journal.saveEntries()
|
||||
journal.saveEntries(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -505,25 +474,14 @@ export default class SaleInvoicesService {
|
||||
tenantId: number,
|
||||
saleInvoiceId: number
|
||||
): Promise<void> {
|
||||
const { inventoryTransactionRepository } = this.tenancy.repositories(
|
||||
tenantId
|
||||
);
|
||||
// Retrieve the inventory transactions of the given sale invoice.
|
||||
const oldInventoryTransactions = await inventoryTransactionRepository.find({
|
||||
transactionId: saleInvoiceId,
|
||||
transactionType: 'SaleInvoice',
|
||||
});
|
||||
// Delete the inventory transaction of the given sale invoice.
|
||||
await this.inventoryService.deleteInventoryTransactions(
|
||||
const {
|
||||
oldInventoryTransactions,
|
||||
} = await this.inventoryService.deleteInventoryTransactions(
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
'SaleInvoice'
|
||||
);
|
||||
// Triggers 'onInventoryTransactionsDeleted' event.
|
||||
this.eventDispatcher.dispatch(
|
||||
events.saleInvoice.onInventoryTransactionsDeleted,
|
||||
{ tenantId, saleInvoiceId, oldInventoryTransactions }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -545,8 +503,9 @@ export default class SaleInvoicesService {
|
||||
|
||||
/**
|
||||
* Retrieve sale invoice with associated entries.
|
||||
* @async
|
||||
* @param {Number} saleInvoiceId
|
||||
* @param {Number} saleInvoiceId -
|
||||
* @param {ISystemUser} authorizedUser -
|
||||
* @return {Promise<ISaleInvoice>}
|
||||
*/
|
||||
public async getSaleInvoice(
|
||||
tenantId: number,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Container, Service, Inject } from 'typedi';
|
||||
import { map } from 'lodash';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import InventoryService from 'services/Inventory/Inventory';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import { ISaleInvoice, IItemEntry, IInventoryLotCost, IItem } from 'interfaces';
|
||||
import { IInventoryLotCost, IItem } from 'interfaces';
|
||||
import JournalCommands from 'services/Accounting/JournalCommands';
|
||||
|
||||
@Service()
|
||||
@@ -39,86 +38,6 @@ export default class SaleInvoicesCost {
|
||||
return Promise.all([...asyncOpers]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules compute sale invoice items cost based on each item
|
||||
* cost method.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {ISaleInvoice} saleInvoiceId - Sale invoice id.
|
||||
* @param {boolean} override - Allow to override old computes in edit mode.
|
||||
* @return {Promise}
|
||||
*/
|
||||
async scheduleComputeCostByInvoiceId(
|
||||
tenantId: number,
|
||||
saleInvoiceId: number
|
||||
) {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the sale invoice with associated entries.
|
||||
const saleInvoice: ISaleInvoice = await SaleInvoice.query()
|
||||
.findById(saleInvoiceId)
|
||||
.withGraphFetched('entries');
|
||||
|
||||
// Schedule compute inventory items cost by the given invoice model object.
|
||||
return this.scheduleComputeCostByEntries(
|
||||
tenantId,
|
||||
saleInvoice.entries,
|
||||
saleInvoice.invoiceDate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the compute inventory items cost by the given bill id.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} billId - Bill id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async scheduleComputeCostByBillId(
|
||||
tenantId: number,
|
||||
billId: number
|
||||
): Promise<void> {
|
||||
const { Bill } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the bill with associated entries.
|
||||
const bill = await Bill.query()
|
||||
.findById(billId)
|
||||
.withGraphFetched('entries');
|
||||
|
||||
return this.scheduleComputeCostByEntries(
|
||||
tenantId,
|
||||
bill.entries,
|
||||
bill.billDate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the compute inventory items by the given invoice.
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleInvoice & { entries: IItemEntry[] }} saleInvoice
|
||||
* @param {boolean} override
|
||||
*/
|
||||
async scheduleComputeCostByEntries(
|
||||
tenantId: number,
|
||||
entries: IItemEntry[],
|
||||
startingDate: Date
|
||||
) {
|
||||
const { Item } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the inventory items that associated to the sale invoice entries.
|
||||
const inventoryItems = await Item.query()
|
||||
.whereIn('id', map(entries, 'itemId'))
|
||||
.where('type', 'inventory');
|
||||
|
||||
const inventoryItemsIds = map(inventoryItems, 'id');
|
||||
|
||||
if (inventoryItemsIds.length > 0) {
|
||||
await this.scheduleComputeCostByItemsIds(
|
||||
tenantId,
|
||||
inventoryItemsIds,
|
||||
startingDate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule writing journal entries.
|
||||
* @param {Date} startingDate
|
||||
|
||||
@@ -6,7 +6,9 @@ import {
|
||||
EventDispatcherInterface,
|
||||
} from 'decorators/eventDispatcher';
|
||||
import events from 'subscribers/events';
|
||||
import { ISaleReceipt, ISaleReceiptDTO } from 'interfaces';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import JournalCommands from 'services/Accounting/JournalCommands';
|
||||
import { ISaleReceipt, ISaleReceiptDTO, IItemEntry, IItem } from 'interfaces';
|
||||
import JournalPosterService from 'services/Sales/JournalPosterService';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import { formatDateFields } from 'utils';
|
||||
@@ -15,15 +17,16 @@ import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
import { ItemEntry } from 'models';
|
||||
|
||||
import InventoryService from 'services/Inventory/Inventory';
|
||||
|
||||
const ERRORS = {
|
||||
SALE_RECEIPT_NOT_FOUND: 'SALE_RECEIPT_NOT_FOUND',
|
||||
DEPOSIT_ACCOUNT_NOT_FOUND: 'DEPOSIT_ACCOUNT_NOT_FOUND',
|
||||
DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET: 'DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET',
|
||||
SALE_RECEIPT_NUMBER_NOT_UNIQUE: 'SALE_RECEIPT_NUMBER_NOT_UNIQUE',
|
||||
SALE_RECEIPT_IS_ALREADY_CLOSED: 'SALE_RECEIPT_IS_ALREADY_CLOSED'
|
||||
SALE_RECEIPT_IS_ALREADY_CLOSED: 'SALE_RECEIPT_IS_ALREADY_CLOSED',
|
||||
};
|
||||
|
||||
@Service()
|
||||
export default class SalesReceiptService {
|
||||
@Inject()
|
||||
@@ -38,6 +41,9 @@ export default class SalesReceiptService {
|
||||
@Inject()
|
||||
itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
@Inject()
|
||||
inventoryService: InventoryService;
|
||||
|
||||
@EventDispatcher()
|
||||
eventDispatcher: EventDispatcherInterface;
|
||||
|
||||
@@ -46,37 +52,56 @@ export default class SalesReceiptService {
|
||||
|
||||
/**
|
||||
* Validate whether sale receipt exists on the storage.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} saleReceiptId -
|
||||
* @param {number} tenantId -
|
||||
* @param {number} saleReceiptId -
|
||||
*/
|
||||
async getSaleReceiptOrThrowError(tenantId: number, saleReceiptId: number) {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
|
||||
this.logger.info('[sale_receipt] trying to validate existance.', { tenantId, saleReceiptId });
|
||||
const foundSaleReceipt = await SaleReceipt.query().findById(saleReceiptId);
|
||||
this.logger.info('[sale_receipt] trying to validate existance.', {
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
});
|
||||
const foundSaleReceipt = await SaleReceipt.query()
|
||||
.findById(saleReceiptId)
|
||||
.withGraphFetched('entries');
|
||||
|
||||
if (!foundSaleReceipt) {
|
||||
this.logger.info('[sale_receipt] not found on the storage.', { tenantId, saleReceiptId });
|
||||
this.logger.info('[sale_receipt] not found on the storage.', {
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
});
|
||||
throw new ServiceError(ERRORS.SALE_RECEIPT_NOT_FOUND);
|
||||
}
|
||||
return foundSaleReceipt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate whether sale receipt deposit account exists on the storage.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} tenantId -
|
||||
* @param {number} accountId -
|
||||
*/
|
||||
async validateReceiptDepositAccountExistance(tenantId: number, accountId: number) {
|
||||
const { accountRepository, accountTypeRepository } = this.tenancy.repositories(tenantId);
|
||||
async validateReceiptDepositAccountExistance(
|
||||
tenantId: number,
|
||||
accountId: number
|
||||
) {
|
||||
const {
|
||||
accountRepository,
|
||||
accountTypeRepository,
|
||||
} = this.tenancy.repositories(tenantId);
|
||||
const depositAccount = await accountRepository.findOneById(accountId);
|
||||
|
||||
if (!depositAccount) {
|
||||
throw new ServiceError(ERRORS.DEPOSIT_ACCOUNT_NOT_FOUND);
|
||||
}
|
||||
const depositAccountType = await accountTypeRepository.findOneById(depositAccount.accountTypeId);
|
||||
const depositAccountType = await accountTypeRepository.findOneById(
|
||||
depositAccount.accountTypeId
|
||||
);
|
||||
|
||||
if (!depositAccountType || depositAccountType.childRoot === 'current_asset') {
|
||||
if (
|
||||
!depositAccountType ||
|
||||
depositAccountType.childRoot === 'current_asset'
|
||||
) {
|
||||
throw new ServiceError(ERRORS.DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET);
|
||||
}
|
||||
}
|
||||
@@ -87,10 +112,17 @@ export default class SalesReceiptService {
|
||||
* @param {string} receiptNumber -
|
||||
* @param {number} notReceiptId -
|
||||
*/
|
||||
async validateReceiptNumberUnique(tenantId: number, receiptNumber: string, notReceiptId?: number) {
|
||||
async validateReceiptNumberUnique(
|
||||
tenantId: number,
|
||||
receiptNumber: string,
|
||||
notReceiptId?: number
|
||||
) {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
|
||||
this.logger.info('[sale_receipt] validate receipt number uniquiness.', { tenantId, receiptNumber });
|
||||
this.logger.info('[sale_receipt] validate receipt number uniquiness.', {
|
||||
tenantId,
|
||||
receiptNumber,
|
||||
});
|
||||
const saleReceipt = await SaleReceipt.query()
|
||||
.findOne('receipt_number', receiptNumber)
|
||||
.onBuild((builder) => {
|
||||
@@ -98,9 +130,11 @@ export default class SalesReceiptService {
|
||||
builder.whereNot('id', notReceiptId);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (saleReceipt) {
|
||||
this.logger.info('[sale_receipt] sale receipt number not unique.', { tenantId });
|
||||
this.logger.info('[sale_receipt] sale receipt number not unique.', {
|
||||
tenantId,
|
||||
});
|
||||
throw new ServiceError(ERRORS.SALE_RECEIPT_NUMBER_NOT_UNIQUE);
|
||||
}
|
||||
}
|
||||
@@ -115,18 +149,20 @@ export default class SalesReceiptService {
|
||||
saleReceiptDTO: ISaleReceiptDTO,
|
||||
oldSaleReceipt?: ISaleReceipt
|
||||
): ISaleReceipt {
|
||||
const amount = sumBy(saleReceiptDTO.entries, e => ItemEntry.calcAmount(e));
|
||||
const amount = sumBy(saleReceiptDTO.entries, (e) =>
|
||||
ItemEntry.calcAmount(e)
|
||||
);
|
||||
|
||||
return {
|
||||
amount,
|
||||
...formatDateFields(
|
||||
omit(saleReceiptDTO, ['closed', 'entries']),
|
||||
['receiptDate']
|
||||
),
|
||||
...formatDateFields(omit(saleReceiptDTO, ['closed', 'entries']), [
|
||||
'receiptDate',
|
||||
]),
|
||||
// Avoid rewrite the deliver date in edit mode when already published.
|
||||
...(saleReceiptDTO.closed && (!oldSaleReceipt?.closedAt)) && ({
|
||||
closedAt: moment().toMySqlDateTime(),
|
||||
}),
|
||||
...(saleReceiptDTO.closed &&
|
||||
!oldSaleReceipt?.closedAt && {
|
||||
closedAt: moment().toMySqlDateTime(),
|
||||
}),
|
||||
entries: saleReceiptDTO.entries.map((entry) => ({
|
||||
reference_type: 'SaleReceipt',
|
||||
...omit(entry, ['id', 'amount']),
|
||||
@@ -140,31 +176,56 @@ export default class SalesReceiptService {
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
* @return {Object}
|
||||
*/
|
||||
public async createSaleReceipt(tenantId: number, saleReceiptDTO: any): Promise<ISaleReceipt> {
|
||||
public async createSaleReceipt(
|
||||
tenantId: number,
|
||||
saleReceiptDTO: any
|
||||
): Promise<ISaleReceipt> {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
|
||||
// Transform sale receipt DTO to model.
|
||||
const saleReceiptObj = this.transformObjectDTOToModel(saleReceiptDTO);
|
||||
|
||||
// Validate receipt deposit account existance and type.
|
||||
await this.validateReceiptDepositAccountExistance(tenantId, saleReceiptDTO.depositAccountId);
|
||||
await this.validateReceiptDepositAccountExistance(
|
||||
tenantId,
|
||||
saleReceiptDTO.depositAccountId
|
||||
);
|
||||
|
||||
// Validate items IDs existance on the storage.
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(tenantId, saleReceiptDTO.entries);
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(
|
||||
tenantId,
|
||||
saleReceiptDTO.entries
|
||||
);
|
||||
|
||||
// Validate the sellable items.
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(tenantId, saleReceiptDTO.entries);
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||
tenantId,
|
||||
saleReceiptDTO.entries
|
||||
);
|
||||
|
||||
// Validate sale receipt number uniuqiness.
|
||||
if (saleReceiptDTO.receiptNumber) {
|
||||
await this.validateReceiptNumberUnique(tenantId, saleReceiptDTO.receiptNumber);
|
||||
await this.validateReceiptNumberUnique(
|
||||
tenantId,
|
||||
saleReceiptDTO.receiptNumber
|
||||
);
|
||||
}
|
||||
this.logger.info('[sale_receipt] trying to insert sale receipt graph.', { tenantId, saleReceiptDTO });
|
||||
const saleReceipt = await SaleReceipt.query().insertGraphAndFetch({ ...saleReceiptObj });
|
||||
|
||||
await this.eventDispatcher.dispatch(events.saleReceipt.onCreated, { tenantId, saleReceipt });
|
||||
|
||||
this.logger.info('[sale_receipt] sale receipt inserted successfully.', { tenantId });
|
||||
this.logger.info('[sale_receipt] trying to insert sale receipt graph.', {
|
||||
tenantId,
|
||||
saleReceiptDTO,
|
||||
});
|
||||
const saleReceipt = await SaleReceipt.query().upsertGraph({
|
||||
...saleReceiptObj,
|
||||
});
|
||||
// Triggers `onSaleReceiptCreated` event.
|
||||
await this.eventDispatcher.dispatch(events.saleReceipt.onCreated, {
|
||||
tenantId,
|
||||
saleReceipt,
|
||||
saleReceiptId: saleReceipt.id,
|
||||
});
|
||||
this.logger.info('[sale_receipt] sale receipt inserted successfully.', {
|
||||
tenantId,
|
||||
});
|
||||
|
||||
return saleReceipt;
|
||||
}
|
||||
@@ -175,37 +236,61 @@ export default class SalesReceiptService {
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
* @return {void}
|
||||
*/
|
||||
public async editSaleReceipt(tenantId: number, saleReceiptId: number, saleReceiptDTO: any) {
|
||||
const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId);
|
||||
public async editSaleReceipt(
|
||||
tenantId: number,
|
||||
saleReceiptId: number,
|
||||
saleReceiptDTO: any
|
||||
) {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve sale receipt or throw not found service error.
|
||||
const oldSaleReceipt = await this.getSaleReceiptOrThrowError(tenantId, saleReceiptId);
|
||||
|
||||
const oldSaleReceipt = await this.getSaleReceiptOrThrowError(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
// Transform sale receipt DTO to model.
|
||||
const saleReceiptObj = this.transformObjectDTOToModel(saleReceiptDTO, oldSaleReceipt);
|
||||
|
||||
const saleReceiptObj = this.transformObjectDTOToModel(
|
||||
saleReceiptDTO,
|
||||
oldSaleReceipt
|
||||
);
|
||||
// Validate receipt deposit account existance and type.
|
||||
await this.validateReceiptDepositAccountExistance(tenantId, saleReceiptDTO.depositAccountId);
|
||||
|
||||
await this.validateReceiptDepositAccountExistance(
|
||||
tenantId,
|
||||
saleReceiptDTO.depositAccountId
|
||||
);
|
||||
// Validate items IDs existance on the storage.
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(tenantId, saleReceiptDTO.entries);
|
||||
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(
|
||||
tenantId,
|
||||
saleReceiptDTO.entries
|
||||
);
|
||||
// Validate the sellable items.
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(tenantId, saleReceiptDTO.entries);
|
||||
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||
tenantId,
|
||||
saleReceiptDTO.entries
|
||||
);
|
||||
// Validate sale receipt number uniuqiness.
|
||||
if (saleReceiptDTO.receiptNumber) {
|
||||
await this.validateReceiptNumberUnique(tenantId, saleReceiptDTO.receiptNumber, saleReceiptId);
|
||||
await this.validateReceiptNumberUnique(
|
||||
tenantId,
|
||||
saleReceiptDTO.receiptNumber,
|
||||
saleReceiptId
|
||||
);
|
||||
}
|
||||
|
||||
const saleReceipt = await SaleReceipt.query().upsertGraphAndFetch({
|
||||
id: saleReceiptId,
|
||||
...saleReceiptObj
|
||||
...saleReceiptObj,
|
||||
});
|
||||
|
||||
this.logger.info('[sale_receipt] edited successfully.', { tenantId, saleReceiptId });
|
||||
this.logger.info('[sale_receipt] edited successfully.', {
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
});
|
||||
// Triggers `onSaleReceiptEdited` event.
|
||||
await this.eventDispatcher.dispatch(events.saleReceipt.onEdited, {
|
||||
oldSaleReceipt, tenantId, saleReceiptId, saleReceipt,
|
||||
tenantId,
|
||||
oldSaleReceipt,
|
||||
saleReceipt,
|
||||
saleReceiptId,
|
||||
});
|
||||
return saleReceipt;
|
||||
}
|
||||
@@ -218,20 +303,31 @@ export default class SalesReceiptService {
|
||||
public async deleteSaleReceipt(tenantId: number, saleReceiptId: number) {
|
||||
const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
const oldSaleReceipt = await this.getSaleReceiptOrThrowError(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
await ItemEntry.query()
|
||||
.where('reference_id', saleReceiptId)
|
||||
.where('reference_type', 'SaleReceipt')
|
||||
.delete();
|
||||
|
||||
|
||||
await SaleReceipt.query().where('id', saleReceiptId).delete();
|
||||
|
||||
this.logger.info('[sale_receipt] deleted successfully.', { tenantId, saleReceiptId });
|
||||
await this.eventDispatcher.dispatch(events.saleReceipt.onDeleted);
|
||||
this.logger.info('[sale_receipt] deleted successfully.', {
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
});
|
||||
await this.eventDispatcher.dispatch(events.saleReceipt.onDeleted, {
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
oldSaleReceipt
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve sale receipt with associated entries.
|
||||
* @param {Integer} saleReceiptId
|
||||
* @param {Integer} saleReceiptId
|
||||
* @return {ISaleReceipt}
|
||||
*/
|
||||
async getSaleReceipt(tenantId: number, saleReceiptId: number) {
|
||||
@@ -242,7 +338,7 @@ export default class SalesReceiptService {
|
||||
.withGraphFetched('entries')
|
||||
.withGraphFetched('customer')
|
||||
.withGraphFetched('depositAccount');
|
||||
|
||||
|
||||
if (!saleReceipt) {
|
||||
throw new ServiceError(ERRORS.SALE_RECEIPT_NOT_FOUND);
|
||||
}
|
||||
@@ -251,28 +347,37 @@ export default class SalesReceiptService {
|
||||
|
||||
/**
|
||||
* Retrieve sales receipts paginated and filterable list.
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleReceiptFilter} salesReceiptsFilter
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleReceiptFilter} salesReceiptsFilter
|
||||
*/
|
||||
public async salesReceiptsList(
|
||||
tenantId: number,
|
||||
salesReceiptsFilter: ISaleReceiptFilter,
|
||||
): Promise<{ salesReceipts: ISaleReceipt[], pagination: IPaginationMeta, filterMeta: IFilterMeta }> {
|
||||
salesReceiptsFilter: ISaleReceiptFilter
|
||||
): Promise<{
|
||||
salesReceipts: ISaleReceipt[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(tenantId, SaleReceipt, salesReceiptsFilter);
|
||||
|
||||
this.logger.info('[sale_receipt] try to get sales receipts list.', { tenantId });
|
||||
const { results, pagination } = await SaleReceipt.query().onBuild((builder) => {
|
||||
builder.withGraphFetched('depositAccount');
|
||||
builder.withGraphFetched('customer');
|
||||
builder.withGraphFetched('entries');
|
||||
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
}).pagination(
|
||||
salesReceiptsFilter.page - 1,
|
||||
salesReceiptsFilter.pageSize,
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
SaleReceipt,
|
||||
salesReceiptsFilter
|
||||
);
|
||||
|
||||
this.logger.info('[sale_receipt] try to get sales receipts list.', {
|
||||
tenantId,
|
||||
});
|
||||
const { results, pagination } = await SaleReceipt.query()
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('depositAccount');
|
||||
builder.withGraphFetched('customer');
|
||||
builder.withGraphFetched('entries');
|
||||
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
})
|
||||
.pagination(salesReceiptsFilter.page - 1, salesReceiptsFilter.pageSize);
|
||||
|
||||
return {
|
||||
salesReceipts: results,
|
||||
pagination,
|
||||
@@ -282,8 +387,8 @@ export default class SalesReceiptService {
|
||||
|
||||
/**
|
||||
* Mark the given sale receipt as closed.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleReceiptId
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleReceiptId
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async closeSaleReceipt(
|
||||
@@ -293,7 +398,10 @@ export default class SalesReceiptService {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve sale receipt or throw not found service error.
|
||||
const oldSaleReceipt = await this.getSaleReceiptOrThrowError(tenantId, saleReceiptId);
|
||||
const oldSaleReceipt = await this.getSaleReceiptOrThrowError(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
|
||||
// Throw service error if the sale receipt already closed.
|
||||
if (oldSaleReceipt.isClosed) {
|
||||
@@ -304,4 +412,96 @@ export default class SalesReceiptService {
|
||||
closedAt: moment().toMySqlDateTime(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the sale invoice income journal entries.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {ISaleInvoice} saleInvoice - Sale invoice id.
|
||||
*/
|
||||
public async writesIncomeJournalEntries(
|
||||
tenantId: number,
|
||||
saleInvoice: ISaleReceipt & {
|
||||
entries: IItemEntry & { item: IItem };
|
||||
},
|
||||
override: boolean = false
|
||||
): Promise<void> {
|
||||
const journal = new JournalPoster(tenantId);
|
||||
const journalCommands = new JournalCommands(journal);
|
||||
|
||||
if (override) {
|
||||
await journalCommands.revertJournalEntries(saleInvoice.id, 'SaleReceipt');
|
||||
}
|
||||
// Records the sale invoice journal entries.
|
||||
await journalCommands.saleReceiptIncomeEntries(saleInvoice);
|
||||
|
||||
await Promise.all([
|
||||
journal.deleteEntries(),
|
||||
journal.saveBalance(),
|
||||
journal.saveEntries(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverting the sale receipt journal entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleReceiptId
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async revertReceiptJournalEntries(
|
||||
tenantId: number,
|
||||
saleReceiptId: number | number[]
|
||||
): Promise<void> {
|
||||
return this.journalService.revertJournalTransactions(
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
'SaleReceipt'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the inventory transactions from the given bill input.
|
||||
* @param {Bill} bill - Bill model object.
|
||||
* @param {number} billId - Bill id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async recordInventoryTransactions(
|
||||
tenantId: number,
|
||||
saleReceipt: ISaleReceipt,
|
||||
override?: boolean
|
||||
): Promise<void> {
|
||||
await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
|
||||
tenantId,
|
||||
saleReceipt.id,
|
||||
'SaleReceipt',
|
||||
saleReceipt.receiptDate,
|
||||
'OUT',
|
||||
override,
|
||||
);
|
||||
// Triggers `onInventoryTransactionsCreated` event.
|
||||
this.eventDispatcher.dispatch(
|
||||
events.saleReceipt.onInventoryTransactionsCreated,
|
||||
{
|
||||
tenantId,
|
||||
saleReceipt,
|
||||
saleReceiptId: saleReceipt.id,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts the inventory transactions of the given bill id.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} billId - Bill id.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async revertInventoryTransactions(
|
||||
tenantId: number,
|
||||
receiptId: number
|
||||
) {
|
||||
return this.inventoryService.deleteInventoryTransactions(
|
||||
tenantId,
|
||||
receiptId,
|
||||
'SaleReceipt'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
59
server/src/subscribers/Bills/SyncItemsQuantity.ts
Normal file
59
server/src/subscribers/Bills/SyncItemsQuantity.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Container } from 'typedi';
|
||||
import { EventSubscriber, On } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class BillSubscriber {
|
||||
tenancy: TenancyService;
|
||||
logger: any;
|
||||
itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.logger = Container.get('logger');
|
||||
this.itemsEntriesService = Container.get(ItemsEntriesService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the sale invoice items once the invoice created.
|
||||
*/
|
||||
@On(events.bill.onCreated)
|
||||
public async handleDecrementSaleInvoiceItemsQuantity({ tenantId, bill }) {
|
||||
await this.itemsEntriesService.incrementItemsEntries(
|
||||
tenantId,
|
||||
bill.entries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the sale invoice items once the invoice deleted.
|
||||
*/
|
||||
@On(events.bill.onDeleted)
|
||||
public async handleIncrementSaleInvoiceItemsQuantity({ tenantId, oldBill }) {
|
||||
await this.itemsEntriesService.decrementItemsQuantity(
|
||||
tenantId,
|
||||
oldBill.entries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle increment/decrement the different items quantity once the sale invoice be edited.
|
||||
*/
|
||||
@On(events.bill.onEdited)
|
||||
public async handleChangeSaleInvoiceItemsQuantityOnEdit({
|
||||
tenantId,
|
||||
bill,
|
||||
oldBill,
|
||||
}) {
|
||||
await this.itemsEntriesService.changeItemsQuantity(
|
||||
tenantId,
|
||||
bill.entries,
|
||||
oldBill.entries
|
||||
);
|
||||
}
|
||||
}
|
||||
68
server/src/subscribers/Bills/SyncVendorsBalances.ts
Normal file
68
server/src/subscribers/Bills/SyncVendorsBalances.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Container } from 'typedi';
|
||||
import { EventSubscriber, On } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class BillSubscriber {
|
||||
tenancy: TenancyService;
|
||||
logger: any;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.logger = Container.get('logger');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles vendor balance increment once bill created.
|
||||
*/
|
||||
@On(events.bill.onCreated)
|
||||
async handleVendorBalanceIncrement({ tenantId, billId, bill }) {
|
||||
const { vendorRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Increments vendor balance.
|
||||
this.logger.info('[bill] trying to increment vendor balance.', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
await vendorRepository.changeBalance(bill.vendorId, bill.amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles vendor balance decrement once bill deleted.
|
||||
*/
|
||||
@On(events.bill.onDeleted)
|
||||
async handleVendorBalanceDecrement({ tenantId, billId, oldBill }) {
|
||||
const { vendorRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Decrements vendor balance.
|
||||
this.logger.info('[bill] trying to decrement vendor balance.', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
await vendorRepository.changeBalance(oldBill.vendorId, oldBill.amount * -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles vendor balance difference change.
|
||||
*/
|
||||
@On(events.bill.onEdited)
|
||||
async handleVendorBalanceDiffChange({ tenantId, billId, oldBill, bill }) {
|
||||
const { vendorRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Changes the diff vendor balance between old and new amount.
|
||||
this.logger.info('[bill[ change vendor the different balance.', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
await vendorRepository.changeDiffBalance(
|
||||
bill.vendorId,
|
||||
bill.amount,
|
||||
oldBill.amount,
|
||||
oldBill.vendorId
|
||||
);
|
||||
}
|
||||
}
|
||||
60
server/src/subscribers/Bills/WriteInventoryTransactions.ts
Normal file
60
server/src/subscribers/Bills/WriteInventoryTransactions.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Container } from 'typedi';
|
||||
import { EventSubscriber, On } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import BillsService from 'services/Purchases/Bills';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class BillSubscriber {
|
||||
tenancy: TenancyService;
|
||||
billsService: BillsService;
|
||||
logger: any;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.billsService = Container.get(BillsService);
|
||||
this.logger = Container.get('logger');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles writing the inventory transactions once bill created.
|
||||
*/
|
||||
@On(events.bill.onCreated)
|
||||
async handleWritingInventoryTransactions({ tenantId, bill }) {
|
||||
this.logger.info('[bill] writing the inventory transactions', { tenantId });
|
||||
this.billsService.recordInventoryTransactions(
|
||||
tenantId,
|
||||
bill
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the overwriting the inventory transactions once bill edited.
|
||||
*/
|
||||
@On(events.bill.onEdited)
|
||||
async handleOverwritingInventoryTransactions({ tenantId, bill }) {
|
||||
this.logger.info('[bill] overwriting the inventory transactions.', {
|
||||
tenantId,
|
||||
});
|
||||
this.billsService.recordInventoryTransactions(
|
||||
tenantId,
|
||||
bill,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the reverting the inventory transactions once the bill deleted.
|
||||
*/
|
||||
@On(events.bill.onDeleted)
|
||||
async handleRevertInventoryTransactions({ tenantId, billId }) {
|
||||
this.logger.info('[bill] reverting the bill inventory transactions', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
this.billsService.revertInventoryTransactions(tenantId, billId);
|
||||
}
|
||||
}
|
||||
54
server/src/subscribers/Bills/WriteJournalEntries.ts
Normal file
54
server/src/subscribers/Bills/WriteJournalEntries.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Container } from 'typedi';
|
||||
import { EventSubscriber, On } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import BillsService from 'services/Purchases/Bills';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class BillSubscriber {
|
||||
tenancy: TenancyService;
|
||||
billsService: BillsService;
|
||||
logger: any;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.billsService = Container.get(BillsService);
|
||||
this.logger = Container.get('logger');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles writing journal entries once bill created.
|
||||
*/
|
||||
@On(events.bill.onCreated)
|
||||
async handlerWriteJournalEntriesOnCreate({ tenantId, bill }) {
|
||||
// Writes the journal entries for the given bill transaction.
|
||||
this.logger.info('[bill] writing bill journal entries.', { tenantId });
|
||||
await this.billsService.recordJournalTransactions(tenantId, bill);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the overwriting journal entries once bill edited.
|
||||
*/
|
||||
@On(events.bill.onEdited)
|
||||
async handleOverwriteJournalEntriesOnEdit({ tenantId, bill }) {
|
||||
// Overwrite the journal entries for the given bill transaction.
|
||||
this.logger.info('[bill] overwriting bill journal entries.', { tenantId });
|
||||
await this.billsService.recordJournalTransactions(tenantId, bill, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles revert journal entries on bill deleted.
|
||||
*/
|
||||
@On(events.bill.onDeleted)
|
||||
async handlerDeleteJournalEntries({ tenantId, billId }) {
|
||||
// Delete associated bill journal transactions.
|
||||
this.logger.info('[bill] trying to delete journal entries.', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
await this.billsService.revertJournalEntries(tenantId, billId);
|
||||
}
|
||||
}
|
||||
22
server/src/subscribers/Bills/index.ts
Normal file
22
server/src/subscribers/Bills/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Container } from 'typedi';
|
||||
import { EventSubscriber, On } from 'event-dispatch';
|
||||
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import BillsService from 'services/Purchases/Bills';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class BillSubscriber {
|
||||
tenancy: TenancyService;
|
||||
billsService: BillsService;
|
||||
logger: any;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.billsService = Container.get(BillsService);
|
||||
this.logger = Container.get('logger');
|
||||
}
|
||||
}
|
||||
82
server/src/subscribers/SaleInvoices/SyncCustomersBalance.ts
Normal file
82
server/src/subscribers/SaleInvoices/SyncCustomersBalance.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { Container } from 'typedi';
|
||||
import { On, EventSubscriber } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class SaleInvoiceSubscriber {
|
||||
logger: any;
|
||||
tenancy: TenancyService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.logger = Container.get('logger');
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles customer balance increment once sale invoice created.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleCustomerBalanceIncrement({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
saleInvoiceId,
|
||||
}) {
|
||||
const { customerRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
this.logger.info('[sale_invoice] trying to increment customer balance.', {
|
||||
tenantId,
|
||||
});
|
||||
await customerRepository.changeBalance(
|
||||
saleInvoice.customerId,
|
||||
saleInvoice.balance
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles customer balance diff balnace change once sale invoice edited.
|
||||
*/
|
||||
@On(events.saleInvoice.onEdited)
|
||||
public async onSaleInvoiceEdited({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
oldSaleInvoice,
|
||||
saleInvoiceId,
|
||||
}) {
|
||||
const { customerRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
this.logger.info('[sale_invoice] trying to change diff customer balance.', {
|
||||
tenantId,
|
||||
});
|
||||
await customerRepository.changeDiffBalance(
|
||||
saleInvoice.customerId,
|
||||
saleInvoice.balance,
|
||||
oldSaleInvoice.balance,
|
||||
oldSaleInvoice.customerId
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles customer balance decrement once sale invoice deleted.
|
||||
*/
|
||||
@On(events.saleInvoice.onDeleted)
|
||||
public async handleCustomerBalanceDecrement({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
oldSaleInvoice,
|
||||
}) {
|
||||
const { customerRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
this.logger.info('[sale_invoice] trying to decrement customer balance.', {
|
||||
tenantId,
|
||||
});
|
||||
await customerRepository.changeBalance(
|
||||
oldSaleInvoice.customerId,
|
||||
oldSaleInvoice.balance * -1
|
||||
);
|
||||
}
|
||||
}
|
||||
71
server/src/subscribers/SaleInvoices/SyncItemsQuantity.ts
Normal file
71
server/src/subscribers/SaleInvoices/SyncItemsQuantity.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Container } from 'typedi';
|
||||
import { On, EventSubscriber } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class SyncItemsQuantityWithInvoices {
|
||||
logger: any;
|
||||
tenancy: TenancyService;
|
||||
itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.logger = Container.get('logger');
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.itemsEntriesService = Container.get(ItemsEntriesService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the sale invoice items once the invoice created.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleDecrementSaleInvoiceItemsQuantity({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
}) {
|
||||
await this.itemsEntriesService.decrementItemsQuantity(
|
||||
tenantId,
|
||||
saleInvoice.entries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the sale invoice items once the invoice deleted.
|
||||
*/
|
||||
@On(events.saleInvoice.onDeleted)
|
||||
public async handleIncrementSaleInvoiceItemsQuantity({
|
||||
tenantId,
|
||||
oldSaleInvoice,
|
||||
}) {
|
||||
await this.itemsEntriesService.incrementItemsEntries(
|
||||
tenantId,
|
||||
oldSaleInvoice.entries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle increment/decrement the different items quantity once the sale invoice be edited.
|
||||
*/
|
||||
@On(events.saleInvoice.onEdited)
|
||||
public async handleChangeSaleInvoiceItemsQuantityOnEdit({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
oldSaleInvoice,
|
||||
}) {
|
||||
await this.itemsEntriesService.changeItemsQuantity(
|
||||
tenantId,
|
||||
saleInvoice.entries.map((entry) => ({
|
||||
...entry,
|
||||
quantity: entry.quantity * -1,
|
||||
})),
|
||||
oldSaleInvoice.entries.map((entry) => ({
|
||||
...entry,
|
||||
quantity: entry.quantity * -1,
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Container } from 'typedi';
|
||||
import { On, EventSubscriber } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import SaleInvoicesService from 'services/Sales/SalesInvoices';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class WriteInventoryTransactions {
|
||||
logger: any;
|
||||
tenancy: TenancyService;
|
||||
saleInvoicesService: SaleInvoicesService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.logger = Container.get('logger');
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.saleInvoicesService = Container.get(SaleInvoicesService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the writing inventory transactions once the invoice created.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleWritingInventoryTransactions({ tenantId, saleInvoice }) {
|
||||
this.logger.info('[sale_invoice] trying to write inventory transactions.', {
|
||||
tenantId,
|
||||
});
|
||||
await this.saleInvoicesService.recordInventoryTranscactions(
|
||||
tenantId,
|
||||
saleInvoice
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewriting the inventory transactions once the sale invoice be edited.
|
||||
*/
|
||||
@On(events.saleInvoice.onEdited)
|
||||
public async handleRewritingInventoryTransactions({ tenantId, saleInvoice }) {
|
||||
this.logger.info('[sale_invoice] trying to write inventory transactions.', {
|
||||
tenantId,
|
||||
});
|
||||
await this.saleInvoicesService.recordInventoryTranscactions(
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles deleting the inventory transactions once the invoice deleted.
|
||||
*/
|
||||
@On(events.saleInvoice.onDeleted)
|
||||
public async handleDeletingInventoryTransactions({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
oldSaleInvoice,
|
||||
}) {
|
||||
this.logger.info(
|
||||
'[sale_invoice] trying to revert inventory transactions.',
|
||||
{
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
}
|
||||
);
|
||||
await this.saleInvoicesService.revertInventoryTransactions(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
);
|
||||
}
|
||||
}
|
||||
80
server/src/subscribers/SaleInvoices/WriteJournalEntries.ts
Normal file
80
server/src/subscribers/SaleInvoices/WriteJournalEntries.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Container } from 'typedi';
|
||||
import { On, EventSubscriber } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import SaleInvoicesService from 'services/Sales/SalesInvoices';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class SaleInvoiceSubscriber {
|
||||
logger: any;
|
||||
tenancy: TenancyService;
|
||||
saleInvoicesService: SaleInvoicesService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.logger = Container.get('logger');
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.saleInvoicesService = Container.get(SaleInvoicesService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records journal entries of the non-inventory invoice.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleWriteJournalEntriesOnInvoiceCreated({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
saleInvoice,
|
||||
authorizedUser,
|
||||
}) {
|
||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
const saleInvoiceWithItems = await saleInvoiceRepository.findOneById(
|
||||
saleInvoiceId,
|
||||
'entries.item'
|
||||
);
|
||||
await this.saleInvoicesService.writesIncomeJournalEntries(
|
||||
tenantId,
|
||||
saleInvoiceWithItems
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records journal entries of the non-inventory invoice.
|
||||
*/
|
||||
@On(events.saleInvoice.onEdited)
|
||||
public async handleRewriteJournalEntriesOnceInvoiceEdit({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
saleInvoice,
|
||||
authorizedUser,
|
||||
}) {
|
||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
const saleInvoiceWithItems = await saleInvoiceRepository.findOneById(
|
||||
saleInvoiceId,
|
||||
'entries.item'
|
||||
);
|
||||
await this.saleInvoicesService.writesIncomeJournalEntries(
|
||||
tenantId,
|
||||
saleInvoiceWithItems,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle reverting journal entries once sale invoice delete.
|
||||
*/
|
||||
@On(events.saleInvoice.onDeleted)
|
||||
public async handleRevertingInvoiceJournalEntriesOnDelete({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
}) {
|
||||
await this.saleInvoicesService.revertInvoiceJournalEntries(
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
57
server/src/subscribers/SaleInvoices/index.ts
Normal file
57
server/src/subscribers/SaleInvoices/index.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Container } from 'typedi';
|
||||
import { On, EventSubscriber } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import SettingsService from 'services/Settings/SettingsService';
|
||||
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class SaleInvoiceSubscriber {
|
||||
logger: any;
|
||||
tenancy: TenancyService;
|
||||
settingsService: SettingsService;
|
||||
saleEstimatesService: SaleEstimateService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.logger = Container.get('logger');
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.settingsService = Container.get(SettingsService);
|
||||
this.saleEstimatesService = Container.get(SaleEstimateService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the sale estimate as converted from the sale invoice once created.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleMarkEstimateConvert({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
saleInvoiceId,
|
||||
}) {
|
||||
if (saleInvoice.fromEstimateId) {
|
||||
this.saleEstimatesService.convertEstimateToInvoice(
|
||||
tenantId,
|
||||
saleInvoice.fromEstiamteId,
|
||||
saleInvoiceId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles sale invoice next number increment once invoice created.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleInvoiceNextNumberIncrement({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
saleInvoice,
|
||||
}) {
|
||||
await this.settingsService.incrementNextNumber(tenantId, {
|
||||
key: 'next_number',
|
||||
group: 'sales_invoices',
|
||||
});
|
||||
}
|
||||
}
|
||||
67
server/src/subscribers/SaleReceipt/SyncItemsQuantity.ts
Normal file
67
server/src/subscribers/SaleReceipt/SyncItemsQuantity.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { Container } from 'typedi';
|
||||
import { On, EventSubscriber } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
import SalesInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class SaleReceiptSubscriber {
|
||||
logger: any;
|
||||
tenancy: TenancyService;
|
||||
itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
constructor() {
|
||||
this.logger = Container.get('logger');
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.itemsEntriesService = Container.get(ItemsEntriesService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the sale receipt items once be created.
|
||||
*/
|
||||
@On(events.saleReceipt.onCreated)
|
||||
public async handleDecremenReceiptItemsQuantity({ tenantId, saleReceipt }) {
|
||||
await this.itemsEntriesService.decrementItemsQuantity(
|
||||
tenantId,
|
||||
saleReceipt.entries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the sale receipt items once be deleted.
|
||||
*/
|
||||
@On(events.saleReceipt.onDeleted)
|
||||
public async handleIncrementReceiptItemsQuantity({
|
||||
tenantId,
|
||||
oldSaleReceipt,
|
||||
}) {
|
||||
await this.itemsEntriesService.incrementItemsEntries(
|
||||
tenantId,
|
||||
oldSaleReceipt.entries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle increment/decrement the different items quantity once
|
||||
* the sale receipt be edited.
|
||||
*/
|
||||
@On(events.saleReceipt.onEdited)
|
||||
public async handleChangeSaleInvoiceItemsQuantityOnEdit({
|
||||
tenantId,
|
||||
saleReceipt,
|
||||
oldSaleReceipt,
|
||||
}) {
|
||||
await this.itemsEntriesService.changeItemsQuantity(
|
||||
tenantId,
|
||||
saleReceipt.entries.map((entry) => ({
|
||||
...entry,
|
||||
quantity: entry.quantity * -1,
|
||||
})),
|
||||
oldSaleReceipt.entries.map((entry) => ({
|
||||
...entry,
|
||||
quantity: entry.quantity * -1,
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Container } from 'typedi';
|
||||
import { On, EventSubscriber } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import SalesReceiptService from 'services/Sales/SalesReceipts';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class SaleReceiptSubscriber {
|
||||
logger: any;
|
||||
tenancy: TenancyService;
|
||||
saleReceiptsService: SalesReceiptService;
|
||||
|
||||
constructor() {
|
||||
this.logger = Container.get('logger');
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.saleReceiptsService = Container.get(SalesReceiptService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the writing inventory transactions once the receipt created.
|
||||
*/
|
||||
@On(events.saleReceipt.onCreated)
|
||||
public async handleWritingInventoryTransactions({ tenantId, saleReceipt }) {
|
||||
this.logger.info('[sale_receipt] trying to write inventory transactions.', {
|
||||
tenantId,
|
||||
});
|
||||
await this.saleReceiptsService.recordInventoryTransactions(
|
||||
tenantId,
|
||||
saleReceipt
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewriting the inventory transactions once the sale invoice be edited.
|
||||
*/
|
||||
@On(events.saleReceipt.onEdited)
|
||||
public async handleRewritingInventoryTransactions({ tenantId, saleReceipt }) {
|
||||
this.logger.info('[sale_invoice] trying to write inventory transactions.', {
|
||||
tenantId,
|
||||
});
|
||||
await this.saleReceiptsService.recordInventoryTransactions(
|
||||
tenantId,
|
||||
saleReceipt,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles deleting the inventory transactions once the receipt deleted.
|
||||
*/
|
||||
@On(events.saleReceipt.onDeleted)
|
||||
public async handleDeletingInventoryTransactions({
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
}) {
|
||||
this.logger.info(
|
||||
'[sale_invoice] trying to revert inventory transactions.',
|
||||
{
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
}
|
||||
);
|
||||
await this.saleReceiptsService.revertInventoryTransactions(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
}
|
||||
}
|
||||
75
server/src/subscribers/SaleReceipt/WriteJournalEntries.ts
Normal file
75
server/src/subscribers/SaleReceipt/WriteJournalEntries.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Container } from 'typedi';
|
||||
import { On, EventSubscriber } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import SalesReceiptService from 'services/Sales/SalesReceipts';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class SaleReceiptSubscriber {
|
||||
logger: any;
|
||||
tenancy: TenancyService;
|
||||
saleReceiptsService: SalesReceiptService;
|
||||
|
||||
constructor() {
|
||||
this.logger = Container.get('logger');
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.saleReceiptsService = Container.get(SalesReceiptService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles writing sale receipt income journal entries once created.
|
||||
*/
|
||||
@On(events.saleReceipt.onCreated)
|
||||
public async handleWriteReceiptIncomeJournalEntrieOnCreate({
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
}) {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
|
||||
const saleReceiptWithItems = await SaleReceipt.query()
|
||||
.findById(saleReceiptId)
|
||||
.withGraphFetched('entries.item');
|
||||
|
||||
// Writes the sale receipt income journal entries.
|
||||
await this.saleReceiptsService.writesIncomeJournalEntries(
|
||||
tenantId,
|
||||
saleReceiptWithItems
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles sale receipt revert jouranl entries once be deleted.
|
||||
*/
|
||||
@On(events.saleReceipt.onDeleted)
|
||||
public async handleRevertReceiptJournalEntriesOnDeleted({
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
}) {
|
||||
await this.saleReceiptsService.revertReceiptJournalEntries(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles writing sale receipt income journal entries once be edited.
|
||||
*/
|
||||
@On(events.saleReceipt.onEdited)
|
||||
public async handleWriteReceiptIncomeJournalEntrieOnEdited({
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
}) {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
|
||||
const saleReceiptWithItems = await SaleReceipt.query()
|
||||
.findById(saleReceiptId)
|
||||
.withGraphFetched('entries.item');
|
||||
|
||||
// Writes the sale receipt income journal entries.
|
||||
await this.saleReceiptsService.writesIncomeJournalEntries(
|
||||
tenantId,
|
||||
saleReceiptWithItems,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Container } from 'typedi';
|
||||
import { On, EventSubscriber } from "event-dispatch";
|
||||
import { On, EventSubscriber } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import SettingsService from 'services/Settings/SettingsService';
|
||||
@@ -15,7 +15,7 @@ export default class SaleReceiptSubscriber {
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.settingsService = Container.get(SettingsService);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle sale receipt increment next number once be created.
|
||||
*/
|
||||
@@ -26,4 +26,4 @@ export default class SaleReceiptSubscriber {
|
||||
group: 'sales_receipts',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
import { Container } from 'typedi';
|
||||
import { EventSubscriber, On } from 'event-dispatch';
|
||||
import { map, head } from 'lodash';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import BillsService from 'services/Purchases/Bills';
|
||||
import JournalPosterService from 'services/Sales/JournalPosterService';
|
||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class BillSubscriber {
|
||||
tenancy: TenancyService;
|
||||
billsService: BillsService;
|
||||
logger: any;
|
||||
journalPosterService: JournalPosterService;
|
||||
itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.billsService = Container.get(BillsService);
|
||||
this.logger = Container.get('logger');
|
||||
this.journalPosterService = Container.get(JournalPosterService);
|
||||
this.itemsEntriesService = Container.get(ItemsEntriesService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles vendor balance increment once bill created.
|
||||
*/
|
||||
@On(events.bill.onCreated)
|
||||
async handleVendorBalanceIncrement({ tenantId, billId, bill }) {
|
||||
const { vendorRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Increments vendor balance.
|
||||
this.logger.info('[bill] trying to increment vendor balance.', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
await vendorRepository.changeBalance(bill.vendorId, bill.amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles writing journal entries once bill created.
|
||||
*/
|
||||
@On(events.bill.onCreated)
|
||||
async handlerWriteJournalEntriesOnCreate({ tenantId, bill }) {
|
||||
// Writes the journal entries for the given bill transaction.
|
||||
this.logger.info('[bill] writing bill journal entries.', { tenantId });
|
||||
await this.billsService.recordJournalTransactions(tenantId, bill);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the overwriting journal entries once bill edited.
|
||||
*/
|
||||
@On(events.bill.onEdited)
|
||||
async handleOverwriteJournalEntriesOnEdit({ tenantId, bill }) {
|
||||
// Overwrite the journal entries for the given bill transaction.
|
||||
this.logger.info('[bill] overwriting bill journal entries.', { tenantId });
|
||||
await this.billsService.recordJournalTransactions(tenantId, bill, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles vendor balance decrement once bill deleted.
|
||||
*/
|
||||
@On(events.bill.onDeleted)
|
||||
async handleVendorBalanceDecrement({ tenantId, billId, oldBill }) {
|
||||
const { vendorRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Decrements vendor balance.
|
||||
this.logger.info('[bill] trying to decrement vendor balance.', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
await vendorRepository.changeBalance(oldBill.vendorId, oldBill.amount * -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles revert journal entries on bill deleted.
|
||||
*/
|
||||
@On(events.bill.onDeleted)
|
||||
async handlerDeleteJournalEntries({ tenantId, billId }) {
|
||||
// Delete associated bill journal transactions.
|
||||
this.logger.info('[bill] trying to delete journal entries.', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
await this.journalPosterService.revertJournalTransactions(
|
||||
tenantId,
|
||||
billId,
|
||||
'Bill'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles vendor balance difference change.
|
||||
*/
|
||||
@On(events.bill.onEdited)
|
||||
async handleVendorBalanceDiffChange({ tenantId, billId, oldBill, bill }) {
|
||||
const { vendorRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Changes the diff vendor balance between old and new amount.
|
||||
this.logger.info('[bill[ change vendor the different balance.', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
await vendorRepository.changeDiffBalance(
|
||||
bill.vendorId,
|
||||
bill.amount,
|
||||
oldBill.amount,
|
||||
oldBill.vendorId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles writing the inventory transactions once bill created.
|
||||
*/
|
||||
@On(events.bill.onCreated)
|
||||
async handleWritingInventoryTransactions({ tenantId, bill }) {
|
||||
this.logger.info('[bill] writing the inventory transactions', { tenantId });
|
||||
this.billsService.recordInventoryTransactions(
|
||||
tenantId,
|
||||
bill.id,
|
||||
bill.billDate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the overwriting the inventory transactions once bill edited.
|
||||
*/
|
||||
@On(events.bill.onEdited)
|
||||
async handleOverwritingInventoryTransactions({ tenantId, bill }) {
|
||||
this.logger.info('[bill] overwriting the inventory transactions.', {
|
||||
tenantId,
|
||||
});
|
||||
this.billsService.recordInventoryTransactions(
|
||||
tenantId,
|
||||
bill.id,
|
||||
bill.billDate,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the reverting the inventory transactions once the bill deleted.
|
||||
*/
|
||||
@On(events.bill.onDeleted)
|
||||
async handleRevertInventoryTransactions({ tenantId, billId }) {
|
||||
this.logger.info('[bill] reverting the bill inventory transactions', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
this.billsService.revertInventoryTransactions(tenantId, billId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules items cost compute jobs once the inventory transactions created
|
||||
* of the bill transaction.
|
||||
*/
|
||||
@On(events.bill.onInventoryTransactionsCreated)
|
||||
public async handleComputeItemsCosts({ tenantId, billId }) {
|
||||
this.logger.info('[bill] trying to compute the bill items cost.', {
|
||||
tenantId,
|
||||
billId,
|
||||
});
|
||||
await this.billsService.scheduleComputeCostByBillId(tenantId, billId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles computes items costs once the inventory transactions deleted.
|
||||
*/
|
||||
@On(events.bill.onInventoryTransactionsDeleted)
|
||||
public async handleComputeCostsOnInventoryTransactionsDeleted({
|
||||
tenantId,
|
||||
billId,
|
||||
oldInventoryTransactions,
|
||||
}) {
|
||||
const inventoryItemsIds = map(oldInventoryTransactions, 'itemId');
|
||||
const startingDates = map(oldInventoryTransactions, 'date');
|
||||
const startingDate = head(startingDates);
|
||||
|
||||
await this.billsService.scheduleComputeCostByItemsIds(
|
||||
tenantId,
|
||||
inventoryItemsIds,
|
||||
startingDate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the sale invoice items once the invoice created.
|
||||
*/
|
||||
@On(events.bill.onCreated)
|
||||
public async handleDecrementSaleInvoiceItemsQuantity({ tenantId, bill }) {
|
||||
await this.itemsEntriesService.incrementItemsEntries(
|
||||
tenantId,
|
||||
bill.entries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the sale invoice items once the invoice deleted.
|
||||
*/
|
||||
@On(events.bill.onDeleted)
|
||||
public async handleIncrementSaleInvoiceItemsQuantity({ tenantId, oldBill }) {
|
||||
await this.itemsEntriesService.decrementItemsQuantity(
|
||||
tenantId,
|
||||
oldBill.entries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle increment/decrement the different items quantity once the sale invoice be edited.
|
||||
*/
|
||||
@On(events.bill.onEdited)
|
||||
public async handleChangeSaleInvoiceItemsQuantityOnEdit({
|
||||
tenantId,
|
||||
bill,
|
||||
oldBill,
|
||||
}) {
|
||||
await this.itemsEntriesService.changeItemsQuantity(
|
||||
tenantId,
|
||||
bill.entries,
|
||||
oldBill.entries
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -83,8 +83,6 @@ export default {
|
||||
onDelivered: 'onSaleInvoiceDelivered',
|
||||
onBulkDelete: 'onSaleInvoiceBulkDeleted',
|
||||
onPublished: 'onSaleInvoicePublished',
|
||||
onInventoryTransactionsCreated: 'onInvoiceInventoryTransactionsCreated',
|
||||
onInventoryTransactionsDeleted: 'onInvoiceInventoryTransactionsDeleted',
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -128,8 +126,6 @@ export default {
|
||||
onDeleted: 'onBillDeleted',
|
||||
onBulkDeleted: 'onBillBulkDeleted',
|
||||
onPublished: 'onBillPublished',
|
||||
onInventoryTransactionsCreated: 'onBillInventoryTransactionsCreated',
|
||||
onInventoryTransactionsDeleted: 'onBillInventoryTransactionsDeleted'
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -189,6 +185,9 @@ export default {
|
||||
* Inventory service.
|
||||
*/
|
||||
inventory: {
|
||||
onInventoryTransactionsCreated: 'onInventoryTransactionsCreated',
|
||||
onInventoryTransactionsDeleted: 'onInventoryTransactionsDeleted',
|
||||
|
||||
onComputeItemCostJobScheduled: 'onComputeItemCostJobScheduled',
|
||||
onComputeItemCostJobStarted: 'onComputeItemCostJobStarted',
|
||||
onComputeItemCostJobCompleted: 'onComputeItemCostJobCompleted'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Container } from 'typedi';
|
||||
import { EventSubscriber, On } from 'event-dispatch';
|
||||
import { map, head } from 'lodash';
|
||||
import events from 'subscribers/events';
|
||||
import SaleInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
||||
|
||||
@@ -7,16 +8,20 @@ import SaleInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
||||
export class InventorySubscriber {
|
||||
depends: number = 0;
|
||||
startingDate: Date;
|
||||
saleInvoicesCost: SaleInvoicesCost;
|
||||
agenda: any;
|
||||
|
||||
constructor() {
|
||||
this.saleInvoicesCost = Container.get(SaleInvoicesCost);
|
||||
this.agenda = Container.get('agenda');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle run writing the journal entries once the compute items jobs completed.
|
||||
*/
|
||||
@On(events.inventory.onComputeItemCostJobCompleted)
|
||||
async onComputeItemCostJobFinished({ itemId, tenantId, startingDate }) {
|
||||
const saleInvoicesCost = Container.get(SaleInvoicesCost);
|
||||
const agenda = Container.get('agenda');
|
||||
|
||||
const dependsComputeJobs = await agenda.jobs({
|
||||
const dependsComputeJobs = await this.agenda.jobs({
|
||||
name: 'compute-item-cost',
|
||||
nextRunAt: { $ne: null },
|
||||
'data.tenantId': tenantId,
|
||||
@@ -25,10 +30,53 @@ export class InventorySubscriber {
|
||||
if (dependsComputeJobs.length === 0) {
|
||||
this.startingDate = null;
|
||||
|
||||
await saleInvoicesCost.scheduleWriteJournalEntries(
|
||||
await this.saleInvoicesCost.scheduleWriteJournalEntries(
|
||||
tenantId,
|
||||
startingDate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@On(events.inventory.onInventoryTransactionsCreated)
|
||||
async handleScheduleItemsCostOnInventoryTransactionsCreated({
|
||||
tenantId,
|
||||
inventoryEntries,
|
||||
transactionId,
|
||||
transactionType,
|
||||
transactionDate,
|
||||
transactionDirection,
|
||||
override
|
||||
}) {
|
||||
const inventoryItemsIds = map(inventoryEntries, 'itemId');
|
||||
|
||||
await this.saleInvoicesCost.scheduleComputeCostByItemsIds(
|
||||
tenantId,
|
||||
inventoryItemsIds,
|
||||
transactionDate,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules compute items cost once the inventory transactions deleted.
|
||||
*/
|
||||
@On(events.inventory.onInventoryTransactionsDeleted)
|
||||
async handleScheduleItemsCostOnInventoryTransactionsDeleted({
|
||||
tenantId,
|
||||
transactionType,
|
||||
transactionId,
|
||||
oldInventoryTransactions
|
||||
}) {
|
||||
const inventoryItemsIds = map(oldInventoryTransactions, 'itemId');
|
||||
const startingDates = map(oldInventoryTransactions, 'date');
|
||||
const startingDate = head(startingDates);
|
||||
|
||||
await this.saleInvoicesCost.scheduleComputeCostByItemsIds(
|
||||
tenantId,
|
||||
inventoryItemsIds,
|
||||
startingDate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
import { Container } from 'typedi';
|
||||
import { head, map } from 'lodash';
|
||||
import { On, EventSubscriber } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import SettingsService from 'services/Settings/SettingsService';
|
||||
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
||||
import SaleInvoicesService from 'services/Sales/SalesInvoices';
|
||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
import SalesInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
||||
|
||||
@EventSubscriber()
|
||||
export default class SaleInvoiceSubscriber {
|
||||
logger: any;
|
||||
tenancy: TenancyService;
|
||||
settingsService: SettingsService;
|
||||
saleEstimatesService: SaleEstimateService;
|
||||
saleInvoicesService: SaleInvoicesService;
|
||||
itemsEntriesService: ItemsEntriesService;
|
||||
salesInvoicesCost: SalesInvoicesCost;
|
||||
|
||||
constructor() {
|
||||
this.logger = Container.get('logger');
|
||||
this.tenancy = Container.get(TenancyService);
|
||||
this.settingsService = Container.get(SettingsService);
|
||||
this.saleEstimatesService = Container.get(SaleEstimateService);
|
||||
this.saleInvoicesService = Container.get(SaleInvoicesService);
|
||||
this.itemsEntriesService = Container.get(ItemsEntriesService);
|
||||
this.salesInvoicesCost = Container.get(SalesInvoicesCost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles customer balance increment once sale invoice created.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleCustomerBalanceIncrement({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
saleInvoiceId,
|
||||
}) {
|
||||
const { customerRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
this.logger.info('[sale_invoice] trying to increment customer balance.', {
|
||||
tenantId,
|
||||
});
|
||||
await customerRepository.changeBalance(
|
||||
saleInvoice.customerId,
|
||||
saleInvoice.balance
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the sale estimate as converted from the sale invoice once created.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleMarkEstimateConvert({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
saleInvoiceId,
|
||||
}) {
|
||||
if (saleInvoice.fromEstimateId) {
|
||||
this.saleEstimatesService.convertEstimateToInvoice(
|
||||
tenantId,
|
||||
saleInvoice.fromEstiamteId,
|
||||
saleInvoiceId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles sale invoice next number increment once invoice created.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleInvoiceNextNumberIncrement({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
saleInvoice,
|
||||
}) {
|
||||
await this.settingsService.incrementNextNumber(tenantId, {
|
||||
key: 'next_number',
|
||||
group: 'sales_invoices',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the writing inventory transactions once the invoice created.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleWritingInventoryTransactions({ tenantId, saleInvoice }) {
|
||||
this.logger.info('[sale_invoice] trying to write inventory transactions.', {
|
||||
tenantId,
|
||||
});
|
||||
await this.saleInvoicesService.recordInventoryTranscactions(
|
||||
tenantId,
|
||||
saleInvoice.id,
|
||||
saleInvoice.invoiceDate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles handle write income journal entries of sale invoice.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleWriteInvoiceIncomeJournalEntries({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
saleInvoice,
|
||||
authorizedUser,
|
||||
}) {
|
||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
const saleInvoiceWithItems = await saleInvoiceRepository.findOneById(
|
||||
saleInvoiceId,
|
||||
'entries.item'
|
||||
);
|
||||
await this.saleInvoicesService.writesIncomeJournalEntries(
|
||||
tenantId,
|
||||
saleInvoiceWithItems
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the sale invoice items once the invoice created.
|
||||
*/
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleDecrementSaleInvoiceItemsQuantity({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
}) {
|
||||
await this.itemsEntriesService.decrementItemsQuantity(
|
||||
tenantId,
|
||||
saleInvoice.entries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewriting the inventory transactions once the sale invoice be edited.
|
||||
*/
|
||||
@On(events.saleInvoice.onEdited)
|
||||
public async handleRewritingInventoryTransactions({ tenantId, saleInvoice }) {
|
||||
this.logger.info('[sale_invoice] trying to write inventory transactions.', {
|
||||
tenantId,
|
||||
});
|
||||
await this.saleInvoicesService.recordInventoryTranscactions(
|
||||
tenantId,
|
||||
saleInvoice.id,
|
||||
saleInvoice.invoiceDate,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles customer balance diff balnace change once sale invoice edited.
|
||||
*/
|
||||
@On(events.saleInvoice.onEdited)
|
||||
public async onSaleInvoiceEdited({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
oldSaleInvoice,
|
||||
saleInvoiceId,
|
||||
}) {
|
||||
const { customerRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
this.logger.info('[sale_invoice] trying to change diff customer balance.', {
|
||||
tenantId,
|
||||
});
|
||||
await customerRepository.changeDiffBalance(
|
||||
saleInvoice.customerId,
|
||||
saleInvoice.balance,
|
||||
oldSaleInvoice.balance,
|
||||
oldSaleInvoice.customerId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records journal entries of the non-inventory invoice.
|
||||
*/
|
||||
@On(events.saleInvoice.onEdited)
|
||||
public async handleRewriteJournalEntriesOnceInvoiceEdit({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
saleInvoice,
|
||||
authorizedUser,
|
||||
}) {
|
||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
const saleInvoiceWithItems = await saleInvoiceRepository.findOneById(
|
||||
saleInvoiceId,
|
||||
'entries.item'
|
||||
);
|
||||
await this.saleInvoicesService.writesIncomeJournalEntries(
|
||||
tenantId,
|
||||
saleInvoiceWithItems,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles customer balance decrement once sale invoice deleted.
|
||||
*/
|
||||
@On(events.saleInvoice.onDeleted)
|
||||
public async handleCustomerBalanceDecrement({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
oldSaleInvoice,
|
||||
}) {
|
||||
const { customerRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
this.logger.info('[sale_invoice] trying to decrement customer balance.', {
|
||||
tenantId,
|
||||
});
|
||||
await customerRepository.changeBalance(
|
||||
oldSaleInvoice.customerId,
|
||||
oldSaleInvoice.balance * -1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle reverting journal entries once sale invoice delete.
|
||||
*/
|
||||
@On(events.saleInvoice.onDelete)
|
||||
public async handleRevertingInvoiceJournalEntriesOnDelete({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
}) {
|
||||
await this.saleInvoicesService.revertInvoiceJournalEntries(
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles deleting the inventory transactions once the invoice deleted.
|
||||
*/
|
||||
@On(events.saleInvoice.onDelete)
|
||||
public async handleDeletingInventoryTransactions({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
oldSaleInvoice,
|
||||
}) {
|
||||
this.logger.info(
|
||||
'[sale_invoice] trying to revert inventory transactions.',
|
||||
{
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
}
|
||||
);
|
||||
await this.saleInvoicesService.revertInventoryTransactions(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules compute invoice items cost job.
|
||||
*/
|
||||
@On(events.saleInvoice.onInventoryTransactionsCreated)
|
||||
public async handleComputeCostsOnInventoryTransactionsCreated({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
}) {
|
||||
this.logger.info(
|
||||
'[sale_invoice] trying to compute the invoice items cost.',
|
||||
{
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
}
|
||||
);
|
||||
await this.salesInvoicesCost.scheduleComputeCostByInvoiceId(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules compute items cost once the inventory transactions deleted.
|
||||
*/
|
||||
@On(events.saleInvoice.onInventoryTransactionsDeleted)
|
||||
public async handleComputeCostsOnInventoryTransactionsDeleted({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
oldInventoryTransactions,
|
||||
}) {
|
||||
const inventoryItemsIds = map(oldInventoryTransactions, 'itemId');
|
||||
const startingDates = map(oldInventoryTransactions, 'date');
|
||||
const startingDate = head(startingDates);
|
||||
|
||||
await this.salesInvoicesCost.scheduleComputeCostByItemsIds(
|
||||
tenantId,
|
||||
inventoryItemsIds,
|
||||
startingDate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the sale invoice items once the invoice deleted.
|
||||
*/
|
||||
@On(events.saleInvoice.onDeleted)
|
||||
public async handleIncrementSaleInvoiceItemsQuantity({
|
||||
tenantId,
|
||||
oldSaleInvoice,
|
||||
}) {
|
||||
await this.itemsEntriesService.incrementItemsEntries(
|
||||
tenantId,
|
||||
oldSaleInvoice.entries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle increment/decrement the different items quantity once the sale invoice be edited.
|
||||
*/
|
||||
@On(events.saleInvoice.onEdited)
|
||||
public async handleChangeSaleInvoiceItemsQuantityOnEdit({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
oldSaleInvoice,
|
||||
}) {
|
||||
await this.itemsEntriesService.changeItemsQuantity(
|
||||
tenantId,
|
||||
saleInvoice.entries.map((entry) => ({
|
||||
...entry,
|
||||
quantity: entry.quantity * -1,
|
||||
})),
|
||||
oldSaleInvoice.entries.map((entry) => ({
|
||||
...entry,
|
||||
quantity: entry.quantity * -1,
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -174,8 +174,6 @@ const getDefinedOption = (key, group) => {
|
||||
|
||||
const isDefinedOptionConfigurable = (key, group) => {
|
||||
const definedOption = getDefinedOption(key, group);
|
||||
console.log(definedOption, 'definedOption');
|
||||
|
||||
return definedOption?.config || false;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user