mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
fix: delete customer with associated opening journal entries.
This commit is contained in:
@@ -212,7 +212,7 @@ export default class ContactsController extends BaseController {
|
||||
*/
|
||||
get bulkContactsSchema(): ValidationChain[] {
|
||||
return [
|
||||
query('ids').isArray({ min: 2 }),
|
||||
query('ids').isArray({ min: 1 }),
|
||||
query('ids.*').isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import Bill from 'models/Bill';
|
||||
import BillPayment from 'models/BillPayment';
|
||||
import BillPaymentEntry from 'models/BillPaymentEntry';
|
||||
import Currency from 'models/Currency';
|
||||
import Contact from 'models/Contact';
|
||||
import Vendor from 'models/Vendor';
|
||||
import Customer from 'models/Customer';
|
||||
import ExchangeRate from 'models/ExchangeRate';
|
||||
@@ -69,6 +70,7 @@ export default (knex) => {
|
||||
MediaLink,
|
||||
Vendor,
|
||||
Customer,
|
||||
Contact,
|
||||
};
|
||||
return mapValues(models, (model) => model.bindKnex(knex));
|
||||
}
|
||||
@@ -182,14 +182,14 @@ export default class ContactsService {
|
||||
async getContactsOrThrowErrorNotFound(
|
||||
tenantId: number,
|
||||
contactsIds: number[],
|
||||
contactService: TContactService
|
||||
contactService: TContactService,
|
||||
) {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
const contacts = await Contact.query()
|
||||
.whereIn('id', contactsIds)
|
||||
.where('contact_service', contactService);
|
||||
const storedContactsIds = contacts.map((contact: IContact) => contact.id);
|
||||
|
||||
const storedContactsIds = contacts.map((contact: IContact) => contact.id);
|
||||
const notFoundCustomers = difference(contactsIds, storedContactsIds);
|
||||
|
||||
if (notFoundCustomers.length > 0) {
|
||||
@@ -211,6 +211,8 @@ export default class ContactsService {
|
||||
contactService: TContactService
|
||||
) {
|
||||
const { contactRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Retrieve the given contacts or throw not found service error.
|
||||
this.getContactsOrThrowErrorNotFound(tenantId, contactsIds, contactService);
|
||||
|
||||
await contactRepository.deleteWhereIdIn(contactsIds);
|
||||
@@ -230,11 +232,12 @@ export default class ContactsService {
|
||||
const { AccountTransaction } = this.tenancy.models(tenantId);
|
||||
const journal = new JournalPoster(tenantId);
|
||||
|
||||
// Loads the contact opening balance journal transactions.
|
||||
const contactsTransactions = await AccountTransaction.query()
|
||||
.whereIn('reference_id', contactsIds)
|
||||
.where('reference_type', `${upperFirst(contactService)}OpeningBalance`);
|
||||
|
||||
journal.loadEntries(contactsTransactions);
|
||||
journal.fromTransactions(contactsTransactions);
|
||||
journal.removeEntries();
|
||||
|
||||
await Promise.all([journal.saveBalance(), journal.deleteEntries()]);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { omit, difference, defaultTo } from 'lodash';
|
||||
import { omit, intersection, defaultTo } from 'lodash';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -71,6 +71,10 @@ export default class CustomersService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the contact model to customer model.
|
||||
* @param {IContact} contactModel
|
||||
*/
|
||||
private transformContactToCustomer(contactModel: IContact) {
|
||||
return {
|
||||
...omit(contactModel.toJSON(), ['contactService', 'contactType']),
|
||||
@@ -172,6 +176,7 @@ export default class CustomersService {
|
||||
|
||||
await this.contactService.deleteContact(tenantId, customerId, 'customer');
|
||||
|
||||
// Throws `onCustomerDeleted` event.
|
||||
await this.eventDispatcher.dispatch(events.customers.onDeleted, {
|
||||
tenantId,
|
||||
customerId,
|
||||
@@ -182,29 +187,6 @@ export default class CustomersService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts customer opening balance journal entries.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} customerId -
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async revertOpeningBalanceEntries(
|
||||
tenantId: number,
|
||||
customerId: number | number[]
|
||||
) {
|
||||
const id = Array.isArray(customerId) ? customerId : [customerId];
|
||||
|
||||
this.logger.info(
|
||||
'[customer] trying to revert opening balance journal entries.',
|
||||
{ tenantId, customerId }
|
||||
);
|
||||
await this.contactService.revertJEntriesContactsOpeningBalance(
|
||||
tenantId,
|
||||
id,
|
||||
'customer'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the given customer details.
|
||||
* @param {number} tenantId
|
||||
@@ -262,16 +244,43 @@ export default class CustomersService {
|
||||
public async writeCustomerOpeningBalanceJournal(
|
||||
tenantId: number,
|
||||
customerId: number,
|
||||
openingBalance: number
|
||||
openingBalance: number,
|
||||
openingBalanceAt: Date | string
|
||||
) {
|
||||
const journal = new JournalPoster(tenantId);
|
||||
const journalCommands = new JournalCommands(journal);
|
||||
|
||||
await journalCommands.customerOpeningBalance(customerId, openingBalance);
|
||||
|
||||
await journalCommands.customerOpeningBalance(
|
||||
customerId,
|
||||
openingBalance,
|
||||
openingBalanceAt
|
||||
);
|
||||
await Promise.all([journal.saveBalance(), journal.saveEntries()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts customer opening balance journal entries.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} customerId -
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async revertOpeningBalanceEntries(
|
||||
tenantId: number,
|
||||
customerId: number | number[]
|
||||
) {
|
||||
const id = Array.isArray(customerId) ? customerId : [customerId];
|
||||
|
||||
this.logger.info(
|
||||
'[customer] trying to revert opening balance journal entries.',
|
||||
{ tenantId, customerId }
|
||||
);
|
||||
await this.contactService.revertJEntriesContactsOpeningBalance(
|
||||
tenantId,
|
||||
id,
|
||||
'customer'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the given customer by id or throw not found.
|
||||
* @param {number} tenantId
|
||||
@@ -310,11 +319,20 @@ export default class CustomersService {
|
||||
public async deleteBulkCustomers(tenantId: number, customersIds: number[]) {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Validate the customers existance on the storage.
|
||||
await this.getCustomersOrThrowErrorNotFound(tenantId, customersIds);
|
||||
|
||||
// Validate the customers have no associated invoices.
|
||||
await this.customersHaveNoInvoicesOrThrowError(tenantId, customersIds);
|
||||
|
||||
// Deletes the given customers.
|
||||
await Contact.query().whereIn('id', customersIds).delete();
|
||||
await this.eventDispatcher.dispatch(events.customers.onBulkDeleted);
|
||||
|
||||
// Triggers `onCustomersBulkDeleted` event.
|
||||
await this.eventDispatcher.dispatch(events.customers.onBulkDeleted, {
|
||||
tenantId,
|
||||
customersIds,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -361,12 +379,10 @@ export default class CustomersService {
|
||||
const customersIdsWithInvoice = customersInvoices.map(
|
||||
(saleInvoice: ISaleInvoice) => saleInvoice.customerId
|
||||
);
|
||||
|
||||
const customersHaveInvoices = difference(
|
||||
const customersHaveInvoices = intersection(
|
||||
customersIds,
|
||||
customersIdsWithInvoice
|
||||
);
|
||||
|
||||
if (customersHaveInvoices.length > 0) {
|
||||
throw new ServiceError('some_customers_have_invoices');
|
||||
}
|
||||
|
||||
@@ -465,7 +465,7 @@ export default class ItemsService implements IItemsService {
|
||||
const { Item } = this.tenancy.models(tenantId);
|
||||
|
||||
const storedItems = await Item.query().whereIn('id', itemsIDs);
|
||||
const storedItemsIds = storedItems.map((t) => t.id);
|
||||
const storedItemsIds = storedItems.map((t: IItem) => t.id);
|
||||
|
||||
const notFoundItemsIds = difference(itemsIDs, storedItemsIds);
|
||||
return notFoundItemsIds;
|
||||
@@ -474,7 +474,7 @@ export default class ItemsService implements IItemsService {
|
||||
/**
|
||||
* Deletes items in bulk.
|
||||
* @param {number} tenantId
|
||||
* @param {number[]} itemsIds
|
||||
* @param {number[]} itemsIds - Items ids.
|
||||
*/
|
||||
public async bulkDeleteItems(tenantId: number, itemsIds: number[]) {
|
||||
const { Item } = this.tenancy.models(tenantId);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Container, Inject, Service } from 'typedi';
|
||||
import { Container } from 'typedi';
|
||||
import { EventSubscriber, On } from 'event-dispatch';
|
||||
import events from 'subscribers/events';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
@@ -15,6 +15,9 @@ export default class CustomersSubscriber {
|
||||
this.customersService = Container.get(CustomersService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the writing opening balance journal entries once the customer created.
|
||||
*/
|
||||
@On(events.customers.onCreated)
|
||||
async handleWriteOpenBalanceEntries({ tenantId, customerId, customer }) {
|
||||
|
||||
@@ -24,10 +27,14 @@ export default class CustomersSubscriber {
|
||||
tenantId,
|
||||
customer.id,
|
||||
customer.openingBalance,
|
||||
customer.openingBalanceAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the deleting opeing balance journal entrise once the customer deleted.
|
||||
*/
|
||||
@On(events.customers.onDeleted)
|
||||
async handleRevertOpeningBalanceEntries({ tenantId, customerId }) {
|
||||
|
||||
@@ -36,6 +43,10 @@ export default class CustomersSubscriber {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the deleting opening balance journal entries once the given
|
||||
* customers deleted.
|
||||
*/
|
||||
@On(events.customers.onBulkDeleted)
|
||||
async handleBulkRevertOpeningBalanceEntries({ tenantId, customersIds }) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user