mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +00:00
feat(server): wip tax rate on sale invoice service
This commit is contained in:
@@ -17,6 +17,7 @@ import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { CommandSaleInvoiceValidators } from './CommandSaleInvoiceValidators';
|
||||
import { SaleInvoiceIncrement } from './SaleInvoiceIncrement';
|
||||
import { formatDateFields } from 'utils';
|
||||
import { ItemEntriesTaxTransactions } from '@/services/TaxRates/ItemEntriesTaxTransactions';
|
||||
|
||||
@Service()
|
||||
export class CommandSaleInvoiceDTOTransformer {
|
||||
@@ -38,6 +39,9 @@ export class CommandSaleInvoiceDTOTransformer {
|
||||
@Inject()
|
||||
private invoiceIncrement: SaleInvoiceIncrement;
|
||||
|
||||
@Inject()
|
||||
private taxDTOTransformer: ItemEntriesTaxTransactions;
|
||||
|
||||
/**
|
||||
* Transformes the create DTO to invoice object model.
|
||||
* @param {ISaleInvoiceCreateDTO} saleInvoiceDTO - Sale invoice DTO.
|
||||
@@ -96,6 +100,7 @@ export class CommandSaleInvoiceDTOTransformer {
|
||||
} as ISaleInvoice;
|
||||
|
||||
return R.compose(
|
||||
this.taxDTOTransformer.assocTaxAmountWithheldFromEntries,
|
||||
this.branchDTOTransform.transformDTO<ISaleInvoice>(tenantId),
|
||||
this.warehouseDTOTransform.transformDTO<ISaleInvoice>(tenantId)
|
||||
)(initialDTO);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as R from 'ramda';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
ISaleInvoice,
|
||||
IItemEntry,
|
||||
ILedgerEntry,
|
||||
AccountNormal,
|
||||
ILedger,
|
||||
ITaxTransaction,
|
||||
} from '@/interfaces';
|
||||
import { Knex } from 'knex';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||
@@ -22,8 +23,8 @@ export class SaleInvoiceGLEntries {
|
||||
|
||||
/**
|
||||
* Writes a sale invoice GL entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleInvoiceId
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} saleInvoiceId - Sale invoice id.
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public writeInvoiceGLEntries = async (
|
||||
@@ -42,9 +43,17 @@ export class SaleInvoiceGLEntries {
|
||||
const ARAccount = await accountRepository.findOrCreateAccountReceivable(
|
||||
saleInvoice.currencyCode
|
||||
);
|
||||
// Find or create tax payable account.
|
||||
const taxPayableAccount = await accountRepository.findOrCreateTaxPayable(
|
||||
{},
|
||||
trx
|
||||
);
|
||||
// Retrieves the ledger of the invoice.
|
||||
const ledger = this.getInvoiceGLedger(saleInvoice, ARAccount.id);
|
||||
|
||||
const ledger = this.getInvoiceGLedger(
|
||||
saleInvoice,
|
||||
ARAccount.id,
|
||||
taxPayableAccount.id
|
||||
);
|
||||
// Commits the ledger entries to the storage as UOW.
|
||||
await this.ledegrRepository.commit(tenantId, ledger, trx);
|
||||
};
|
||||
@@ -94,10 +103,14 @@ export class SaleInvoiceGLEntries {
|
||||
*/
|
||||
public getInvoiceGLedger = (
|
||||
saleInvoice: ISaleInvoice,
|
||||
ARAccountId: number
|
||||
ARAccountId: number,
|
||||
taxPayableAccountId: number
|
||||
): ILedger => {
|
||||
const entries = this.getInvoiceGLEntries(saleInvoice, ARAccountId);
|
||||
|
||||
const entries = this.getInvoiceGLEntries(
|
||||
saleInvoice,
|
||||
ARAccountId,
|
||||
taxPayableAccountId
|
||||
);
|
||||
return new Ledger(entries);
|
||||
};
|
||||
|
||||
@@ -143,7 +156,7 @@ export class SaleInvoiceGLEntries {
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
debit: saleInvoice.localAmount,
|
||||
debit: saleInvoice.totalBcy,
|
||||
accountId: ARAccountId,
|
||||
contactId: saleInvoice.customerId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
@@ -176,7 +189,27 @@ export class SaleInvoiceGLEntries {
|
||||
itemId: entry.itemId,
|
||||
itemQuantity: entry.quantity,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
projectId: entry.projectId || saleInvoice.projectId
|
||||
projectId: entry.projectId || saleInvoice.projectId,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retreives the GL entry of tax payable.
|
||||
* @param {ISaleInvoice} saleInvoice -
|
||||
* @param {number} taxPayableAccountId -
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getInvoiceTaxEntry = R.curry(
|
||||
(saleInvoice: ISaleInvoice, taxPayableAccountId: number): ILedgerEntry => {
|
||||
const commonEntry = this.getInvoiceGLCommonEntry(saleInvoice);
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
credit: saleInvoice.taxAmountWithheld,
|
||||
accountId: taxPayableAccountId,
|
||||
index: saleInvoice.entries.length + 3,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -189,15 +222,18 @@ export class SaleInvoiceGLEntries {
|
||||
*/
|
||||
public getInvoiceGLEntries = (
|
||||
saleInvoice: ISaleInvoice,
|
||||
ARAccountId: number
|
||||
ARAccountId: number,
|
||||
taxPayableAccountId: number
|
||||
): ILedgerEntry[] => {
|
||||
const receivableEntry = this.getInvoiceReceivableEntry(
|
||||
saleInvoice,
|
||||
ARAccountId
|
||||
);
|
||||
const transformItemEntry = this.getInvoiceItemEntry(saleInvoice);
|
||||
const creditEntries = saleInvoice.entries.map(transformItemEntry);
|
||||
|
||||
return [receivableEntry, ...creditEntries];
|
||||
const creditEntries = saleInvoice.entries.map(transformItemEntry);
|
||||
const taxEntry = this.getInvoiceTaxEntry(saleInvoice, taxPayableAccountId);
|
||||
|
||||
return [receivableEntry, ...creditEntries, taxEntry];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { ItemEntry } from "@/models";
|
||||
import { sumBy } from "lodash";
|
||||
import { Service } from "typedi";
|
||||
|
||||
|
||||
@Service()
|
||||
export class ItemEntriesTaxTransactions {
|
||||
/**
|
||||
*
|
||||
* @param model
|
||||
* @returns
|
||||
*/
|
||||
public assocTaxAmountWithheldFromEntries(model: any) {
|
||||
const entries = model.entries.map((entry) => ItemEntry.fromJson(entry));
|
||||
const taxAmountWithheld = sumBy(entries, 'taxAmount');
|
||||
|
||||
if (taxAmountWithheld) {
|
||||
model.taxAmountWithheld = taxAmountWithheld;
|
||||
}
|
||||
return model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { sumBy, chain } from 'lodash';
|
||||
import { IItemEntry } from '@/interfaces';
|
||||
import HasTenancyService from '../Tenancy/TenancyService';
|
||||
import { Inject, Service } from 'typedi';
|
||||
|
||||
@Service()
|
||||
export class WriteTaxTransactionsItemEntries {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Writes the tax transactions from the given item entries.
|
||||
* @param {number} tenantId
|
||||
* @param {IItemEntry[]} itemEntries
|
||||
*/
|
||||
public async writeTaxTransactionsFromItemEntries(
|
||||
tenantId: number,
|
||||
itemEntries: IItemEntry[]
|
||||
) {
|
||||
const { TaxRateTransaction } = this.tenancy.models(tenantId);
|
||||
const aggregatedEntries = this.aggregateItemEntriesByTaxCode(itemEntries);
|
||||
|
||||
const taxTransactions = aggregatedEntries.map((entry) => ({
|
||||
taxName: 'TAX NAME',
|
||||
taxCode: 'TAG_CODE',
|
||||
referenceType: entry.referenceType,
|
||||
referenceId: entry.referenceId,
|
||||
taxAmount: entry.taxAmount,
|
||||
}));
|
||||
await TaxRateTransaction.query().upsertGraph(taxTransactions);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IItemEntry[]} itemEntries
|
||||
* @returns {}
|
||||
*/
|
||||
private aggregateItemEntriesByTaxCode(itemEntries: IItemEntry[]) {
|
||||
return chain(itemEntries.filter((item) => item.taxCode))
|
||||
.groupBy((item) => item.taxCode)
|
||||
.values()
|
||||
.map((group) => ({ ...group[0], amount: sumBy(group, 'amount') }))
|
||||
.value();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param itemEntries
|
||||
*/
|
||||
private aggregateItemEntriesByReferenceTypeId(itemEntries: IItemEntry) {}
|
||||
|
||||
/**
|
||||
* Removes the tax transactions from the given item entries.
|
||||
* @param tenantId
|
||||
* @param itemEntries
|
||||
*/
|
||||
public removeTaxTransactionsFromItemEntries(
|
||||
tenantId: number,
|
||||
itemEntries: IItemEntry[]
|
||||
) {
|
||||
const { TaxRateTransaction } = this.tenancy.models(tenantId);
|
||||
|
||||
const filteredEntries = itemEntries.filter((item) => item.taxCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
ISaleInvoiceCreatedPayload,
|
||||
ISaleInvoiceDeletedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { WriteTaxTransactionsItemEntries } from '../WriteTaxTransactionsItemEntries';
|
||||
|
||||
@Service()
|
||||
export class WriteInvoiceTaxTransactionsSubscriber {
|
||||
@Inject()
|
||||
private writeTaxTransactions: WriteTaxTransactionsItemEntries;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onCreated,
|
||||
this.writeInvoiceTaxTransactionsOnCreated
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onDeleted,
|
||||
this.removeInvoiceTaxTransactionsOnDeleted
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate receipt entries tax rate code existance.
|
||||
* @param {ISaleInvoiceCreatingPaylaod}
|
||||
*/
|
||||
private writeInvoiceTaxTransactionsOnCreated = async ({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
}: ISaleInvoiceCreatedPayload) => {
|
||||
await this.writeTaxTransactions.writeTaxTransactionsFromItemEntries(
|
||||
tenantId,
|
||||
saleInvoice.entries
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the invoice tax transactions on invoice deleted.
|
||||
* @param {ISaleInvoiceEditingPayload}
|
||||
*/
|
||||
private removeInvoiceTaxTransactionsOnDeleted = async ({
|
||||
tenantId,
|
||||
oldSaleInvoice,
|
||||
}: ISaleInvoiceDeletedPayload) => {
|
||||
await this.writeTaxTransactions.removeTaxTransactionsFromItemEntries(
|
||||
tenantId,
|
||||
oldSaleInvoice.entries
|
||||
);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user