mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 23:00:34 +00:00
fix: refactoring invoice calc cost service.
This commit is contained in:
@@ -97,7 +97,6 @@ function VendorForm({
|
|||||||
}),
|
}),
|
||||||
[defaultInitialValues],
|
[defaultInitialValues],
|
||||||
);
|
);
|
||||||
console.log(isNewMode, 'Val');
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
!isNewMode
|
!isNewMode
|
||||||
? changePageTitle(formatMessage({ id: 'edit_vendor' }))
|
? changePageTitle(formatMessage({ id: 'edit_vendor' }))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response, NextFunction } from 'express';
|
||||||
import { query, ValidationChain } from 'express-validator';
|
import { query, ValidationChain } from 'express-validator';
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import BaseController from '../BaseController';
|
import BaseController from '../BaseController';
|
||||||
@@ -48,7 +48,7 @@ export default class GeneralLedgerReportController extends BaseController{
|
|||||||
* @param {Request} req -
|
* @param {Request} req -
|
||||||
* @param {Response} res -
|
* @param {Response} res -
|
||||||
*/
|
*/
|
||||||
async generalLedger(req: Request, res: Response) {
|
async generalLedger(req: Request, res: Response, next: NextFunction) {
|
||||||
const { tenantId, settings } = req;
|
const { tenantId, settings } = req;
|
||||||
const filter = this.matchedQueryData(req);
|
const filter = this.matchedQueryData(req);
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ export default class GeneralLedgerReportController extends BaseController{
|
|||||||
query: this.transfromToResponse(query),
|
query: this.transfromToResponse(query),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { Request, Response, Router } from 'express';
|
import { Request, Response, Router, NextFunction } from 'express';
|
||||||
import { castArray } from 'lodash';
|
import { castArray } from 'lodash';
|
||||||
import { query, oneOf } from 'express-validator';
|
import { query, oneOf } from 'express-validator';
|
||||||
import JournalSheetService from 'services/FinancialStatements/JournalSheet/JournalSheetService';
|
import JournalSheetService from 'services/FinancialStatements/JournalSheet/JournalSheetService';
|
||||||
@@ -55,7 +55,7 @@ export default class JournalSheetController extends BaseController {
|
|||||||
* @param {Request} req -
|
* @param {Request} req -
|
||||||
* @param {Response} res -
|
* @param {Response} res -
|
||||||
*/
|
*/
|
||||||
async journal(req: Request, res: Response) {
|
async journal(req: Request, res: Response, next: NextFunction) {
|
||||||
const { tenantId, settings } = req;
|
const { tenantId, settings } = req;
|
||||||
let filter = this.matchedQueryData(req);
|
let filter = this.matchedQueryData(req);
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ export default class JournalSheetController extends BaseController {
|
|||||||
query: this.transfromToResponse(query),
|
query: this.transfromToResponse(query),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ export default class ProfitLossSheetController extends BaseController {
|
|||||||
query: this.transfromToResponse(query),
|
query: this.transfromToResponse(query),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,6 @@ export default class BillsPayments extends BaseController {
|
|||||||
message: 'Payment made has been created successfully.',
|
message: 'Payment made has been created successfully.',
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,12 +248,13 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
async getSaleInvoice(req: Request, res: Response, next: NextFunction) {
|
async getSaleInvoice(req: Request, res: Response, next: NextFunction) {
|
||||||
const { id: saleInvoiceId } = req.params;
|
const { id: saleInvoiceId } = req.params;
|
||||||
const { tenantId } = req;
|
const { tenantId, user } = req;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const saleInvoice = await this.saleInvoiceService.getSaleInvoice(
|
const saleInvoice = await this.saleInvoiceService.getSaleInvoice(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId
|
saleInvoiceId,
|
||||||
|
user
|
||||||
);
|
);
|
||||||
return res.status(200).send({ sale_invoice: saleInvoice });
|
return res.status(200).send({ sale_invoice: saleInvoice });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ exports.up = function(knex) {
|
|||||||
table.string('contact_type').nullable().index();
|
table.string('contact_type').nullable().index();
|
||||||
table.integer('contact_id').unsigned().nullable().index();
|
table.integer('contact_id').unsigned().nullable().index();
|
||||||
table.string('note');
|
table.string('note');
|
||||||
table.boolean('draft').defaultTo(false);
|
|
||||||
table.integer('user_id').unsigned().index();
|
table.integer('user_id').unsigned().index();
|
||||||
table.integer('index').unsigned();
|
table.integer('index').unsigned();
|
||||||
table.date('date').index();
|
table.date('date').index();
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
|
||||||
|
export type TInventoryTransactionDirection = 'IN' | 'OUT';
|
||||||
|
|
||||||
export interface IInventoryTransaction {
|
export interface IInventoryTransaction {
|
||||||
id?: number,
|
id?: number,
|
||||||
date: Date,
|
date: Date,
|
||||||
direction: string,
|
direction: TInventoryTransactionDirection,
|
||||||
itemId: number,
|
itemId: number,
|
||||||
quantity: number,
|
quantity: number,
|
||||||
rate: number,
|
rate: number,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
|
export type IItemEntryTransactionType = 'SaleInvoice' | 'Bill' | 'SaleReceipt';
|
||||||
|
|
||||||
export interface IItemEntry {
|
export interface IItemEntry {
|
||||||
id?: number,
|
id?: number,
|
||||||
|
|||||||
@@ -1,40 +1,50 @@
|
|||||||
import { ISalesInvoicesFilter } from "./SaleInvoice";
|
import { ISalesInvoicesFilter } from './SaleInvoice';
|
||||||
|
|
||||||
|
|
||||||
export interface ISaleReceipt {
|
export interface ISaleReceipt {
|
||||||
id?: number,
|
id?: number;
|
||||||
customerId: number,
|
customerId: number;
|
||||||
depositAccountId: number,
|
depositAccountId: number;
|
||||||
receiptDate: Date,
|
receiptDate: Date;
|
||||||
sendToEmail: string,
|
sendToEmail: string;
|
||||||
referenceNo: string,
|
referenceNo: string;
|
||||||
receiptMessage: string,
|
receiptMessage: string;
|
||||||
receiptNumber: string,
|
receiptNumber: string;
|
||||||
statement: string,
|
amount: number;
|
||||||
closedAt: Date|string,
|
statement: string;
|
||||||
entries: any[],
|
closedAt: Date | string;
|
||||||
};
|
entries: any[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ISalesReceiptsFilter {
|
export interface ISalesReceiptsFilter {}
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ISaleReceiptDTO {
|
export interface ISaleReceiptDTO {
|
||||||
customerId: number,
|
customerId: number;
|
||||||
depositAccountId: number,
|
depositAccountId: number;
|
||||||
receiptDate: Date,
|
receiptDate: Date;
|
||||||
sendToEmail: string,
|
sendToEmail: string;
|
||||||
referenceNo: string,
|
referenceNo: string;
|
||||||
receiptMessage: string,
|
receiptMessage: string;
|
||||||
statement: string,
|
statement: string;
|
||||||
closed: boolean,
|
closed: boolean;
|
||||||
entries: any[],
|
entries: any[];
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface ISalesReceiptService {
|
export interface ISalesReceiptService {
|
||||||
createSaleReceipt(tenantId: number, saleReceiptDTO: ISaleReceiptDTO): Promise<void>;
|
createSaleReceipt(
|
||||||
|
tenantId: number,
|
||||||
|
saleReceiptDTO: ISaleReceiptDTO
|
||||||
|
): Promise<void>;
|
||||||
|
|
||||||
editSaleReceipt(tenantId: number, saleReceiptId: number): Promise<void>;
|
editSaleReceipt(tenantId: number, saleReceiptId: number): Promise<void>;
|
||||||
|
|
||||||
deleteSaleReceipt(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.
|
* The job handler.
|
||||||
* @param {} -
|
|
||||||
*/
|
*/
|
||||||
public async handler(job, done: Function): Promise<void> {
|
public async handler(job, done: Function): Promise<void> {
|
||||||
const Logger = Container.get('logger');
|
const Logger = Container.get('logger');
|
||||||
@@ -51,7 +50,6 @@ export default class ComputeItemCostJob {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the job started.
|
* Handle the job started.
|
||||||
* @param {Job} job - .
|
|
||||||
*/
|
*/
|
||||||
async onJobStart(job) {
|
async onJobStart(job) {
|
||||||
const { startingDate, itemId, tenantId } = job.attrs.data;
|
const { startingDate, itemId, tenantId } = job.attrs.data;
|
||||||
|
|||||||
@@ -12,15 +12,22 @@ export default class MailNotificationSubscribeEnd {
|
|||||||
const subscriptionService = Container.get(SubscriptionService);
|
const subscriptionService = Container.get(SubscriptionService);
|
||||||
const Logger = Container.get('logger');
|
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 {
|
try {
|
||||||
subscriptionService.mailMessages.sendRemainingTrialPeriod(
|
subscriptionService.mailMessages.sendRemainingTrialPeriod(
|
||||||
phoneNumber, remainingDays,
|
phoneNumber,
|
||||||
|
remainingDays
|
||||||
|
);
|
||||||
|
Logger.info(
|
||||||
|
`Send mail notification subscription end soon - finished: ${job.attrs.data}`
|
||||||
);
|
);
|
||||||
Logger.info(`Send mail notification subscription end soon - finished: ${job.attrs.data}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.info(`Send mail notification subscription end soon - failed: ${job.attrs.data}, error: ${e}`);
|
Logger.info(
|
||||||
|
`Send mail notification subscription end soon - failed: ${job.attrs.data}, error: ${e}`
|
||||||
|
);
|
||||||
done(e);
|
done(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +12,22 @@ export default class MailNotificationTrialEnd {
|
|||||||
const subscriptionService = Container.get(SubscriptionService);
|
const subscriptionService = Container.get(SubscriptionService);
|
||||||
const Logger = Container.get('logger');
|
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 {
|
try {
|
||||||
subscriptionService.mailMessages.sendRemainingTrialPeriod(
|
subscriptionService.mailMessages.sendRemainingTrialPeriod(
|
||||||
phoneNumber, remainingDays,
|
phoneNumber,
|
||||||
|
remainingDays
|
||||||
|
);
|
||||||
|
Logger.info(
|
||||||
|
`Send mail notification subscription end soon - finished: ${job.attrs.data}`
|
||||||
);
|
);
|
||||||
Logger.info(`Send mail notification subscription end soon - finished: ${job.attrs.data}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.info(`Send mail notification subscription end soon - failed: ${job.attrs.data}, error: ${e}`);
|
Logger.info(
|
||||||
|
`Send mail notification subscription end soon - failed: ${job.attrs.data}, error: ${e}`
|
||||||
|
);
|
||||||
done(e);
|
done(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,28 @@ import 'subscribers/organization';
|
|||||||
import 'subscribers/inviteUser';
|
import 'subscribers/inviteUser';
|
||||||
import 'subscribers/manualJournals';
|
import 'subscribers/manualJournals';
|
||||||
import 'subscribers/expenses';
|
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/customers';
|
||||||
import 'subscribers/vendors';
|
import 'subscribers/vendors';
|
||||||
import 'subscribers/paymentMades';
|
import 'subscribers/paymentMades';
|
||||||
import 'subscribers/paymentReceives';
|
import 'subscribers/paymentReceives';
|
||||||
import 'subscribers/saleEstimates';
|
import 'subscribers/saleEstimates';
|
||||||
import 'subscribers/saleReceipts';
|
|
||||||
import 'subscribers/inventory';
|
import 'subscribers/inventory';
|
||||||
import 'subscribers/items';
|
import 'subscribers/items';
|
||||||
@@ -129,8 +129,6 @@ export default class Vendor extends TenantModel {
|
|||||||
{ key: 'unpaid', label: 'Unpaid' },
|
{ key: 'unpaid', label: 'Unpaid' },
|
||||||
],
|
],
|
||||||
query: (query, role) => {
|
query: (query, role) => {
|
||||||
console.log(role);
|
|
||||||
|
|
||||||
switch(role.value) {
|
switch(role.value) {
|
||||||
case 'active':
|
case 'active':
|
||||||
query.modify('active');
|
query.modify('active');
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { sumBy, chain } from 'lodash';
|
import { sumBy, chain } from 'lodash';
|
||||||
import moment, { LongDateFormatKey } from 'moment';
|
import moment, { LongDateFormatKey } from 'moment';
|
||||||
import { IBill, IManualJournalEntry, ISystemUser } from 'interfaces';
|
import { IBill, IManualJournalEntry, ISaleReceipt, ISystemUser } from 'interfaces';
|
||||||
import JournalPoster from './JournalPoster';
|
import JournalPoster from './JournalPoster';
|
||||||
import JournalEntry from './JournalEntry';
|
import JournalEntry from './JournalEntry';
|
||||||
import { AccountTransaction } from 'models';
|
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 {
|
import {
|
||||||
IInventoryLotCost,
|
IInventoryLotCost,
|
||||||
IInventoryTransaction,
|
IInventoryTransaction,
|
||||||
IItem,
|
TInventoryTransactionDirection,
|
||||||
IItemEntry,
|
IItemEntry,
|
||||||
|
IItemEntryTransactionType,
|
||||||
} from 'interfaces';
|
} from 'interfaces';
|
||||||
import InventoryAverageCost from 'services/Inventory/InventoryAverageCost';
|
import InventoryAverageCost from 'services/Inventory/InventoryAverageCost';
|
||||||
import InventoryCostLotTracker from 'services/Inventory/InventoryCostLotTracker';
|
import InventoryCostLotTracker from 'services/Inventory/InventoryCostLotTracker';
|
||||||
import TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
import events from 'subscribers/events';
|
import events from 'subscribers/events';
|
||||||
|
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||||
|
|
||||||
type TCostMethod = 'FIFO' | 'LIFO' | 'AVG';
|
type TCostMethod = 'FIFO' | 'LIFO' | 'AVG';
|
||||||
|
|
||||||
@@ -26,22 +28,23 @@ export default class InventoryService {
|
|||||||
@EventDispatcher()
|
@EventDispatcher()
|
||||||
eventDispatcher: EventDispatcherInterface;
|
eventDispatcher: EventDispatcherInterface;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
itemsEntriesService: ItemsEntriesService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms the items entries to inventory transactions.
|
* Transforms the items entries to inventory transactions.
|
||||||
*/
|
*/
|
||||||
transformItemEntriesToInventory(
|
transformItemEntriesToInventory(
|
||||||
itemEntries: IItemEntry[],
|
itemEntries: IItemEntry[],
|
||||||
transactionType: string,
|
direction: TInventoryTransactionDirection,
|
||||||
transactionId: number,
|
|
||||||
direction: 'IN' | 'OUT',
|
|
||||||
date: Date | string,
|
date: Date | string,
|
||||||
lotNumber: number
|
lotNumber: number
|
||||||
) {
|
) {
|
||||||
return itemEntries.map((entry: IItemEntry) => ({
|
return itemEntries.map((entry: IItemEntry) => ({
|
||||||
...pick(entry, ['itemId', 'quantity', 'rate']),
|
...pick(entry, ['itemId', 'quantity', 'rate']),
|
||||||
lotNumber,
|
lotNumber,
|
||||||
transactionType,
|
transactionType: entry.referenceType,
|
||||||
transactionId,
|
transactionId: entry.referenceId,
|
||||||
direction,
|
direction,
|
||||||
date,
|
date,
|
||||||
entryId: entry.id,
|
entryId: entry.id,
|
||||||
@@ -130,7 +133,6 @@ export default class InventoryService {
|
|||||||
tenantId,
|
tenantId,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Triggers `onComputeItemCostJobScheduled` event.
|
// Triggers `onComputeItemCostJobScheduled` event.
|
||||||
await this.eventDispatcher.dispatch(
|
await this.eventDispatcher.dispatch(
|
||||||
events.inventory.onComputeItemCostJobScheduled,
|
events.inventory.onComputeItemCostJobScheduled,
|
||||||
@@ -157,10 +159,12 @@ export default class InventoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Writes the inventory transactiosn on the storage from the given
|
||||||
|
* inventory transactions entries.
|
||||||
*
|
*
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId -
|
||||||
* @param {IInventoryTransaction} inventoryEntry
|
* @param {IInventoryTransaction} inventoryEntry -
|
||||||
* @param {boolean} deleteOld
|
* @param {boolean} deleteOld -
|
||||||
*/
|
*/
|
||||||
async recordInventoryTransaction(
|
async recordInventoryTransaction(
|
||||||
tenantId: number,
|
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.
|
* Deletes the given inventory transactions.
|
||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {string} transactionType
|
* @param {string} transactionType
|
||||||
* @param {number} transactionId
|
* @param {number} transactionId
|
||||||
* @return {Promise}
|
* @return {Promise<{
|
||||||
|
* oldInventoryTransactions: IInventoryTransaction[]
|
||||||
|
* }>}
|
||||||
*/
|
*/
|
||||||
async deleteInventoryTransactions(
|
async deleteInventoryTransactions(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
transactionId: number,
|
transactionId: number,
|
||||||
transactionType: string
|
transactionType: string
|
||||||
): Promise<void> {
|
): Promise<{ oldInventoryTransactions: IInventoryTransaction[] }> {
|
||||||
const { InventoryTransaction } = this.tenancy.models(tenantId);
|
const { inventoryTransactionRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
await InventoryTransaction.query()
|
// Retrieve the inventory transactions of the given sale invoice.
|
||||||
.where('transaction_type', transactionType)
|
const oldInventoryTransactions = await inventoryTransactionRepository.find({
|
||||||
.where('transaction_id', transactionId)
|
transactionId,
|
||||||
.delete();
|
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 moment from 'moment';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import {
|
import {
|
||||||
@@ -16,19 +16,17 @@ import { formatDateFields } from 'utils';
|
|||||||
import {
|
import {
|
||||||
IBillDTO,
|
IBillDTO,
|
||||||
IBill,
|
IBill,
|
||||||
IItem,
|
|
||||||
ISystemUser,
|
ISystemUser,
|
||||||
IBillEditDTO,
|
IBillEditDTO,
|
||||||
IPaginationMeta,
|
IPaginationMeta,
|
||||||
IFilterMeta,
|
IFilterMeta,
|
||||||
IBillsFilter,
|
IBillsFilter,
|
||||||
IItemEntry,
|
|
||||||
IInventoryTransaction,
|
|
||||||
} from 'interfaces';
|
} from 'interfaces';
|
||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
import ItemsService from 'services/Items/ItemsService';
|
import ItemsService from 'services/Items/ItemsService';
|
||||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||||
import JournalCommands from 'services/Accounting/JournalCommands';
|
import JournalCommands from 'services/Accounting/JournalCommands';
|
||||||
|
import JournalPosterService from 'services/Sales/JournalPosterService';
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
BILL_NOT_FOUND: 'BILL_NOT_FOUND',
|
BILL_NOT_FOUND: 'BILL_NOT_FOUND',
|
||||||
@@ -71,6 +69,9 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
@Inject()
|
@Inject()
|
||||||
itemsEntriesService: ItemsEntriesService;
|
itemsEntriesService: ItemsEntriesService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
journalPosterService: JournalPosterService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates whether the vendor is exist.
|
* Validates whether the vendor is exist.
|
||||||
* @async
|
* @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.
|
* Retrieve bills data table list.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
@@ -544,36 +445,6 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
return bill;
|
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.
|
* Mark the bill as open.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -588,10 +459,89 @@ export default class BillsService extends SalesInvoicesCost {
|
|||||||
if (oldBill.isOpen) {
|
if (oldBill.isOpen) {
|
||||||
throw new ServiceError(ERRORS.BILL_ALREADY_OPEN);
|
throw new ServiceError(ERRORS.BILL_ALREADY_OPEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the bill opened at on the storage.
|
// Record the bill opened at on the storage.
|
||||||
await Bill.query().findById(billId).patch({
|
await Bill.query().findById(billId).patch({
|
||||||
openedAt: moment().toMySqlDateTime(),
|
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 { omit, sumBy } from 'lodash';
|
||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { IEstimatesFilter, IFilterMeta, IPaginationMeta, ISaleEstimate, ISaleEstimateDTO } from 'interfaces';
|
import {
|
||||||
|
IEstimatesFilter,
|
||||||
|
IFilterMeta,
|
||||||
|
IPaginationMeta,
|
||||||
|
ISaleEstimate,
|
||||||
|
ISaleEstimateDTO,
|
||||||
|
} from 'interfaces';
|
||||||
import {
|
import {
|
||||||
EventDispatcher,
|
EventDispatcher,
|
||||||
EventDispatcherInterface,
|
EventDispatcherInterface,
|
||||||
@@ -14,7 +20,6 @@ import { ServiceError } from 'exceptions';
|
|||||||
import CustomersService from 'services/Contacts/CustomersService';
|
import CustomersService from 'services/Contacts/CustomersService';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
SALE_ESTIMATE_NOT_FOUND: 'SALE_ESTIMATE_NOT_FOUND',
|
SALE_ESTIMATE_NOT_FOUND: 'SALE_ESTIMATE_NOT_FOUND',
|
||||||
CUSTOMER_NOT_FOUND: 'CUSTOMER_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_CONVERTED_TO_INVOICE: 'SALE_ESTIMATE_CONVERTED_TO_INVOICE',
|
||||||
SALE_ESTIMATE_ALREADY_REJECTED: 'SALE_ESTIMATE_ALREADY_REJECTED',
|
SALE_ESTIMATE_ALREADY_REJECTED: 'SALE_ESTIMATE_ALREADY_REJECTED',
|
||||||
SALE_ESTIMATE_ALREADY_APPROVED: 'SALE_ESTIMATE_ALREADY_APPROVED',
|
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.
|
* Sale estimate service.
|
||||||
* @Service
|
* @Service
|
||||||
@@ -57,7 +63,9 @@ export default class SaleEstimateService {
|
|||||||
*/
|
*/
|
||||||
async getSaleEstimateOrThrowError(tenantId: number, saleEstimateId: number) {
|
async getSaleEstimateOrThrowError(tenantId: number, saleEstimateId: number) {
|
||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
const foundSaleEstimate = await SaleEstimate.query().findById(saleEstimateId);
|
const foundSaleEstimate = await SaleEstimate.query().findById(
|
||||||
|
saleEstimateId
|
||||||
|
);
|
||||||
|
|
||||||
if (!foundSaleEstimate) {
|
if (!foundSaleEstimate) {
|
||||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_FOUND);
|
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_FOUND);
|
||||||
@@ -71,7 +79,11 @@ export default class SaleEstimateService {
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @param {Function} next
|
* @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 { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const foundSaleEstimate = await SaleEstimate.query()
|
const foundSaleEstimate = await SaleEstimate.query()
|
||||||
@@ -96,24 +108,25 @@ export default class SaleEstimateService {
|
|||||||
transformDTOToModel(
|
transformDTOToModel(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
estimateDTO: ISaleEstimateDTO,
|
estimateDTO: ISaleEstimateDTO,
|
||||||
oldSaleEstimate?: ISaleEstimate,
|
oldSaleEstimate?: ISaleEstimate
|
||||||
): ISaleEstimate {
|
): ISaleEstimate {
|
||||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
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 {
|
return {
|
||||||
amount,
|
amount,
|
||||||
...formatDateFields(
|
...formatDateFields(omit(estimateDTO, ['delivered', 'entries']), [
|
||||||
omit(estimateDTO, ['delivered', 'entries']),
|
'estimateDate',
|
||||||
['estimateDate', 'expirationDate']
|
'expirationDate',
|
||||||
),
|
]),
|
||||||
entries: estimateDTO.entries.map((entry) => ({
|
entries: estimateDTO.entries.map((entry) => ({
|
||||||
reference_type: 'SaleEstimate',
|
reference_type: 'SaleEstimate',
|
||||||
...omit(entry, ['total', 'amount', 'id']),
|
...omit(entry, ['total', 'amount', 'id']),
|
||||||
})),
|
})),
|
||||||
|
|
||||||
// Avoid rewrite the deliver date in edit mode when already published.
|
// Avoid rewrite the deliver date in edit mode when already published.
|
||||||
...(estimateDTO.delivered && (!oldSaleEstimate?.deliveredAt)) && ({
|
...(estimateDTO.delivered &&
|
||||||
|
!oldSaleEstimate?.deliveredAt && {
|
||||||
deliveredAt: moment().toMySqlDateTime(),
|
deliveredAt: moment().toMySqlDateTime(),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@@ -139,23 +152,35 @@ export default class SaleEstimateService {
|
|||||||
|
|
||||||
// Validate estimate number uniquiness on the storage.
|
// Validate estimate number uniquiness on the storage.
|
||||||
if (estimateDTO.estimateNumber) {
|
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.
|
// Retrieve the given customer or throw not found service error.
|
||||||
await this.customersService.getCustomer(tenantId, estimateDTO.customerId);
|
await this.customersService.getCustomer(tenantId, estimateDTO.customerId);
|
||||||
|
|
||||||
// Validate items IDs existance on the storage.
|
// 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.
|
// Validate non-sellable items.
|
||||||
await this.itemsEntriesService.validateNonSellableEntriesItems(tenantId, estimateDTO.entries);
|
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||||
|
tenantId,
|
||||||
|
estimateDTO.entries
|
||||||
|
);
|
||||||
|
|
||||||
const saleEstimate = await SaleEstimate.query()
|
const saleEstimate = await SaleEstimate.query().upsertGraphAndFetch({
|
||||||
.upsertGraphAndFetch({ ...estimateObj });
|
...estimateObj,
|
||||||
|
});
|
||||||
|
|
||||||
this.logger.info('[sale_estimate] insert sale estimated success.');
|
this.logger.info('[sale_estimate] insert sale estimated success.');
|
||||||
await this.eventDispatcher.dispatch(events.saleEstimate.onCreated, {
|
await this.eventDispatcher.dispatch(events.saleEstimate.onCreated, {
|
||||||
tenantId, saleEstimate, saleEstimateId: saleEstimate.id,
|
tenantId,
|
||||||
|
saleEstimate,
|
||||||
|
saleEstimateId: saleEstimate.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return saleEstimate;
|
return saleEstimate;
|
||||||
@@ -175,38 +200,61 @@ export default class SaleEstimateService {
|
|||||||
estimateDTO: ISaleEstimateDTO
|
estimateDTO: ISaleEstimateDTO
|
||||||
): Promise<ISaleEstimate> {
|
): Promise<ISaleEstimate> {
|
||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
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.
|
// 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.
|
// Validate estimate number uniquiness on the storage.
|
||||||
if (estimateDTO.estimateNumber) {
|
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.
|
// Retrieve the given customer or throw not found service error.
|
||||||
await this.customersService.getCustomer(tenantId, estimateDTO.customerId);
|
await this.customersService.getCustomer(tenantId, estimateDTO.customerId);
|
||||||
|
|
||||||
// Validate sale estimate entries existance.
|
// 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.
|
// 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.
|
// 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.');
|
this.logger.info('[sale_estimate] editing sale estimate on the storage.');
|
||||||
const saleEstimate = await SaleEstimate.query()
|
const saleEstimate = await SaleEstimate.query().upsertGraphAndFetch({
|
||||||
.upsertGraphAndFetch({
|
|
||||||
id: estimateId,
|
id: estimateId,
|
||||||
...estimateObj
|
...estimateObj,
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.eventDispatcher.dispatch(events.saleEstimate.onEdited, {
|
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;
|
return saleEstimate;
|
||||||
}
|
}
|
||||||
@@ -218,28 +266,41 @@ export default class SaleEstimateService {
|
|||||||
* @param {IEstimate} estimateId
|
* @param {IEstimate} estimateId
|
||||||
* @return {void}
|
* @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);
|
const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
// Retrieve sale estimate or throw not found service error.
|
// 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.
|
// Throw error if the sale estimate converted to sale invoice.
|
||||||
if (oldSaleEstimate.convertedToInvoiceId) {
|
if (oldSaleEstimate.convertedToInvoiceId) {
|
||||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_CONVERTED_TO_INVOICE);
|
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()
|
await ItemEntry.query()
|
||||||
.where('reference_id', estimateId)
|
.where('reference_id', estimateId)
|
||||||
.where('reference_type', 'SaleEstimate')
|
.where('reference_type', 'SaleEstimate')
|
||||||
.delete();
|
.delete();
|
||||||
|
|
||||||
await SaleEstimate.query().where('id', estimateId).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, {
|
await this.eventDispatcher.dispatch(events.saleEstimate.onDeleted, {
|
||||||
tenantId, saleEstimateId: estimateId, oldSaleEstimate,
|
tenantId,
|
||||||
|
saleEstimateId: estimateId,
|
||||||
|
oldSaleEstimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,18 +331,25 @@ export default class SaleEstimateService {
|
|||||||
public async estimatesList(
|
public async estimatesList(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
estimatesFilter: IEstimatesFilter
|
estimatesFilter: IEstimatesFilter
|
||||||
): Promise<{ salesEstimates: ISaleEstimate[], pagination: IPaginationMeta, filterMeta: IFilterMeta }> {
|
): Promise<{
|
||||||
|
salesEstimates: ISaleEstimate[];
|
||||||
|
pagination: IPaginationMeta;
|
||||||
|
filterMeta: IFilterMeta;
|
||||||
|
}> {
|
||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
const dynamicFilter = await this.dynamicListService.dynamicList(tenantId, SaleEstimate, estimatesFilter);
|
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||||
|
tenantId,
|
||||||
|
SaleEstimate,
|
||||||
|
estimatesFilter
|
||||||
|
);
|
||||||
|
|
||||||
const { results, pagination } = await SaleEstimate.query().onBuild(builder => {
|
const { results, pagination } = await SaleEstimate.query()
|
||||||
|
.onBuild((builder) => {
|
||||||
builder.withGraphFetched('customer');
|
builder.withGraphFetched('customer');
|
||||||
builder.withGraphFetched('entries');
|
builder.withGraphFetched('entries');
|
||||||
dynamicFilter.buildQuery()(builder);
|
dynamicFilter.buildQuery()(builder);
|
||||||
}).pagination(
|
})
|
||||||
estimatesFilter.page - 1,
|
.pagination(estimatesFilter.page - 1, estimatesFilter.pageSize);
|
||||||
estimatesFilter.pageSize,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
salesEstimates: results,
|
salesEstimates: results,
|
||||||
@@ -299,12 +367,15 @@ export default class SaleEstimateService {
|
|||||||
async convertEstimateToInvoice(
|
async convertEstimateToInvoice(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
estimateId: number,
|
estimateId: number,
|
||||||
invoiceId: number,
|
invoiceId: number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
// Retrieve details of the given sale estimate.
|
// 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({
|
await SaleEstimate.query().where('id', estimateId).patch({
|
||||||
convertedToInvoiceId: invoiceId,
|
convertedToInvoiceId: invoiceId,
|
||||||
@@ -320,13 +391,15 @@ export default class SaleEstimateService {
|
|||||||
*/
|
*/
|
||||||
async unlinkConvertedEstimateFromInvoice(
|
async unlinkConvertedEstimateFromInvoice(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
invoiceId: number,
|
invoiceId: number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
await SaleEstimate.query().where({
|
await SaleEstimate.query()
|
||||||
|
.where({
|
||||||
convertedToInvoiceId: invoiceId,
|
convertedToInvoiceId: invoiceId,
|
||||||
}).patch({
|
})
|
||||||
|
.patch({
|
||||||
convertedToInvoiceId: null,
|
convertedToInvoiceId: null,
|
||||||
convertedToInvoiceAt: null,
|
convertedToInvoiceAt: null,
|
||||||
});
|
});
|
||||||
@@ -339,12 +412,15 @@ export default class SaleEstimateService {
|
|||||||
*/
|
*/
|
||||||
public async deliverSaleEstimate(
|
public async deliverSaleEstimate(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
saleEstimateId: number,
|
saleEstimateId: number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
// Retrieve details of the given sale estimate id.
|
// 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.
|
// Throws error in case the sale estimate already published.
|
||||||
if (saleEstimate.isDelivered) {
|
if (saleEstimate.isDelivered) {
|
||||||
@@ -352,7 +428,7 @@ export default class SaleEstimateService {
|
|||||||
}
|
}
|
||||||
// Record the delivered at on the storage.
|
// Record the delivered at on the storage.
|
||||||
await SaleEstimate.query().where('id', saleEstimateId).patch({
|
await SaleEstimate.query().where('id', saleEstimateId).patch({
|
||||||
deliveredAt: moment().toMySqlDateTime()
|
deliveredAt: moment().toMySqlDateTime(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,12 +439,15 @@ export default class SaleEstimateService {
|
|||||||
*/
|
*/
|
||||||
public async approveSaleEstimate(
|
public async approveSaleEstimate(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
saleEstimateId: number,
|
saleEstimateId: number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
// Retrieve details of the given sale estimate id.
|
// 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.
|
// Throws error in case the sale estimate still not delivered to customer.
|
||||||
if (!saleEstimate.isDelivered) {
|
if (!saleEstimate.isDelivered) {
|
||||||
@@ -391,12 +470,15 @@ export default class SaleEstimateService {
|
|||||||
*/
|
*/
|
||||||
public async rejectSaleEstimate(
|
public async rejectSaleEstimate(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
saleEstimateId: number,
|
saleEstimateId: number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
// Retrieve details of the given sale estimate id.
|
// 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.
|
// Throws error in case the sale estimate still not delivered to customer.
|
||||||
if (!saleEstimate.isDelivered) {
|
if (!saleEstimate.isDelivered) {
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ export default class SaleInvoicesService {
|
|||||||
}
|
}
|
||||||
// Record the delivered at on the storage.
|
// Record the delivered at on the storage.
|
||||||
await saleInvoiceRepository.update(
|
await saleInvoiceRepository.update(
|
||||||
{ deliveredAt: moment().toMySqlDateTime(), },
|
{ deliveredAt: moment().toMySqlDateTime() },
|
||||||
{ id: saleInvoiceId }
|
{ id: saleInvoiceId }
|
||||||
);
|
);
|
||||||
// Triggers `onSaleInvoiceDelivered` event.
|
// Triggers `onSaleInvoiceDelivered` event.
|
||||||
@@ -416,48 +416,17 @@ export default class SaleInvoicesService {
|
|||||||
*/
|
*/
|
||||||
public async recordInventoryTranscactions(
|
public async recordInventoryTranscactions(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
saleInvoiceId: number,
|
saleInvoice: ISaleInvoice,
|
||||||
saleInvoiceDate: Date,
|
|
||||||
override?: boolean
|
override?: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Gets the next inventory lot number.
|
await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
|
||||||
const lotNumber = this.inventoryService.getNextLotNumber(tenantId);
|
|
||||||
|
|
||||||
// Loads the inventory items entries of the given sale invoice.
|
|
||||||
const inventoryEntries = await this.itemsEntriesService.getInventoryEntries(
|
|
||||||
tenantId,
|
tenantId,
|
||||||
|
saleInvoice.id,
|
||||||
'SaleInvoice',
|
'SaleInvoice',
|
||||||
saleInvoiceId
|
saleInvoice.invoiceDate,
|
||||||
);
|
|
||||||
// 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,
|
|
||||||
'OUT',
|
'OUT',
|
||||||
saleInvoiceDate,
|
|
||||||
lotNumber
|
|
||||||
);
|
|
||||||
// Records the inventory transactions of the given sale invoice.
|
|
||||||
await this.inventoryService.recordInventoryTransactions(
|
|
||||||
tenantId,
|
|
||||||
inventoryTranscations,
|
|
||||||
override
|
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([
|
await Promise.all([
|
||||||
journal.deleteEntries(),
|
journal.deleteEntries(),
|
||||||
journal.saveBalance(),
|
journal.saveBalance(),
|
||||||
journal.saveEntries()
|
journal.saveEntries(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,25 +474,14 @@ export default class SaleInvoicesService {
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
saleInvoiceId: number
|
saleInvoiceId: number
|
||||||
): Promise<void> {
|
): 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.
|
// Delete the inventory transaction of the given sale invoice.
|
||||||
await this.inventoryService.deleteInventoryTransactions(
|
const {
|
||||||
|
oldInventoryTransactions,
|
||||||
|
} = await this.inventoryService.deleteInventoryTransactions(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId,
|
saleInvoiceId,
|
||||||
'SaleInvoice'
|
'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.
|
* Retrieve sale invoice with associated entries.
|
||||||
* @async
|
* @param {Number} saleInvoiceId -
|
||||||
* @param {Number} saleInvoiceId
|
* @param {ISystemUser} authorizedUser -
|
||||||
|
* @return {Promise<ISaleInvoice>}
|
||||||
*/
|
*/
|
||||||
public async getSaleInvoice(
|
public async getSaleInvoice(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { Container, Service, Inject } from 'typedi';
|
import { Container, Service, Inject } from 'typedi';
|
||||||
import { map } from 'lodash';
|
|
||||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||||
import InventoryService from 'services/Inventory/Inventory';
|
import InventoryService from 'services/Inventory/Inventory';
|
||||||
import TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
import { ISaleInvoice, IItemEntry, IInventoryLotCost, IItem } from 'interfaces';
|
import { IInventoryLotCost, IItem } from 'interfaces';
|
||||||
import JournalCommands from 'services/Accounting/JournalCommands';
|
import JournalCommands from 'services/Accounting/JournalCommands';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -39,86 +38,6 @@ export default class SaleInvoicesCost {
|
|||||||
return Promise.all([...asyncOpers]);
|
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.
|
* Schedule writing journal entries.
|
||||||
* @param {Date} startingDate
|
* @param {Date} startingDate
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import {
|
|||||||
EventDispatcherInterface,
|
EventDispatcherInterface,
|
||||||
} from 'decorators/eventDispatcher';
|
} from 'decorators/eventDispatcher';
|
||||||
import events from 'subscribers/events';
|
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 JournalPosterService from 'services/Sales/JournalPosterService';
|
||||||
import TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
import { formatDateFields } from 'utils';
|
import { formatDateFields } from 'utils';
|
||||||
@@ -15,15 +17,16 @@ import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
|||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||||
import { ItemEntry } from 'models';
|
import { ItemEntry } from 'models';
|
||||||
|
import InventoryService from 'services/Inventory/Inventory';
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
SALE_RECEIPT_NOT_FOUND: 'SALE_RECEIPT_NOT_FOUND',
|
SALE_RECEIPT_NOT_FOUND: 'SALE_RECEIPT_NOT_FOUND',
|
||||||
DEPOSIT_ACCOUNT_NOT_FOUND: 'DEPOSIT_ACCOUNT_NOT_FOUND',
|
DEPOSIT_ACCOUNT_NOT_FOUND: 'DEPOSIT_ACCOUNT_NOT_FOUND',
|
||||||
DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET: 'DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET',
|
DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET: 'DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET',
|
||||||
SALE_RECEIPT_NUMBER_NOT_UNIQUE: 'SALE_RECEIPT_NUMBER_NOT_UNIQUE',
|
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()
|
@Service()
|
||||||
export default class SalesReceiptService {
|
export default class SalesReceiptService {
|
||||||
@Inject()
|
@Inject()
|
||||||
@@ -38,6 +41,9 @@ export default class SalesReceiptService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
itemsEntriesService: ItemsEntriesService;
|
itemsEntriesService: ItemsEntriesService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
inventoryService: InventoryService;
|
||||||
|
|
||||||
@EventDispatcher()
|
@EventDispatcher()
|
||||||
eventDispatcher: EventDispatcherInterface;
|
eventDispatcher: EventDispatcherInterface;
|
||||||
|
|
||||||
@@ -52,11 +58,19 @@ export default class SalesReceiptService {
|
|||||||
async getSaleReceiptOrThrowError(tenantId: number, saleReceiptId: number) {
|
async getSaleReceiptOrThrowError(tenantId: number, saleReceiptId: number) {
|
||||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
this.logger.info('[sale_receipt] trying to validate existance.', { tenantId, saleReceiptId });
|
this.logger.info('[sale_receipt] trying to validate existance.', {
|
||||||
const foundSaleReceipt = await SaleReceipt.query().findById(saleReceiptId);
|
tenantId,
|
||||||
|
saleReceiptId,
|
||||||
|
});
|
||||||
|
const foundSaleReceipt = await SaleReceipt.query()
|
||||||
|
.findById(saleReceiptId)
|
||||||
|
.withGraphFetched('entries');
|
||||||
|
|
||||||
if (!foundSaleReceipt) {
|
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);
|
throw new ServiceError(ERRORS.SALE_RECEIPT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return foundSaleReceipt;
|
return foundSaleReceipt;
|
||||||
@@ -67,16 +81,27 @@ export default class SalesReceiptService {
|
|||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
* @param {number} accountId -
|
* @param {number} accountId -
|
||||||
*/
|
*/
|
||||||
async validateReceiptDepositAccountExistance(tenantId: number, accountId: number) {
|
async validateReceiptDepositAccountExistance(
|
||||||
const { accountRepository, accountTypeRepository } = this.tenancy.repositories(tenantId);
|
tenantId: number,
|
||||||
|
accountId: number
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
accountRepository,
|
||||||
|
accountTypeRepository,
|
||||||
|
} = this.tenancy.repositories(tenantId);
|
||||||
const depositAccount = await accountRepository.findOneById(accountId);
|
const depositAccount = await accountRepository.findOneById(accountId);
|
||||||
|
|
||||||
if (!depositAccount) {
|
if (!depositAccount) {
|
||||||
throw new ServiceError(ERRORS.DEPOSIT_ACCOUNT_NOT_FOUND);
|
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);
|
throw new ServiceError(ERRORS.DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,10 +112,17 @@ export default class SalesReceiptService {
|
|||||||
* @param {string} receiptNumber -
|
* @param {string} receiptNumber -
|
||||||
* @param {number} notReceiptId -
|
* @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);
|
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()
|
const saleReceipt = await SaleReceipt.query()
|
||||||
.findOne('receipt_number', receiptNumber)
|
.findOne('receipt_number', receiptNumber)
|
||||||
.onBuild((builder) => {
|
.onBuild((builder) => {
|
||||||
@@ -100,7 +132,9 @@ export default class SalesReceiptService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (saleReceipt) {
|
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);
|
throw new ServiceError(ERRORS.SALE_RECEIPT_NUMBER_NOT_UNIQUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,16 +149,18 @@ export default class SalesReceiptService {
|
|||||||
saleReceiptDTO: ISaleReceiptDTO,
|
saleReceiptDTO: ISaleReceiptDTO,
|
||||||
oldSaleReceipt?: ISaleReceipt
|
oldSaleReceipt?: ISaleReceipt
|
||||||
): ISaleReceipt {
|
): ISaleReceipt {
|
||||||
const amount = sumBy(saleReceiptDTO.entries, e => ItemEntry.calcAmount(e));
|
const amount = sumBy(saleReceiptDTO.entries, (e) =>
|
||||||
|
ItemEntry.calcAmount(e)
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
amount,
|
amount,
|
||||||
...formatDateFields(
|
...formatDateFields(omit(saleReceiptDTO, ['closed', 'entries']), [
|
||||||
omit(saleReceiptDTO, ['closed', 'entries']),
|
'receiptDate',
|
||||||
['receiptDate']
|
]),
|
||||||
),
|
|
||||||
// Avoid rewrite the deliver date in edit mode when already published.
|
// Avoid rewrite the deliver date in edit mode when already published.
|
||||||
...(saleReceiptDTO.closed && (!oldSaleReceipt?.closedAt)) && ({
|
...(saleReceiptDTO.closed &&
|
||||||
|
!oldSaleReceipt?.closedAt && {
|
||||||
closedAt: moment().toMySqlDateTime(),
|
closedAt: moment().toMySqlDateTime(),
|
||||||
}),
|
}),
|
||||||
entries: saleReceiptDTO.entries.map((entry) => ({
|
entries: saleReceiptDTO.entries.map((entry) => ({
|
||||||
@@ -140,31 +176,56 @@ export default class SalesReceiptService {
|
|||||||
* @param {ISaleReceipt} saleReceipt
|
* @param {ISaleReceipt} saleReceipt
|
||||||
* @return {Object}
|
* @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);
|
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
// Transform sale receipt DTO to model.
|
// Transform sale receipt DTO to model.
|
||||||
const saleReceiptObj = this.transformObjectDTOToModel(saleReceiptDTO);
|
const saleReceiptObj = this.transformObjectDTOToModel(saleReceiptDTO);
|
||||||
|
|
||||||
// Validate receipt deposit account existance and type.
|
// 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.
|
// 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.
|
// Validate the sellable items.
|
||||||
await this.itemsEntriesService.validateNonSellableEntriesItems(tenantId, saleReceiptDTO.entries);
|
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||||
|
tenantId,
|
||||||
|
saleReceiptDTO.entries
|
||||||
|
);
|
||||||
|
|
||||||
// Validate sale receipt number uniuqiness.
|
// Validate sale receipt number uniuqiness.
|
||||||
if (saleReceiptDTO.receiptNumber) {
|
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 });
|
this.logger.info('[sale_receipt] trying to insert sale receipt graph.', {
|
||||||
const saleReceipt = await SaleReceipt.query().insertGraphAndFetch({ ...saleReceiptObj });
|
tenantId,
|
||||||
|
saleReceiptDTO,
|
||||||
await this.eventDispatcher.dispatch(events.saleReceipt.onCreated, { tenantId, saleReceipt });
|
});
|
||||||
|
const saleReceipt = await SaleReceipt.query().upsertGraph({
|
||||||
this.logger.info('[sale_receipt] sale receipt inserted successfully.', { tenantId });
|
...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;
|
return saleReceipt;
|
||||||
}
|
}
|
||||||
@@ -175,37 +236,61 @@ export default class SalesReceiptService {
|
|||||||
* @param {ISaleReceipt} saleReceipt
|
* @param {ISaleReceipt} saleReceipt
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
public async editSaleReceipt(tenantId: number, saleReceiptId: number, saleReceiptDTO: any) {
|
public async editSaleReceipt(
|
||||||
const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId);
|
tenantId: number,
|
||||||
|
saleReceiptId: number,
|
||||||
|
saleReceiptDTO: any
|
||||||
|
) {
|
||||||
|
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
// Retrieve sale receipt or throw not found service error.
|
// 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.
|
// 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.
|
// 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.
|
// 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.
|
// Validate the sellable items.
|
||||||
await this.itemsEntriesService.validateNonSellableEntriesItems(tenantId, saleReceiptDTO.entries);
|
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||||
|
tenantId,
|
||||||
|
saleReceiptDTO.entries
|
||||||
|
);
|
||||||
// Validate sale receipt number uniuqiness.
|
// Validate sale receipt number uniuqiness.
|
||||||
if (saleReceiptDTO.receiptNumber) {
|
if (saleReceiptDTO.receiptNumber) {
|
||||||
await this.validateReceiptNumberUnique(tenantId, saleReceiptDTO.receiptNumber, saleReceiptId);
|
await this.validateReceiptNumberUnique(
|
||||||
|
tenantId,
|
||||||
|
saleReceiptDTO.receiptNumber,
|
||||||
|
saleReceiptId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const saleReceipt = await SaleReceipt.query().upsertGraphAndFetch({
|
const saleReceipt = await SaleReceipt.query().upsertGraphAndFetch({
|
||||||
id: saleReceiptId,
|
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, {
|
await this.eventDispatcher.dispatch(events.saleReceipt.onEdited, {
|
||||||
oldSaleReceipt, tenantId, saleReceiptId, saleReceipt,
|
tenantId,
|
||||||
|
oldSaleReceipt,
|
||||||
|
saleReceipt,
|
||||||
|
saleReceiptId,
|
||||||
});
|
});
|
||||||
return saleReceipt;
|
return saleReceipt;
|
||||||
}
|
}
|
||||||
@@ -218,6 +303,10 @@ export default class SalesReceiptService {
|
|||||||
public async deleteSaleReceipt(tenantId: number, saleReceiptId: number) {
|
public async deleteSaleReceipt(tenantId: number, saleReceiptId: number) {
|
||||||
const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId);
|
const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const oldSaleReceipt = await this.getSaleReceiptOrThrowError(
|
||||||
|
tenantId,
|
||||||
|
saleReceiptId
|
||||||
|
);
|
||||||
await ItemEntry.query()
|
await ItemEntry.query()
|
||||||
.where('reference_id', saleReceiptId)
|
.where('reference_id', saleReceiptId)
|
||||||
.where('reference_type', 'SaleReceipt')
|
.where('reference_type', 'SaleReceipt')
|
||||||
@@ -225,8 +314,15 @@ export default class SalesReceiptService {
|
|||||||
|
|
||||||
await SaleReceipt.query().where('id', saleReceiptId).delete();
|
await SaleReceipt.query().where('id', saleReceiptId).delete();
|
||||||
|
|
||||||
this.logger.info('[sale_receipt] deleted successfully.', { tenantId, saleReceiptId });
|
this.logger.info('[sale_receipt] deleted successfully.', {
|
||||||
await this.eventDispatcher.dispatch(events.saleReceipt.onDeleted);
|
tenantId,
|
||||||
|
saleReceiptId,
|
||||||
|
});
|
||||||
|
await this.eventDispatcher.dispatch(events.saleReceipt.onDeleted, {
|
||||||
|
tenantId,
|
||||||
|
saleReceiptId,
|
||||||
|
oldSaleReceipt
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -256,22 +352,31 @@ export default class SalesReceiptService {
|
|||||||
*/
|
*/
|
||||||
public async salesReceiptsList(
|
public async salesReceiptsList(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
salesReceiptsFilter: ISaleReceiptFilter,
|
salesReceiptsFilter: ISaleReceiptFilter
|
||||||
): Promise<{ salesReceipts: ISaleReceipt[], pagination: IPaginationMeta, filterMeta: IFilterMeta }> {
|
): Promise<{
|
||||||
|
salesReceipts: ISaleReceipt[];
|
||||||
|
pagination: IPaginationMeta;
|
||||||
|
filterMeta: IFilterMeta;
|
||||||
|
}> {
|
||||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||||
const dynamicFilter = await this.dynamicListService.dynamicList(tenantId, SaleReceipt, salesReceiptsFilter);
|
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||||
|
tenantId,
|
||||||
|
SaleReceipt,
|
||||||
|
salesReceiptsFilter
|
||||||
|
);
|
||||||
|
|
||||||
this.logger.info('[sale_receipt] try to get sales receipts list.', { tenantId });
|
this.logger.info('[sale_receipt] try to get sales receipts list.', {
|
||||||
const { results, pagination } = await SaleReceipt.query().onBuild((builder) => {
|
tenantId,
|
||||||
|
});
|
||||||
|
const { results, pagination } = await SaleReceipt.query()
|
||||||
|
.onBuild((builder) => {
|
||||||
builder.withGraphFetched('depositAccount');
|
builder.withGraphFetched('depositAccount');
|
||||||
builder.withGraphFetched('customer');
|
builder.withGraphFetched('customer');
|
||||||
builder.withGraphFetched('entries');
|
builder.withGraphFetched('entries');
|
||||||
|
|
||||||
dynamicFilter.buildQuery()(builder);
|
dynamicFilter.buildQuery()(builder);
|
||||||
}).pagination(
|
})
|
||||||
salesReceiptsFilter.page - 1,
|
.pagination(salesReceiptsFilter.page - 1, salesReceiptsFilter.pageSize);
|
||||||
salesReceiptsFilter.pageSize,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
salesReceipts: results,
|
salesReceipts: results,
|
||||||
@@ -293,7 +398,10 @@ export default class SalesReceiptService {
|
|||||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
// Retrieve sale receipt or throw not found service error.
|
// 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.
|
// Throw service error if the sale receipt already closed.
|
||||||
if (oldSaleReceipt.isClosed) {
|
if (oldSaleReceipt.isClosed) {
|
||||||
@@ -304,4 +412,96 @@ export default class SalesReceiptService {
|
|||||||
closedAt: moment().toMySqlDateTime(),
|
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 { Container } from 'typedi';
|
||||||
import { On, EventSubscriber } from "event-dispatch";
|
import { On, EventSubscriber } from 'event-dispatch';
|
||||||
import events from 'subscribers/events';
|
import events from 'subscribers/events';
|
||||||
import TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
import SettingsService from 'services/Settings/SettingsService';
|
import SettingsService from 'services/Settings/SettingsService';
|
||||||
@@ -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',
|
onDelivered: 'onSaleInvoiceDelivered',
|
||||||
onBulkDelete: 'onSaleInvoiceBulkDeleted',
|
onBulkDelete: 'onSaleInvoiceBulkDeleted',
|
||||||
onPublished: 'onSaleInvoicePublished',
|
onPublished: 'onSaleInvoicePublished',
|
||||||
onInventoryTransactionsCreated: 'onInvoiceInventoryTransactionsCreated',
|
|
||||||
onInventoryTransactionsDeleted: 'onInvoiceInventoryTransactionsDeleted',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,8 +126,6 @@ export default {
|
|||||||
onDeleted: 'onBillDeleted',
|
onDeleted: 'onBillDeleted',
|
||||||
onBulkDeleted: 'onBillBulkDeleted',
|
onBulkDeleted: 'onBillBulkDeleted',
|
||||||
onPublished: 'onBillPublished',
|
onPublished: 'onBillPublished',
|
||||||
onInventoryTransactionsCreated: 'onBillInventoryTransactionsCreated',
|
|
||||||
onInventoryTransactionsDeleted: 'onBillInventoryTransactionsDeleted'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,6 +185,9 @@ export default {
|
|||||||
* Inventory service.
|
* Inventory service.
|
||||||
*/
|
*/
|
||||||
inventory: {
|
inventory: {
|
||||||
|
onInventoryTransactionsCreated: 'onInventoryTransactionsCreated',
|
||||||
|
onInventoryTransactionsDeleted: 'onInventoryTransactionsDeleted',
|
||||||
|
|
||||||
onComputeItemCostJobScheduled: 'onComputeItemCostJobScheduled',
|
onComputeItemCostJobScheduled: 'onComputeItemCostJobScheduled',
|
||||||
onComputeItemCostJobStarted: 'onComputeItemCostJobStarted',
|
onComputeItemCostJobStarted: 'onComputeItemCostJobStarted',
|
||||||
onComputeItemCostJobCompleted: 'onComputeItemCostJobCompleted'
|
onComputeItemCostJobCompleted: 'onComputeItemCostJobCompleted'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
import { EventSubscriber, On } from 'event-dispatch';
|
import { EventSubscriber, On } from 'event-dispatch';
|
||||||
|
import { map, head } from 'lodash';
|
||||||
import events from 'subscribers/events';
|
import events from 'subscribers/events';
|
||||||
import SaleInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
import SaleInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
||||||
|
|
||||||
@@ -7,16 +8,20 @@ import SaleInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
|||||||
export class InventorySubscriber {
|
export class InventorySubscriber {
|
||||||
depends: number = 0;
|
depends: number = 0;
|
||||||
startingDate: Date;
|
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.
|
* Handle run writing the journal entries once the compute items jobs completed.
|
||||||
*/
|
*/
|
||||||
@On(events.inventory.onComputeItemCostJobCompleted)
|
@On(events.inventory.onComputeItemCostJobCompleted)
|
||||||
async onComputeItemCostJobFinished({ itemId, tenantId, startingDate }) {
|
async onComputeItemCostJobFinished({ itemId, tenantId, startingDate }) {
|
||||||
const saleInvoicesCost = Container.get(SaleInvoicesCost);
|
const dependsComputeJobs = await this.agenda.jobs({
|
||||||
const agenda = Container.get('agenda');
|
|
||||||
|
|
||||||
const dependsComputeJobs = await agenda.jobs({
|
|
||||||
name: 'compute-item-cost',
|
name: 'compute-item-cost',
|
||||||
nextRunAt: { $ne: null },
|
nextRunAt: { $ne: null },
|
||||||
'data.tenantId': tenantId,
|
'data.tenantId': tenantId,
|
||||||
@@ -25,10 +30,53 @@ export class InventorySubscriber {
|
|||||||
if (dependsComputeJobs.length === 0) {
|
if (dependsComputeJobs.length === 0) {
|
||||||
this.startingDate = null;
|
this.startingDate = null;
|
||||||
|
|
||||||
await saleInvoicesCost.scheduleWriteJournalEntries(
|
await this.saleInvoicesCost.scheduleWriteJournalEntries(
|
||||||
tenantId,
|
tenantId,
|
||||||
startingDate
|
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 isDefinedOptionConfigurable = (key, group) => {
|
||||||
const definedOption = getDefinedOption(key, group);
|
const definedOption = getDefinedOption(key, group);
|
||||||
console.log(definedOption, 'definedOption');
|
|
||||||
|
|
||||||
return definedOption?.config || false;
|
return definedOption?.config || false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user