Skip to content

Commit

Permalink
feat(server): add tax rates to bills service
Browse files Browse the repository at this point in the history
  • Loading branch information
abouolia committed Oct 6, 2023
1 parent 06ee20c commit a633e3a
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 41 deletions.
9 changes: 9 additions & 0 deletions packages/server/src/interfaces/Bill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface IBillDTO {
branchId?: number;
warehouseId?: number;
projectId?: number;
isInclusiveTax?: boolean;
}

export interface IBillEditDTO {
Expand Down Expand Up @@ -80,6 +81,14 @@ export interface IBill {

localAmount?: number;
locatedLandedCosts?: IBillLandedCost[];

amountLocal: number;
subtotal: number;
subtotalLocal: number;
subtotalExcludingTax: number;
taxAmountWithheldLocal: number;
total: number;
totalLocal: number;
}

export interface IBillsFilter extends IDynamicListFilterDTO {
Expand Down
148 changes: 120 additions & 28 deletions packages/server/src/models/Bill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,110 @@ export default class Bill extends mixin(TenantModel, [
CustomViewBaseModel,
ModelSearchable,
]) {
public amount: number;
public paymentAmount: number;
public landedCostAmount: number;
public allocatedCostAmount: number;
public isInclusiveTax: boolean;
public taxAmountWithheld: number;
public exchangeRate: number;

/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}

/**
* Virtual attributes.
*/
static get virtualAttributes() {
return [
'balance',
'dueAmount',
'isOpen',
'isPartiallyPaid',
'isFullyPaid',
'isPaid',
'remainingDays',
'overdueDays',
'isOverdue',
'unallocatedCostAmount',
'localAmount',
'localAllocatedCostAmount',
'billableAmount',

'amountLocal',
'subtotal',
'subtotalLocal',
'subtotalExludingTax',
'taxAmountWithheldLocal',
'total',
'totalLocal',
];
}

/**
* Invoice amount in base currency.
* @returns {number}
*/
get amountLocal() {
return this.amount * this.exchangeRate;
}

/**
* Subtotal. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number}
*/
get subtotal() {
return this.amount;
}

/**
* Subtotal in base currency. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number}
*/
get subtotalLocal() {
return this.amountLocal;
}

/**
* Sale invoice amount excluding tax.
* @returns {number}
*/
get subtotalExcludingTax() {
return this.isInclusiveTax
? this.subtotal - this.taxAmountWithheld
: this.subtotal;
}

/**
* Tax amount withheld in base currency.
* @returns {number}
*/
get taxAmountWithheldLocal() {
return this.taxAmountWithheld * this.exchangeRate;
}

/**
* Invoice total. (Tax included)
* @returns {number}
*/
get total() {
return this.isInclusiveTax
? this.subtotal
: this.subtotal + this.taxAmountWithheld;
}

/**
* Invoice total in local currency. (Tax included)
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}

/**
* Table name
*/
Expand Down Expand Up @@ -158,34 +262,6 @@ export default class Bill extends mixin(TenantModel, [
};
}

/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}

/**
* Virtual attributes.
*/
static get virtualAttributes() {
return [
'balance',
'dueAmount',
'isOpen',
'isPartiallyPaid',
'isFullyPaid',
'isPaid',
'remainingDays',
'overdueDays',
'isOverdue',
'unallocatedCostAmount',
'localAmount',
'localAllocatedCostAmount',
'billableAmount',
];
}

/**
* Invoice amount in organization base currency.
* @returns {number}
Expand Down Expand Up @@ -326,6 +402,7 @@ export default class Bill extends mixin(TenantModel, [
const ItemEntry = require('models/ItemEntry');
const BillLandedCost = require('models/BillLandedCost');
const Branch = require('models/Branch');
const TaxRateTransaction = require('models/TaxRateTransaction');

return {
vendor: {
Expand Down Expand Up @@ -373,6 +450,21 @@ export default class Bill extends mixin(TenantModel, [
to: 'branches.id',
},
},

/**
* Bill may has associated tax rate transactions.
*/
taxes: {
relation: Model.HasManyRelation,
modelClass: TaxRateTransaction.default,
join: {
from: 'bills.id',
to: 'tax_rate_transactions.referenceId',
},
filter(builder) {
builder.where('reference_type', 'Bill');
},
},
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ export class BillDTOTransformer {
const billNumber = billDTO.billNumber || oldBill?.billNumber;

const initialEntries = billDTO.entries.map((entry) => ({
reference_type: 'Bill',
referenceType: 'Bill',
isInclusiveTax: billDTO.isInclusiveTax,
...omit(entry, ['amount']),
}));
const asyncEntries = await composeAsync(
Expand Down
3 changes: 2 additions & 1 deletion packages/server/src/services/Purchases/Bills/GetBill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export class GetBill {
.findById(billId)
.withGraphFetched('vendor')
.withGraphFetched('entries.item')
.withGraphFetched('branch');
.withGraphFetched('branch')
.withGraphFetched('taxes.taxRate');

// Validates the bill existance.
this.validators.validateBillExistance(bill);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { IBill } from '@/interfaces';
import { Transformer } from '@/lib/Transformer/Transformer';
import { SaleInvoiceTaxEntryTransformer } from '@/services/Sales/Invoices/SaleInvoiceTaxEntryTransformer';
import { formatNumber } from 'utils';

export class PurchaseInvoiceTransformer extends Transformer {
/**
* Include these attributes to sale invoice object.
* Include these attributes to sale bill object.
* @returns {Array}
*/
public includeAttributes = (): string[] => {
Expand All @@ -16,21 +17,28 @@ export class PurchaseInvoiceTransformer extends Transformer {
'formattedBalance',
'formattedDueAmount',
'formattedExchangeRate',
'subtotalFormatted',
'subtotalLocalFormatted',
'subtotalExcludingTaxFormatted',
'taxAmountWithheldLocalFormatted',
'totalFormatted',
'totalLocalFormatted',
'taxes',
];
};

/**
* Retrieve formatted invoice date.
* @param {IBill} invoice
* Retrieve formatted bill date.
* @param {IBill} bill
* @returns {String}
*/
protected formattedBillDate = (bill: IBill): string => {
return this.formatDate(bill.billDate);
};

/**
* Retrieve formatted invoice date.
* @param {IBill} invoice
* Retrieve formatted bill date.
* @param {IBill} bill
* @returns {String}
*/
protected formattedDueDate = (bill: IBill): string => {
Expand All @@ -39,7 +47,7 @@ export class PurchaseInvoiceTransformer extends Transformer {

/**
* Retrieve formatted bill amount.
* @param {IBill} invoice
* @param {IBill} bill
* @returns {string}
*/
protected formattedAmount = (bill): string => {
Expand All @@ -48,7 +56,7 @@ export class PurchaseInvoiceTransformer extends Transformer {

/**
* Retrieve formatted bill amount.
* @param {IBill} invoice
* @param {IBill} bill
* @returns {string}
*/
protected formattedPaymentAmount = (bill): string => {
Expand All @@ -59,7 +67,7 @@ export class PurchaseInvoiceTransformer extends Transformer {

/**
* Retrieve formatted bill amount.
* @param {IBill} invoice
* @param {IBill} bill
* @returns {string}
*/
protected formattedDueAmount = (bill): string => {
Expand All @@ -77,10 +85,90 @@ export class PurchaseInvoiceTransformer extends Transformer {

/**
* Retrieve the formatted exchange rate.
* @param {ISaleInvoice} invoice
* @param {IBill} bill
* @returns {string}
*/
protected formattedExchangeRate = (bill): string => {
return formatNumber(bill.exchangeRate, {
currencyCode: this.context.organization.baseCurrency,
});
};

/**
* Retrieves the formatted subtotal.
* @param {IBill} bill
* @returns {string}
*/
protected subtotalFormatted = (bill): string => {
return formatNumber(bill.subtotal, {
currencyCode: bill.currencyCode,
});
};

/**
* Retrieves the local subtotal formatted.
* @param {IBill} bill
* @returns {string}
*/
protected subtotalLocalFormatted = (bill): string => {
return formatNumber(bill.subtotalLocal, {
currencyCode: this.context.organization.baseCurrency,
});
};

/**
* Retrieves the formatted subtotal tax excluded.
* @param {IBill} bill
* @returns {string}
*/
protected subtotalExcludingTaxFormatted = (bill): string => {
return formatNumber(bill.subtotalExludingTax, {
currencyCode: bill.currencyCode,
});
};

/**
* Retrieves the local formatted tax amount withheld
* @param {IBill} bill
* @returns {string}
*/
protected formattedExchangeRate = (invoice): string => {
return formatNumber(invoice.exchangeRate, { money: false });
protected taxAmountWithheldLocalFormatted = (bill): string => {
return formatNumber(bill.taxAmountWithheldLocal, {
currencyCode: this.context.organization.baseCurrency,
});
};

/**
* Retrieves the total formatted.
* @param {IBill} bill
* @returns {string}
*/
protected totalFormatted = (bill): string => {
return formatNumber(bill.total, {
currencyCode: bill.currencyCode,
});
};

/**
* Retrieves the local total formatted.
* @param {IBill} bill
* @returns {string}
*/
protected totalLocalFormatted = (bill): string => {
return formatNumber(bill.totalLocal, {
currencyCode: this.context.organization.baseCurrency,
});
};

/**
* Retrieve the taxes lines of bill.
* @param {Bill} bill
*/
protected taxes = (bill) => {
return this.item(bill.taxes, new SaleInvoiceTaxEntryTransformer(), {
amount: bill.amount,
isInclusiveTax: bill.isInclusiveTax,
currencyCode: bill.currencyCode,
});
};
}

0 comments on commit a633e3a

Please sign in to comment.