diff --git a/apps/api/src/app/order/create-order.dto.ts b/apps/api/src/app/order/create-order.dto.ts
index aecec842a..0023eab41 100644
--- a/apps/api/src/app/order/create-order.dto.ts
+++ b/apps/api/src/app/order/create-order.dto.ts
@@ -15,10 +15,33 @@ import {
IsNumber,
IsOptional,
IsString,
- Min
+ Min,
+ ValidationArguments,
+ ValidationOptions,
+ registerDecorator
} from 'class-validator';
import { isString } from 'lodash';
+function IsQuantityValid(validationOptions?: ValidationOptions) {
+ return function (object: Object, propertyName: string) {
+ registerDecorator({
+ name: 'isQuantityValid',
+ target: object.constructor,
+ propertyName: propertyName,
+ options: validationOptions,
+ validator: {
+ validate(value: any, args: ValidationArguments) {
+ const order = args.object as CreateOrderDto;
+ return order.type !== 'SPLIT' ? value >= 0 : true;
+ },
+ defaultMessage(args: ValidationArguments) {
+ return `Quantity must not be less than 0 unless the type is ${Type.SPLIT}`;
+ }
+ }
+ });
+ };
+}
+
export class CreateOrderDto {
@IsOptional()
@IsString()
@@ -54,7 +77,7 @@ export class CreateOrderDto {
fee: number;
@IsNumber()
- @Min(0)
+ @IsQuantityValid()
quantity: number;
@IsString()
diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts
index 6ea93a670..441779289 100644
--- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts
+++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts
@@ -792,9 +792,12 @@ export class PortfolioCalculator {
if (oldAccumulatedSymbol) {
let investment = oldAccumulatedSymbol.investment;
- const newQuantity = quantity
- .mul(factor)
- .plus(oldAccumulatedSymbol.quantity);
+ const newQuantity =
+ type === 'SPLIT'
+ ? quantity.s === 1
+ ? oldAccumulatedSymbol.quantity.mul(quantity)
+ : oldAccumulatedSymbol.quantity.div(quantity.abs())
+ : quantity.mul(factor).plus(oldAccumulatedSymbol.quantity);
if (type === 'BUY') {
investment = oldAccumulatedSymbol.investment.plus(
diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts
index 8384427c3..7a15d898c 100644
--- a/apps/api/src/app/portfolio/portfolio.service.ts
+++ b/apps/api/src/app/portfolio/portfolio.service.ts
@@ -724,7 +724,9 @@ export class PortfolioService {
activities: orders.filter((order) => {
tags = tags.concat(order.tags);
- return ['BUY', 'DIVIDEND', 'ITEM', 'SELL'].includes(order.type);
+ return ['BUY', 'DIVIDEND', 'ITEM', 'SELL', 'SPLIT'].includes(
+ order.type
+ );
}),
currency: userCurrency,
currentRateService: this.currentRateService,
diff --git a/apps/api/src/helper/portfolio.helper.ts b/apps/api/src/helper/portfolio.helper.ts
index 730f34bde..d3d3856af 100644
--- a/apps/api/src/helper/portfolio.helper.ts
+++ b/apps/api/src/helper/portfolio.helper.ts
@@ -19,6 +19,7 @@ export function getFactor(activityType: ActivityType) {
switch (activityType) {
case 'BUY':
case 'ITEM':
+ case 'SPLIT':
factor = 1;
break;
case 'LIABILITY':
diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
index b628aba46..4cc405505 100644
--- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
+++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
@@ -279,7 +279,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
if (this.activityForm.controls['searchSymbol'].invalid) {
this.data.activity.SymbolProfile = null;
} else if (
- ['BUY', 'DIVIDEND', 'SELL'].includes(
+ ['BUY', 'DIVIDEND', 'SELL', 'SPLIT'].includes(
this.activityForm.controls['type'].value
)
) {
@@ -394,6 +394,50 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
this.activityForm.controls['updateAccountBalance'].disable();
this.activityForm.controls['updateAccountBalance'].setValue(false);
}
+ } else if (type === 'SPLIT') {
+ this.activityForm.controls['currency'].removeValidators(
+ Validators.required
+ );
+ this.activityForm.controls['currency'].updateValueAndValidity();
+
+ this.activityForm.controls['currencyOfFee'].removeValidators(
+ Validators.required
+ );
+ this.activityForm.controls['currencyOfFee'].updateValueAndValidity();
+
+ this.activityForm.controls['currencyOfUnitPrice'].removeValidators(
+ Validators.required
+ );
+ this.activityForm.controls[
+ 'currencyOfUnitPrice'
+ ].updateValueAndValidity();
+
+ this.activityForm.controls['fee'].removeValidators(
+ Validators.required
+ );
+ this.activityForm.controls['fee'].updateValueAndValidity();
+
+ this.activityForm.controls['feeInCustomCurrency'].removeValidators(
+ Validators.required
+ );
+ this.activityForm.controls[
+ 'feeInCustomCurrency'
+ ].updateValueAndValidity();
+
+ this.activityForm.controls['unitPrice'].removeValidators(
+ Validators.required
+ );
+ this.activityForm.controls['unitPrice'].updateValueAndValidity();
+
+ this.activityForm.controls[
+ 'unitPriceInCustomCurrency'
+ ].removeValidators(Validators.required);
+ this.activityForm.controls[
+ 'unitPriceInCustomCurrency'
+ ].updateValueAndValidity();
+
+ this.activityForm.controls['updateAccountBalance'].disable();
+ this.activityForm.controls['updateAccountBalance'].setValue(false);
} else {
this.activityForm.controls['accountId'].setValidators(
Validators.required
diff --git a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html
index 81c41a316..87ffc2e18 100644
--- a/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html
+++ b/apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html
@@ -70,6 +70,14 @@
>Luxury items, real estate, private companies
+