mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
WIP: Allocate landed cost.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { omit, sumBy } from 'lodash';
|
||||
import { omit, runInContext, sumBy } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import composeAsync from 'async/compose';
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
IBillsFilter,
|
||||
IBillsService,
|
||||
IItemEntry,
|
||||
IItemEntryDTO,
|
||||
} from 'interfaces';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import ItemsService from 'services/Items/ItemsService';
|
||||
@@ -32,6 +33,7 @@ import JournalCommands from 'services/Accounting/JournalCommands';
|
||||
import JournalPosterService from 'services/Sales/JournalPosterService';
|
||||
import VendorsService from 'services/Contacts/VendorsService';
|
||||
import { ERRORS } from './constants';
|
||||
import EntriesService from 'services/Entries';
|
||||
|
||||
/**
|
||||
* Vendor bills services.
|
||||
@@ -72,6 +74,9 @@ export default class BillsService
|
||||
@Inject()
|
||||
vendorsService: VendorsService;
|
||||
|
||||
@Inject()
|
||||
entriesService: EntriesService;
|
||||
|
||||
/**
|
||||
* Validates whether the vendor is exist.
|
||||
* @async
|
||||
@@ -166,16 +171,33 @@ export default class BillsService
|
||||
* Validate the bill number require.
|
||||
* @param {string} billNo -
|
||||
*/
|
||||
validateBillNoRequire(billNo: string) {
|
||||
private validateBillNoRequire(billNo: string) {
|
||||
if (!billNo) {
|
||||
throw new ServiceError(ERRORS.BILL_NO_IS_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate bill transaction has no associated allocated landed cost transactions.
|
||||
* @param {number} tenantId
|
||||
* @param {number} billId
|
||||
*/
|
||||
private async validateBillHasNoLandedCost(tenantId: number, billId: number) {
|
||||
const { BillLandedCost } = this.tenancy.models(tenantId);
|
||||
|
||||
const billLandedCosts = await BillLandedCost.query().where(
|
||||
'billId',
|
||||
billId
|
||||
);
|
||||
if (billLandedCosts.length > 0) {
|
||||
throw new ServiceError(ERRORS.BILL_HAS_ASSOCIATED_LANDED_COSTS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default cost account to the bill entries.
|
||||
*/
|
||||
setBillEntriesDefaultAccounts(tenantId: number) {
|
||||
private setBillEntriesDefaultAccounts(tenantId: number) {
|
||||
return async (entries: IItemEntry[]) => {
|
||||
const { Item } = this.tenancy.models(tenantId);
|
||||
|
||||
@@ -246,6 +268,7 @@ export default class BillsService
|
||||
billDTO.vendorId
|
||||
);
|
||||
const initialEntries = billDTO.entries.map((entry) => ({
|
||||
amount: ItemEntry.calcAmount(entry),
|
||||
reference_type: 'Bill',
|
||||
...omit(entry, ['amount']),
|
||||
}));
|
||||
@@ -397,6 +420,16 @@ export default class BillsService
|
||||
authorizedUser,
|
||||
oldBill
|
||||
);
|
||||
// Validate landed cost entries that have allocated cost could not be deleted.
|
||||
await this.entriesService.validateLandedCostEntriesNotDeleted(
|
||||
oldBill.entries,
|
||||
billObj.entries,
|
||||
);
|
||||
// Validate new landed cost entries should be bigger than new entries.
|
||||
await this.entriesService.validateLocatedCostEntriesSmallerThanNewEntries(
|
||||
oldBill.entries,
|
||||
billObj.entries
|
||||
);
|
||||
// Update the bill transaction.
|
||||
const bill = await billRepository.upsertGraph({
|
||||
id: billId,
|
||||
@@ -429,6 +462,9 @@ export default class BillsService
|
||||
// Retrieve the given bill or throw not found error.
|
||||
const oldBill = await this.getBillOrThrowError(tenantId, billId);
|
||||
|
||||
// Validate the givne bill has no associated landed cost transactions.
|
||||
await this.validateBillHasNoLandedCost(tenantId, billId);
|
||||
|
||||
// Validate the purchase bill has no assocaited payments transactions.
|
||||
await this.validateBillHasNoEntries(tenantId, billId);
|
||||
|
||||
@@ -561,9 +597,16 @@ export default class BillsService
|
||||
*/
|
||||
public async recordInventoryTransactions(
|
||||
tenantId: number,
|
||||
bill: IBill,
|
||||
billId: number,
|
||||
override?: boolean
|
||||
): Promise<void> {
|
||||
const { Bill } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retireve bill with assocaited entries and allocated cost entries.
|
||||
const bill = await Bill.query()
|
||||
.findById(billId)
|
||||
.withGraphFetched('entries.allocatedCostEntries');
|
||||
|
||||
// Loads the inventory items entries of the given sale invoice.
|
||||
const inventoryEntries =
|
||||
await this.itemsEntriesService.filterInventoryEntries(
|
||||
@@ -573,7 +616,6 @@ export default class BillsService
|
||||
const transaction = {
|
||||
transactionId: bill.id,
|
||||
transactionType: 'Bill',
|
||||
|
||||
date: bill.billDate,
|
||||
direction: 'IN',
|
||||
entries: inventoryEntries,
|
||||
@@ -609,13 +651,30 @@ export default class BillsService
|
||||
*/
|
||||
public async recordJournalTransactions(
|
||||
tenantId: number,
|
||||
bill: IBill,
|
||||
billId: number,
|
||||
override: boolean = false
|
||||
) {
|
||||
const { Bill, Account } = this.tenancy.models(tenantId);
|
||||
|
||||
const journal = new JournalPoster(tenantId);
|
||||
const journalCommands = new JournalCommands(journal);
|
||||
|
||||
await journalCommands.bill(bill, override);
|
||||
const bill = await Bill.query()
|
||||
.findById(billId)
|
||||
.withGraphFetched('entries.item')
|
||||
.withGraphFetched('entries.allocatedCostEntries')
|
||||
.withGraphFetched('locatedLandedCosts.allocateEntries');
|
||||
|
||||
const payableAccount = await Account.query().findOne({
|
||||
slug: 'accounts-payable',
|
||||
});
|
||||
|
||||
// Overrides the bill journal entries.
|
||||
if (override) {
|
||||
await journalCommands.revertJournalEntries(billId, 'Bill');
|
||||
}
|
||||
// Writes the bill journal entries.
|
||||
journalCommands.bill(bill, payableAccount);
|
||||
|
||||
return Promise.all([
|
||||
journal.deleteEntries(),
|
||||
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
export default class BillLandedCost {
|
||||
/**
|
||||
* Retrieve the landed cost transaction from the given bill transaction.
|
||||
* @param {IBill} bill
|
||||
* @returns {ILandedCostTransaction}
|
||||
* @param {IBill} bill - Bill transaction.
|
||||
* @returns {ILandedCostTransaction} - Landed cost transaction.
|
||||
*/
|
||||
public transformToLandedCost = (bill: IBill): ILandedCostTransaction => {
|
||||
const number = bill.billNumber || bill.referenceNo;
|
||||
@@ -49,7 +49,10 @@ export default class BillLandedCost {
|
||||
name: billEntry.item.name,
|
||||
code: billEntry.item.code,
|
||||
amount: billEntry.amount,
|
||||
unallocatedCostAmount: billEntry.unallocatedCostAmount,
|
||||
allocatedCostAmount: billEntry.allocatedCostAmount,
|
||||
description: billEntry.description,
|
||||
costAccountId: billEntry.costAccountId || billEntry.item.costAccountId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ export default class ExpenseLandedCost {
|
||||
code: expenseEntry.expenseAccount.code,
|
||||
amount: expenseEntry.amount,
|
||||
description: expenseEntry.description,
|
||||
allocatedCostAmount: expenseEntry.allocatedCostAmount,
|
||||
unallocatedCostAmount: expenseEntry.unallocatedCostAmount,
|
||||
costAccountId: expenseEntry.expenseAccount.id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import { IBill, IExpense, ILandedCostTransaction } from 'interfaces';
|
||||
import { Model } from 'objection';
|
||||
import { IBill, IExpense, ILandedCostTransaction, ILandedCostTransactionEntry } from 'interfaces';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import BillLandedCost from './BillLandedCost';
|
||||
import ExpenseLandedCost from './ExpenseLandedCost';
|
||||
import HasTenancyService from 'services/Tenancy/TenancyService';
|
||||
import { ERRORS } from './constants';
|
||||
import { ERRORS } from './utils';
|
||||
|
||||
@Service()
|
||||
export default class TransactionLandedCost {
|
||||
@@ -27,7 +28,7 @@ export default class TransactionLandedCost {
|
||||
public getModel = (
|
||||
tenantId: number,
|
||||
transactionType: string
|
||||
): IBill | IExpense => {
|
||||
): Model => {
|
||||
const Models = this.tenancy.models(tenantId);
|
||||
const Model = Models[transactionType];
|
||||
|
||||
@@ -58,4 +59,26 @@ export default class TransactionLandedCost {
|
||||
),
|
||||
)(transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformes the given expense or bill entry to landed cost transaction entry.
|
||||
* @param {string} transactionType
|
||||
* @param {} transactionEntry
|
||||
* @returns {ILandedCostTransactionEntry}
|
||||
*/
|
||||
public transformToLandedCostEntry = (
|
||||
transactionType: 'Bill' | 'Expense',
|
||||
transactionEntry,
|
||||
): ILandedCostTransactionEntry => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
R.always(transactionType === 'Bill'),
|
||||
this.billLandedCost.transformToLandedCostEntry,
|
||||
),
|
||||
R.when(
|
||||
R.always(transactionType === 'Expense'),
|
||||
this.expenseLandedCost.transformToLandedCostEntry,
|
||||
),
|
||||
)(transactionEntry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
|
||||
|
||||
export const ERRORS = {
|
||||
COST_TYPE_UNDEFINED: 'COST_TYPE_UNDEFINED',
|
||||
LANDED_COST_ITEMS_IDS_NOT_FOUND: 'LANDED_COST_ITEMS_IDS_NOT_FOUND',
|
||||
COST_TRANSACTION_HAS_NO_ENOUGH_TO_LOCATE:
|
||||
'COST_TRANSACTION_HAS_NO_ENOUGH_TO_LOCATE',
|
||||
BILL_LANDED_COST_NOT_FOUND: 'BILL_LANDED_COST_NOT_FOUND',
|
||||
COST_ENTRY_ID_NOT_FOUND: 'COST_ENTRY_ID_NOT_FOUND',
|
||||
LANDED_COST_TRANSACTION_NOT_FOUND: 'LANDED_COST_TRANSACTION_NOT_FOUND',
|
||||
LANDED_COST_ENTRY_NOT_FOUND: 'LANDED_COST_ENTRY_NOT_FOUND',
|
||||
COST_AMOUNT_BIGGER_THAN_UNALLOCATED_AMOUNT: 'COST_AMOUNT_BIGGER_THAN_UNALLOCATED_AMOUNT',
|
||||
ALLOCATE_COST_SHOULD_NOT_BE_BILL: 'ALLOCATE_COST_SHOULD_NOT_BE_BILL'
|
||||
};
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { difference, sumBy } from 'lodash';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
} from 'decorators/eventDispatcher';
|
||||
import BillsService from '../Bills';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import {
|
||||
@@ -9,15 +13,14 @@ import {
|
||||
ILandedCostItemDTO,
|
||||
ILandedCostDTO,
|
||||
IBillLandedCostTransaction,
|
||||
IBillLandedCostTransactionEntry,
|
||||
ILandedCostTransaction,
|
||||
ILandedCostTransactionEntry,
|
||||
} from 'interfaces';
|
||||
import events from 'subscribers/events';
|
||||
import InventoryService from 'services/Inventory/Inventory';
|
||||
import HasTenancyService from 'services/Tenancy/TenancyService';
|
||||
import { ERRORS } from './constants';
|
||||
import { transformToMap } from 'utils';
|
||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||
import JournalEntry from 'services/Accounting/JournalEntry';
|
||||
import TransactionLandedCost from './TransctionLandedCost';
|
||||
import { ERRORS, mergeLocatedWithBillEntries } from './utils';
|
||||
|
||||
const CONFIG = {
|
||||
COST_TYPES: {
|
||||
@@ -47,6 +50,9 @@ export default class AllocateLandedCostService {
|
||||
@Inject()
|
||||
public transactionLandedCost: TransactionLandedCost;
|
||||
|
||||
@EventDispatcher()
|
||||
eventDispatcher: EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Validates allocate cost items association with the purchase invoice entries.
|
||||
* @param {IItemEntry[]} purchaseInvoiceEntries
|
||||
@@ -72,23 +78,23 @@ export default class AllocateLandedCostService {
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves the bill landed cost model.
|
||||
* @param {number} tenantId
|
||||
* @param {ILandedCostDTO} landedCostDTO
|
||||
* @param {number} purchaseInvoiceId
|
||||
* @returns {Promise<void>}
|
||||
* Transformes DTO to bill landed cost model object.
|
||||
* @param landedCostDTO
|
||||
* @param bill
|
||||
* @param costTransaction
|
||||
* @param costTransactionEntry
|
||||
* @returns
|
||||
*/
|
||||
private saveBillLandedCostModel = (
|
||||
tenantId: number,
|
||||
private transformToBillLandedCost(
|
||||
landedCostDTO: ILandedCostDTO,
|
||||
purchaseInvoiceId: number
|
||||
): Promise<IBillLandedCost> => {
|
||||
const { BillLandedCost } = this.tenancy.models(tenantId);
|
||||
bill: IBill,
|
||||
costTransaction: ILandedCostTransaction,
|
||||
costTransactionEntry: ILandedCostTransactionEntry
|
||||
) {
|
||||
const amount = sumBy(landedCostDTO.items, 'cost');
|
||||
|
||||
// Inserts the bill landed cost to the storage.
|
||||
return BillLandedCost.query().insertGraph({
|
||||
billId: purchaseInvoiceId,
|
||||
return {
|
||||
billId: bill.id,
|
||||
fromTransactionType: landedCostDTO.transactionType,
|
||||
fromTransactionId: landedCostDTO.transactionId,
|
||||
fromTransactionEntryId: landedCostDTO.transactionEntryId,
|
||||
@@ -96,8 +102,9 @@ export default class AllocateLandedCostService {
|
||||
allocationMethod: landedCostDTO.allocationMethod,
|
||||
description: landedCostDTO.description,
|
||||
allocateEntries: landedCostDTO.items,
|
||||
});
|
||||
};
|
||||
costAccountId: costTransactionEntry.costAccountId,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate the landed cost amount to cost transactions.
|
||||
@@ -147,7 +154,6 @@ export default class AllocateLandedCostService {
|
||||
tenantId,
|
||||
transactionType
|
||||
);
|
||||
|
||||
// Decrement the allocate cost amount of cost transaction.
|
||||
return Model.query()
|
||||
.where('id', transactionId)
|
||||
@@ -202,12 +208,22 @@ export default class AllocateLandedCostService {
|
||||
const entry = await Model.relatedQuery(relation)
|
||||
.for(transactionId)
|
||||
.findOne('id', transactionEntryId)
|
||||
.where('landedCost', true);
|
||||
.where('landedCost', true)
|
||||
.onBuild((q) => {
|
||||
if (transactionType === 'Bill') {
|
||||
q.withGraphFetched('item');
|
||||
} else if (transactionType === 'Expense') {
|
||||
q.withGraphFetched('expenseAccount');
|
||||
}
|
||||
});
|
||||
|
||||
if (!entry) {
|
||||
throw new ServiceError(ERRORS.LANDED_COST_ENTRY_NOT_FOUND);
|
||||
}
|
||||
return entry;
|
||||
return this.transactionLandedCost.transformToLandedCostEntry(
|
||||
transactionType,
|
||||
entry
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -230,31 +246,11 @@ export default class AllocateLandedCostService {
|
||||
unallocatedCost: number,
|
||||
amount: number
|
||||
): void => {
|
||||
console.log(unallocatedCost, amount, '123');
|
||||
|
||||
if (unallocatedCost < amount) {
|
||||
throw new ServiceError(ERRORS.COST_AMOUNT_BIGGER_THAN_UNALLOCATED_AMOUNT);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges item entry to bill located landed cost entry.
|
||||
* @param {IBillLandedCostTransactionEntry[]} locatedEntries -
|
||||
* @param {IItemEntry[]} billEntries -
|
||||
* @returns {(IBillLandedCostTransactionEntry & { entry: IItemEntry })[]}
|
||||
*/
|
||||
private mergeLocatedWithBillEntries = (
|
||||
locatedEntries: IBillLandedCostTransactionEntry[],
|
||||
billEntries: IItemEntry[]
|
||||
): (IBillLandedCostTransactionEntry & { entry: IItemEntry })[] => {
|
||||
const billEntriesByEntryId = transformToMap(billEntries, 'id');
|
||||
|
||||
return locatedEntries.map((entry) => ({
|
||||
...entry,
|
||||
entry: billEntriesByEntryId.get(entry.entryId),
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Records inventory transactions.
|
||||
* @param {number} tenantId
|
||||
@@ -266,7 +262,7 @@ export default class AllocateLandedCostService {
|
||||
bill: IBill
|
||||
) => {
|
||||
// Retrieve the merged allocated entries with bill entries.
|
||||
const allocateEntries = this.mergeLocatedWithBillEntries(
|
||||
const allocateEntries = mergeLocatedWithBillEntries(
|
||||
billLandedCost.allocateEntries,
|
||||
bill.entries
|
||||
);
|
||||
@@ -304,22 +300,24 @@ export default class AllocateLandedCostService {
|
||||
*
|
||||
* @param {ILandedCostDTO} landedCostDTO - Landed cost DTO.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} purchaseInvoiceId - Purchase invoice id.
|
||||
* @param {number} billId - Purchase invoice id.
|
||||
*/
|
||||
public allocateLandedCost = async (
|
||||
tenantId: number,
|
||||
allocateCostDTO: ILandedCostDTO,
|
||||
purchaseInvoiceId: number
|
||||
billId: number
|
||||
): Promise<{
|
||||
billLandedCost: IBillLandedCost;
|
||||
}> => {
|
||||
const { BillLandedCost } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve total cost of allocated items.
|
||||
const amount = this.getAllocateItemsCostTotal(allocateCostDTO);
|
||||
|
||||
// Retrieve the purchase invoice or throw not found error.
|
||||
const purchaseInvoice = await this.billsService.getBillOrThrowError(
|
||||
const bill = await this.billsService.getBillOrThrowError(
|
||||
tenantId,
|
||||
purchaseInvoiceId
|
||||
billId
|
||||
);
|
||||
// Retrieve landed cost transaction or throw not found service error.
|
||||
const landedCostTransaction = await this.getLandedCostOrThrowError(
|
||||
@@ -336,25 +334,36 @@ export default class AllocateLandedCostService {
|
||||
);
|
||||
// Validates allocate cost items association with the purchase invoice entries.
|
||||
this.validateAllocateCostItems(
|
||||
purchaseInvoice.entries,
|
||||
bill.entries,
|
||||
allocateCostDTO.items
|
||||
);
|
||||
// Validate the amount of cost with unallocated landed cost.
|
||||
this.validateLandedCostEntryAmount(
|
||||
landedCostEntry.unallocatedLandedCost,
|
||||
landedCostEntry.unallocatedCostAmount,
|
||||
amount
|
||||
);
|
||||
// Save the bill landed cost model.
|
||||
const billLandedCost = await this.saveBillLandedCostModel(
|
||||
tenantId,
|
||||
// Transformes DTO to bill landed cost model object.
|
||||
const billLandedCostObj = this.transformToBillLandedCost(
|
||||
allocateCostDTO,
|
||||
purchaseInvoiceId
|
||||
bill,
|
||||
landedCostTransaction,
|
||||
landedCostEntry
|
||||
);
|
||||
// Save the bill landed cost model.
|
||||
const billLandedCost = await BillLandedCost.query().insertGraph(
|
||||
billLandedCostObj
|
||||
);
|
||||
// Triggers the event `onBillLandedCostCreated`.
|
||||
await this.eventDispatcher.dispatch(events.billLandedCost.onCreated, {
|
||||
tenantId,
|
||||
billId,
|
||||
billLandedCostId: billLandedCost.id,
|
||||
});
|
||||
// Records the inventory transactions.
|
||||
await this.recordInventoryTransactions(
|
||||
tenantId,
|
||||
billLandedCost,
|
||||
purchaseInvoice
|
||||
bill
|
||||
);
|
||||
// Increment landed cost amount on transaction and entry.
|
||||
await this.incrementLandedCostAmount(
|
||||
@@ -364,55 +373,9 @@ export default class AllocateLandedCostService {
|
||||
allocateCostDTO.transactionEntryId,
|
||||
amount
|
||||
);
|
||||
// Write the landed cost journal entries.
|
||||
// await this.writeJournalEntry(tenantId, billLandedCost, purchaseInvoice);
|
||||
|
||||
return { billLandedCost };
|
||||
};
|
||||
|
||||
/**
|
||||
* Write journal entries of the given purchase invoice landed cost.
|
||||
* @param tenantId
|
||||
* @param purchaseInvoice
|
||||
* @param landedCost
|
||||
*/
|
||||
private writeJournalEntry = async (
|
||||
tenantId: number,
|
||||
landedCostEntry: any,
|
||||
purchaseInvoice: IBill,
|
||||
landedCost: IBillLandedCost
|
||||
) => {
|
||||
const journal = new JournalPoster(tenantId);
|
||||
const billEntriesById = purchaseInvoice.entries;
|
||||
|
||||
const commonEntry = {
|
||||
referenceType: 'Bill',
|
||||
referenceId: purchaseInvoice.id,
|
||||
date: purchaseInvoice.billDate,
|
||||
indexGroup: 300,
|
||||
};
|
||||
const costEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
credit: landedCost.amount,
|
||||
account: landedCost.costAccountId,
|
||||
index: 1,
|
||||
});
|
||||
journal.credit(costEntry);
|
||||
|
||||
landedCost.allocateEntries.forEach((entry, index) => {
|
||||
const billEntry = billEntriesById[entry.entryId];
|
||||
|
||||
const inventoryEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
debit: entry.cost,
|
||||
account: billEntry.item.inventoryAccountId,
|
||||
index: 1 + index,
|
||||
});
|
||||
journal.debit(inventoryEntry);
|
||||
});
|
||||
return journal;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the give bill landed cost or throw not found service error.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
@@ -422,7 +385,7 @@ export default class AllocateLandedCostService {
|
||||
public getBillLandedCostOrThrowError = async (
|
||||
tenantId: number,
|
||||
landedCostId: number
|
||||
): Promise<IBillLandedCost> => {
|
||||
): Promise<IBillLandedCostTransaction> => {
|
||||
const { BillLandedCost } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the bill landed cost model.
|
||||
@@ -462,7 +425,7 @@ export default class AllocateLandedCostService {
|
||||
* - Delete the associated inventory transactions.
|
||||
* - Decrement allocated amount of landed cost transaction and entry.
|
||||
* - Revert journal entries.
|
||||
*
|
||||
* ----------------------------------
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} landedCostId - Landed cost id.
|
||||
* @return {Promise<void>}
|
||||
@@ -481,6 +444,12 @@ export default class AllocateLandedCostService {
|
||||
// Delete landed cost transaction with assocaited locate entries.
|
||||
await this.deleteLandedCost(tenantId, landedCostId);
|
||||
|
||||
// Triggers the event `onBillLandedCostCreated`.
|
||||
await this.eventDispatcher.dispatch(events.billLandedCost.onDeleted, {
|
||||
tenantId,
|
||||
billLandedCostId: oldBillLandedCost.id,
|
||||
billId: oldBillLandedCost.billId,
|
||||
});
|
||||
// Removes the inventory transactions.
|
||||
await this.removeInventoryTransactions(tenantId, landedCostId);
|
||||
|
||||
|
||||
34
server/src/services/Purchases/LandedCost/utils.ts
Normal file
34
server/src/services/Purchases/LandedCost/utils.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { IItemEntry, IBillLandedCostTransactionEntry } from 'interfaces';
|
||||
import { transformToMap } from 'utils';
|
||||
|
||||
export const ERRORS = {
|
||||
COST_TYPE_UNDEFINED: 'COST_TYPE_UNDEFINED',
|
||||
LANDED_COST_ITEMS_IDS_NOT_FOUND: 'LANDED_COST_ITEMS_IDS_NOT_FOUND',
|
||||
COST_TRANSACTION_HAS_NO_ENOUGH_TO_LOCATE:
|
||||
'COST_TRANSACTION_HAS_NO_ENOUGH_TO_LOCATE',
|
||||
BILL_LANDED_COST_NOT_FOUND: 'BILL_LANDED_COST_NOT_FOUND',
|
||||
COST_ENTRY_ID_NOT_FOUND: 'COST_ENTRY_ID_NOT_FOUND',
|
||||
LANDED_COST_TRANSACTION_NOT_FOUND: 'LANDED_COST_TRANSACTION_NOT_FOUND',
|
||||
LANDED_COST_ENTRY_NOT_FOUND: 'LANDED_COST_ENTRY_NOT_FOUND',
|
||||
COST_AMOUNT_BIGGER_THAN_UNALLOCATED_AMOUNT:
|
||||
'COST_AMOUNT_BIGGER_THAN_UNALLOCATED_AMOUNT',
|
||||
ALLOCATE_COST_SHOULD_NOT_BE_BILL: 'ALLOCATE_COST_SHOULD_NOT_BE_BILL',
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges item entry to bill located landed cost entry.
|
||||
* @param {IBillLandedCostTransactionEntry[]} locatedEntries -
|
||||
* @param {IItemEntry[]} billEntries -
|
||||
* @returns {(IBillLandedCostTransactionEntry & { entry: IItemEntry })[]}
|
||||
*/
|
||||
export const mergeLocatedWithBillEntries = (
|
||||
locatedEntries: IBillLandedCostTransactionEntry[],
|
||||
billEntries: IItemEntry[]
|
||||
): (IBillLandedCostTransactionEntry & { entry: IItemEntry })[] => {
|
||||
const billEntriesByEntryId = transformToMap(billEntries, 'id');
|
||||
|
||||
return locatedEntries.map((entry) => ({
|
||||
...entry,
|
||||
entry: billEntriesByEntryId.get(entry.entryId),
|
||||
}));
|
||||
};
|
||||
@@ -9,5 +9,8 @@ export const ERRORS = {
|
||||
BILL_ALREADY_OPEN: 'BILL_ALREADY_OPEN',
|
||||
BILL_NO_IS_REQUIRED: 'BILL_NO_IS_REQUIRED',
|
||||
BILL_HAS_ASSOCIATED_PAYMENT_ENTRIES: 'BILL_HAS_ASSOCIATED_PAYMENT_ENTRIES',
|
||||
VENDOR_HAS_BILLS: 'VENDOR_HAS_BILLS'
|
||||
VENDOR_HAS_BILLS: 'VENDOR_HAS_BILLS',
|
||||
BILL_HAS_ASSOCIATED_LANDED_COSTS: 'BILL_HAS_ASSOCIATED_LANDED_COSTS',
|
||||
BILL_ENTRIES_ALLOCATED_COST_COULD_DELETED: 'BILL_ENTRIES_ALLOCATED_COST_COULD_DELETED',
|
||||
LOCATED_COST_ENTRIES_SHOULD_BIGGE_THAN_NEW_ENTRIES: 'LOCATED_COST_ENTRIES_SHOULD_BIGGE_THAN_NEW_ENTRIES'
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user