mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +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(),
|
||||
|
||||
Reference in New Issue
Block a user