WIP: Allocate landed cost.

This commit is contained in:
a.bouhuolia
2021-07-24 03:10:32 +02:00
parent 70aea9bf2d
commit cf2ebe9597
30 changed files with 602 additions and 218 deletions

View File

@@ -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(),