Merge branch 'develop' into migrate-server-nestjs

This commit is contained in:
Ahmed Bouhuolia
2024-12-29 11:14:15 +02:00
282 changed files with 11273 additions and 3304 deletions

View File

@@ -1,12 +1,14 @@
import { Model, raw, mixin } from 'objection';
import { castArray, difference } from 'lodash';
import { castArray, defaultTo, difference } from 'lodash';
import moment from 'moment';
import * as R from 'ramda';
import TenantModel from 'models/TenantModel';
import BillSettings from './Bill.Settings';
import ModelSetting from './ModelSetting';
import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Purchases/Bills/constants';
import ModelSearchable from './ModelSearchable';
import { DiscountType } from '@/interfaces';
export default class Bill extends mixin(TenantModel, [
ModelSetting,
@@ -21,6 +23,11 @@ export default class Bill extends mixin(TenantModel, [
public taxAmountWithheld: number;
public exchangeRate: number;
public discount: number;
public discountType: DiscountType;
public adjustment: number;
/**
* Timestamps columns.
*/
@@ -47,6 +54,13 @@ export default class Bill extends mixin(TenantModel, [
'localAllocatedCostAmount',
'billableAmount',
'amountLocal',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'adjustmentLocal',
'subtotal',
'subtotalLocal',
'subtotalExludingTax',
@@ -98,14 +112,53 @@ export default class Bill extends mixin(TenantModel, [
return this.taxAmountWithheld * this.exchangeRate;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount amount in local currency.
* @returns {number | null}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Adjustment amount in local currency.
* @returns {number | null}
*/
get adjustmentLocal() {
return this.adjustment ? this.adjustment * this.exchangeRate : null;
}
/**
* Invoice total. (Tax included)
* @returns {number}
*/
get total() {
return this.isInclusiveTax
? this.subtotal
: this.subtotal + this.taxAmountWithheld;
const adjustmentAmount = defaultTo(this.adjustment, 0);
return R.compose(
R.add(adjustmentAmount),
R.subtract(R.__, this.discountAmount),
R.when(R.always(this.isInclusiveTax), R.add(this.taxAmountWithheld))
)(this.subtotal);
}
/**

View File

@@ -5,12 +5,20 @@ import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/CreditNotes/constants';
import ModelSearchable from './ModelSearchable';
import CreditNoteMeta from './CreditNote.Meta';
import { DiscountType } from '@/interfaces';
export default class CreditNote extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
public amount: number;
public exchangeRate: number;
public openedAt: Date;
public discount: number;
public discountType: DiscountType;
public adjustment: number;
/**
* Table name
*/
@@ -35,8 +43,21 @@ export default class CreditNote extends mixin(TenantModel, [
'isPublished',
'isOpen',
'isClosed',
'creditsRemaining',
'creditsUsed',
'subtotal',
'subtotalLocal',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'total',
'totalLocal',
'adjustmentLocal',
];
}
@@ -48,6 +69,72 @@ export default class CreditNote extends mixin(TenantModel, [
return this.amount * this.exchangeRate;
}
/**
* Credit note subtotal.
* @returns {number}
*/
get subtotal() {
return this.amount;
}
/**
* Credit note subtotal in local currency.
* @returns {number}
*/
get subtotalLocal() {
return this.subtotal * this.exchangeRate;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount amount in local currency.
* @returns {number}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Adjustment amount in local currency.
* @returns {number}
*/
get adjustmentLocal() {
return this.adjustment ? this.adjustment * this.exchangeRate : null;
}
/**
* Credit note total.
* @returns {number}
*/
get total() {
return this.subtotal - this.discountAmount + this.adjustment;
}
/**
* Credit note total in local currency.
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/**
* Detarmines whether the credit note is draft.
* @returns {boolean}
@@ -176,6 +263,7 @@ export default class CreditNote extends mixin(TenantModel, [
const Branch = require('models/Branch');
const Document = require('models/Document');
const Warehouse = require('models/Warehouse');
const { PdfTemplate } = require('models/PdfTemplate');
return {
/**
@@ -266,6 +354,18 @@ export default class CreditNote extends mixin(TenantModel, [
query.where('model_ref', 'CreditNote');
},
},
/**
* Credit note may belongs to pdf branding template.
*/
pdfTemplate: {
relation: Model.BelongsToOneRelation,
modelClass: PdfTemplate,
join: {
from: 'credit_notes.pdfTemplateId',
to: 'pdf_templates.id',
},
},
};
}

View File

@@ -1,6 +1,12 @@
import { Model } from 'objection';
import TenantModel from 'models/TenantModel';
import { getExlusiveTaxAmount, getInclusiveTaxAmount } from '@/utils/taxRate';
import { DiscountType } from '@/interfaces';
// Subtotal (qty * rate) (tax inclusive)
// Subtotal Tax Exclusive (Subtotal - Tax Amount)
// Discount (Is percentage ? amount * discount : discount)
// Total (Subtotal - Discount)
export default class ItemEntry extends TenantModel {
public taxRate: number;
@@ -8,7 +14,7 @@ export default class ItemEntry extends TenantModel {
public quantity: number;
public rate: number;
public isInclusiveTax: number;
public discountType: DiscountType;
/**
* Table name.
* @returns {string}
@@ -31,10 +37,24 @@ export default class ItemEntry extends TenantModel {
*/
static get virtualAttributes() {
return [
// Amount (qty * rate)
'amount',
'taxAmount',
'amountExludingTax',
'amountInclusingTax',
// Subtotal (qty * rate) + (tax inclusive)
'subtotalInclusingTax',
// Subtotal Tax Exclusive (Subtotal - Tax Amount)
'subtotalExcludingTax',
// Subtotal (qty * rate) + (tax inclusive)
'subtotal',
// Discount (Is percentage ? amount * discount : discount)
'discountAmount',
// Total (Subtotal - Discount)
'total',
];
}
@@ -45,7 +65,15 @@ export default class ItemEntry extends TenantModel {
* @returns {number}
*/
get total() {
return this.amountInclusingTax;
return this.subtotal - this.discountAmount;
}
/**
* Total (excluding tax).
* @returns {number}
*/
get totalExcludingTax() {
return this.subtotalExcludingTax - this.discountAmount;
}
/**
@@ -57,19 +85,27 @@ export default class ItemEntry extends TenantModel {
return this.quantity * this.rate;
}
/**
* Subtotal amount (tax inclusive).
* @returns {number}
*/
get subtotal() {
return this.subtotalInclusingTax;
}
/**
* Item entry amount including tax.
* @returns {number}
*/
get amountInclusingTax() {
get subtotalInclusingTax() {
return this.isInclusiveTax ? this.amount : this.amount + this.taxAmount;
}
/**
* Item entry amount excluding tax.
* Subtotal amount (tax exclusive).
* @returns {number}
*/
get amountExludingTax() {
get subtotalExcludingTax() {
return this.isInclusiveTax ? this.amount - this.taxAmount : this.amount;
}
@@ -78,7 +114,9 @@ export default class ItemEntry extends TenantModel {
* @returns {number}
*/
get discountAmount() {
return this.amount * (this.discount / 100);
return this.discountType === DiscountType.Percentage
? this.amount * (this.discount / 100)
: this.discount;
}
/**

View File

@@ -11,6 +11,10 @@ export default class PaymentReceive extends mixin(TenantModel, [
CustomViewBaseModel,
ModelSearchable,
]) {
amount!: number;
paymentAmount!: number;
exchangeRate!: number;
/**
* Table name.
*/
@@ -29,7 +33,7 @@ export default class PaymentReceive extends mixin(TenantModel, [
* Virtual attributes.
*/
static get virtualAttributes() {
return ['localAmount'];
return ['localAmount', 'total'];
}
/**
@@ -40,6 +44,14 @@ export default class PaymentReceive extends mixin(TenantModel, [
return this.amount * this.exchangeRate;
}
/**
* Payment receive total.
* @returns {number}
*/
get total() {
return this.paymentAmount;
}
/**
* Resourcable model.
*/
@@ -57,6 +69,7 @@ export default class PaymentReceive extends mixin(TenantModel, [
const Account = require('models/Account');
const Branch = require('models/Branch');
const Document = require('models/Document');
const { PdfTemplate } = require('models/PdfTemplate');
return {
customer: {
@@ -131,6 +144,18 @@ export default class PaymentReceive extends mixin(TenantModel, [
query.where('model_ref', 'PaymentReceive');
},
},
/**
* Payment received may belongs to pdf branding template.
*/
pdfTemplate: {
relation: Model.BelongsToOneRelation,
modelClass: PdfTemplate,
join: {
from: 'payment_receives.pdfTemplateId',
to: 'pdf_templates.id',
},
},
};
}

View File

@@ -7,12 +7,30 @@ import ModelSetting from './ModelSetting';
import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Sales/Estimates/constants';
import ModelSearchable from './ModelSearchable';
import { DiscountType } from '@/interfaces';
import { defaultTo } from 'lodash';
export default class SaleEstimate extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
public amount: number;
public exchangeRate: number;
public discount: number;
public discountType: DiscountType;
public adjustment: number;
public expirationDate!: string;
public deliveredAt!: string | null;
public approvedAt!: string | null;
public rejectedAt!: string | null;
public convertedToInvoiceId!: number | null;
public convertedToInvoiceAt!: string | null;
/**
* Table name
*/
@@ -33,6 +51,12 @@ export default class SaleEstimate extends mixin(TenantModel, [
static get virtualAttributes() {
return [
'localAmount',
'discountAmount',
'discountPercentage',
'total',
'totalLocal',
'subtotal',
'subtotalLocal',
'isDelivered',
'isExpired',
'isConvertedToInvoice',
@@ -49,6 +73,60 @@ export default class SaleEstimate extends mixin(TenantModel, [
return this.amount * this.exchangeRate;
}
/**
* Estimate subtotal.
* @returns {number}
*/
get subtotal() {
return this.amount;;
}
/**
* Estimate subtotal in local currency.
* @returns {number}
*/
get subtotalLocal() {
return this.localAmount;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage
? this.discount
: null;
}
/**
* Estimate total.
* @returns {number}
*/
get total() {
const adjustmentAmount = defaultTo(this.adjustment, 0);
return this.subtotal - this.discountAmount + adjustmentAmount;
}
/**
* Estimate total in local currency.
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/**
* Detarmines whether the sale estimate converted to sale invoice.
* @return {boolean}
@@ -184,6 +262,7 @@ export default class SaleEstimate extends mixin(TenantModel, [
const Branch = require('models/Branch');
const Warehouse = require('models/Warehouse');
const Document = require('models/Document');
const { PdfTemplate } = require('models/PdfTemplate');
return {
customer: {
@@ -252,6 +331,18 @@ export default class SaleEstimate extends mixin(TenantModel, [
query.where('model_ref', 'SaleEstimate');
},
},
/**
* Sale estimate may belongs to pdf branding template.
*/
pdfTemplate: {
relation: Model.BelongsToOneRelation,
modelClass: PdfTemplate,
join: {
from: 'sales_estimates.pdfTemplateId',
to: 'pdf_templates.id',
},
},
};
}

View File

@@ -1,5 +1,6 @@
import { mixin, Model, raw } from 'objection';
import { castArray, takeWhile } from 'lodash';
import * as R from 'ramda';
import { castArray, defaultTo, takeWhile } from 'lodash';
import moment from 'moment';
import TenantModel from 'models/TenantModel';
import ModelSetting from './ModelSetting';
@@ -7,6 +8,7 @@ import SaleInvoiceMeta from './SaleInvoice.Settings';
import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Sales/Invoices/constants';
import ModelSearchable from './ModelSearchable';
import { DiscountType } from '@/interfaces';
export default class SaleInvoice extends mixin(TenantModel, [
ModelSetting,
@@ -24,6 +26,9 @@ export default class SaleInvoice extends mixin(TenantModel, [
public dueDate: Date;
public deliveredAt: Date;
public pdfTemplateId: number;
public discount: number;
public discountType: DiscountType;
public adjustment: number | null;
/**
* Table name
@@ -68,10 +73,15 @@ export default class SaleInvoice extends mixin(TenantModel, [
'subtotalExludingTax',
'taxAmountWithheldLocal',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'total',
'totalLocal',
'writtenoffAmountLocal',
'adjustmentLocal',
];
}
@@ -126,14 +136,52 @@ export default class SaleInvoice extends mixin(TenantModel, [
return this.taxAmountWithheld * this.exchangeRate;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Local discount amount.
* @returns {number | null}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Adjustment amount in local currency.
* @returns {number | null}
*/
get adjustmentLocal(): number | null {
return this.adjustment ? this.adjustment * this.exchangeRate : null;
}
/**
* Invoice total. (Tax included)
* @returns {number}
*/
get total() {
return this.isInclusiveTax
? this.subtotal
: this.subtotal + this.taxAmountWithheld;
const adjustmentAmount = defaultTo(this.adjustment, 0);
return R.compose(
R.add(adjustmentAmount),
R.subtract(R.__, this.discountAmount),
R.when(R.always(this.isInclusiveTax), R.add(this.taxAmountWithheld))
)(this.subtotal);
}
/**
@@ -605,7 +653,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
join: {
from: 'sales_invoices.pdfTemplateId',
to: 'pdf_templates.id',
}
},
},
};
}

View File

@@ -5,12 +5,23 @@ import SaleReceiptSettings from './SaleReceipt.Settings';
import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Sales/Receipts/constants';
import ModelSearchable from './ModelSearchable';
import { DiscountType } from '@/interfaces';
import { defaultTo } from 'lodash';
export default class SaleReceipt extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
public amount: number;
public exchangeRate: number;
public closedAt: Date;
public discount: number;
public discountType: DiscountType;
public adjustment: number;
/**
* Table name
*/
@@ -29,7 +40,28 @@ export default class SaleReceipt extends mixin(TenantModel, [
* Virtual attributes.
*/
static get virtualAttributes() {
return ['localAmount', 'isClosed', 'isDraft'];
return [
'localAmount',
'subtotal',
'subtotalLocal',
'total',
'totalLocal',
'adjustment',
'adjustmentLocal',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'paid',
'paidLocal',
'isClosed',
'isDraft',
];
}
/**
@@ -40,6 +72,90 @@ export default class SaleReceipt extends mixin(TenantModel, [
return this.amount * this.exchangeRate;
}
/**
* Receipt subtotal.
* @returns {number}
*/
get subtotal() {
return this.amount;
}
/**
* Receipt subtotal in local currency.
* @returns {number}
*/
get subtotalLocal() {
return this.localAmount;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount amount in local currency.
* @returns {number | null}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Receipt total.
* @returns {number}
*/
get total() {
const adjustmentAmount = defaultTo(this.adjustment, 0);
return this.subtotal - this.discountAmount + adjustmentAmount;
}
/**
* Receipt total in local currency.
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/**
* Adjustment amount in local currency.
* @returns {number}
*/
get adjustmentLocal() {
return this.adjustment * this.exchangeRate;
}
/**
* Receipt paid amount.
* @returns {number}
*/
get paid() {
return this.total;
}
/**
* Receipt paid amount in local currency.
* @returns {number}
*/
get paidLocal() {
return this.paid * this.exchangeRate;
}
/**
* Detarmine whether the sale receipt closed.
* @return {boolean}

View File

@@ -6,26 +6,26 @@ import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Purchases/VendorCredits/constants';
import ModelSearchable from './ModelSearchable';
import VendorCreditMeta from './VendorCredit.Meta';
import { DiscountType } from '@/interfaces';
export default class VendorCredit extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
public amount: number;
public exchangeRate: number;
public openedAt: Date;
public discount: number;
public discountType: DiscountType;
public adjustment: number;
/**
* Table name
*/
static get tableName() {
return 'vendor_credits';
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return ['localAmount'];
}
/**
* Vendor credit amount in local currency.
* @returns {number}
@@ -34,6 +34,72 @@ export default class VendorCredit extends mixin(TenantModel, [
return this.amount * this.exchangeRate;
}
/**
* Vendor credit subtotal.
* @returns {number}
*/
get subtotal() {
return this.amount;
}
/**
* Vendor credit subtotal in local currency.
* @returns {number}
*/
get subtotalLocal() {
return this.subtotal * this.exchangeRate;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount amount in local currency.
* @returns {number | null}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Adjustment amount in local currency.
* @returns {number | null}
*/
get adjustmentLocal() {
return this.adjustment ? this.adjustment * this.exchangeRate : null;
}
/**
* Vendor credit total.
* @returns {number}
*/
get total() {
return this.subtotal - this.discountAmount + this.adjustment;
}
/**
* Vendor credit total in local currency.
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/**
* Model modifiers.
*/
@@ -120,7 +186,24 @@ export default class VendorCredit extends mixin(TenantModel, [
* Virtual attributes.
*/
static get virtualAttributes() {
return ['isDraft', 'isPublished', 'isOpen', 'isClosed', 'creditsRemaining'];
return [
'isDraft',
'isPublished',
'isOpen',
'isClosed',
'creditsRemaining',
'localAmount',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'adjustmentLocal',
'total',
'totalLocal',
];
}
/**