This commit is contained in:
@@ -8,6 +8,9 @@ class PriceDetailDTO {
|
|||||||
@Expose()
|
@Expose()
|
||||||
shipping_cost: number;
|
shipping_cost: number;
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
shipping_cost_on_delivery: number;
|
||||||
|
|
||||||
@Expose()
|
@Expose()
|
||||||
process_cost: number;
|
process_cost: number;
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export interface ICartPayment {
|
|||||||
|
|
||||||
export interface IPriceDetails {
|
export interface IPriceDetails {
|
||||||
shipping_cost: number;
|
shipping_cost: number;
|
||||||
|
shipping_cost_on_delivery: number;
|
||||||
process_cost: number;
|
process_cost: number;
|
||||||
total_retail_price: number;
|
total_retail_price: number;
|
||||||
total_payable_price: number;
|
total_payable_price: number;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { PaymentStatus } from "../../../common/enums/order.enum";
|
|||||||
const PaymentPriceDetails = new Schema<IPriceDetails>(
|
const PaymentPriceDetails = new Schema<IPriceDetails>(
|
||||||
{
|
{
|
||||||
shipping_cost: { type: Number, required: true },
|
shipping_cost: { type: Number, required: true },
|
||||||
|
shipping_cost_on_delivery: { type: Number, default: 0 },
|
||||||
process_cost: { type: Number, required: true },
|
process_cost: { type: Number, required: true },
|
||||||
total_retail_price: { type: Number, required: true },
|
total_retail_price: { type: Number, required: true },
|
||||||
total_discount: { type: Number, default: 0 },
|
total_discount: { type: Number, default: 0 },
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { OrderService } from "../../order/order.service";
|
|||||||
import { PricingRepository } from "../../pricing/pricing.repository";
|
import { PricingRepository } from "../../pricing/pricing.repository";
|
||||||
import { ProductService } from "../../product/providers/product.service";
|
import { ProductService } from "../../product/providers/product.service";
|
||||||
import { ShopRepo } from "../../shop/shop.repository";
|
import { ShopRepo } from "../../shop/shop.repository";
|
||||||
|
import { ShipmentRepository } from "../../shipment/shipment.repository";
|
||||||
import { IUser } from "../../user/models/Abstraction/IUser";
|
import { IUser } from "../../user/models/Abstraction/IUser";
|
||||||
import { WalletService } from "../../wallet/wallet.service";
|
import { WalletService } from "../../wallet/wallet.service";
|
||||||
import { CartCheckoutDTO } from "../DTO/cartCheckout.dto";
|
import { CartCheckoutDTO } from "../DTO/cartCheckout.dto";
|
||||||
@@ -46,6 +47,7 @@ class PaymentService {
|
|||||||
@inject(IOCTYPES.WalletService) walletService: WalletService;
|
@inject(IOCTYPES.WalletService) walletService: WalletService;
|
||||||
@inject(IOCTYPES.NotificationService) notificationService: NotificationService;
|
@inject(IOCTYPES.NotificationService) notificationService: NotificationService;
|
||||||
@inject(IOCTYPES.ShopRepo) shopRepo: ShopRepo;
|
@inject(IOCTYPES.ShopRepo) shopRepo: ShopRepo;
|
||||||
|
@inject(IOCTYPES.ShipmentRepository) shipmentRepo: ShipmentRepository;
|
||||||
|
|
||||||
//##################################################################
|
//##################################################################
|
||||||
//##################################################################
|
//##################################################################
|
||||||
@@ -98,7 +100,7 @@ class PaymentService {
|
|||||||
|
|
||||||
if (!paymentMethods) throw new BadRequestError(PaymentMessage.PaymentMethodsNotFound);
|
if (!paymentMethods) throw new BadRequestError(PaymentMessage.PaymentMethodsNotFound);
|
||||||
|
|
||||||
const { price } = this.calculateTotalPrice(cart, cartShipmentItems);
|
const { price } = await this.calculateTotalPrice(cart, cartShipmentItems);
|
||||||
return {
|
return {
|
||||||
cart,
|
cart,
|
||||||
cartShipmentItems,
|
cartShipmentItems,
|
||||||
@@ -135,7 +137,7 @@ class PaymentService {
|
|||||||
const paymentMethod = await this.paymentMethodRepo.findById(cartCheckoutDto.payment_method_id);
|
const paymentMethod = await this.paymentMethodRepo.findById(cartCheckoutDto.payment_method_id);
|
||||||
if (!paymentMethod) throw new BadRequestError(PaymentMessage.PaymentMethodNotFound);
|
if (!paymentMethod) throw new BadRequestError(PaymentMessage.PaymentMethodNotFound);
|
||||||
|
|
||||||
const { price, description } = this.calculateTotalPrice(cart, cartShipmentItems);
|
const { price, description } = await this.calculateTotalPrice(cart, cartShipmentItems);
|
||||||
|
|
||||||
// extracted to handle payment creation
|
// extracted to handle payment creation
|
||||||
const { payment, paymentData } = await this.handlePaymentCreation(paymentMethod, price, user, description, session);
|
const { payment, paymentData } = await this.handlePaymentCreation(paymentMethod, price, user, description, session);
|
||||||
@@ -238,26 +240,33 @@ class PaymentService {
|
|||||||
//##################################################################
|
//##################################################################
|
||||||
//##################################################################
|
//##################################################################
|
||||||
// Helper method to calculate the total price of cart items
|
// Helper method to calculate the total price of cart items
|
||||||
private calculateTotalPrice(cart: CartDTO, cartShipItem: ICartShipmentItem[]) {
|
private async calculateTotalPrice(cart: CartDTO, cartShipItem: ICartShipmentItem[]) {
|
||||||
const totalShippingCost = cartShipItem.reduce(
|
const totalShippingCost = cartShipItem.reduce(
|
||||||
(sum, item) => sum + item.shipmentItems.reduce((subSum, shipmentItem) => subSum + shipmentItem.shipmentCost, 0),
|
(sum, item) => sum + item.shipmentItems.reduce((subSum, shipmentItem) => subSum + shipmentItem.shipmentCost, 0),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
// const totalCouponDiscount = cartShipItem.reduce((sum, item) => sum + item.totalCouponDiscount, 0);
|
const shipperIds = [...new Set(cartShipItem.map((item) => item.shipper))];
|
||||||
// const totalShippingCost = cartShipItem.reduce((sum, item) => sum + item.shipmentCost, 0);
|
const shippers = await this.shipmentRepo.model.find({ _id: { $in: shipperIds } }).select("_id payDeliveryFeeOnDelivery").lean();
|
||||||
|
const payOnDeliveryShipperIds = new Set(
|
||||||
|
shippers.filter((shipper) => shipper.payDeliveryFeeOnDelivery).map((shipper) => shipper._id),
|
||||||
|
);
|
||||||
|
|
||||||
|
const onlineShippingCost = cartShipItem.reduce((sum, item) => {
|
||||||
|
const itemShippingCost = item.shipmentItems.reduce((subSum, shipmentItem) => subSum + shipmentItem.shipmentCost, 0);
|
||||||
|
return sum + (payOnDeliveryShipperIds.has(item.shipper) ? 0 : itemShippingCost);
|
||||||
|
}, 0);
|
||||||
|
const shippingCostOnDelivery = totalShippingCost - onlineShippingCost;
|
||||||
|
|
||||||
const price = {
|
const price = {
|
||||||
shipping_cost: totalShippingCost,
|
shipping_cost: onlineShippingCost,
|
||||||
// process_cost: totalShippingCost,
|
shipping_cost_on_delivery: shippingCostOnDelivery,
|
||||||
process_cost: 0,
|
process_cost: 0,
|
||||||
cart_retail_price: cart.retail_price,
|
cart_retail_price: cart.retail_price,
|
||||||
// cart_payable_price: cart.payable_price - cart.coupon_discount,
|
|
||||||
cart_payable_price: cart.payable_price,
|
cart_payable_price: cart.payable_price,
|
||||||
total_discount: cart.total_discount,
|
total_discount: cart.total_discount,
|
||||||
coupon_discount: cart.coupon_discount,
|
coupon_discount: cart.coupon_discount,
|
||||||
// total_payable_price: cart.payable_price + totalShippingCost * 2,
|
total_payable_price: cart.isWholeSale ? cart.retail_price + onlineShippingCost : cart.payable_price + onlineShippingCost,
|
||||||
total_payable_price: cart.isWholeSale ? cart.retail_price + totalShippingCost : cart.payable_price + totalShippingCost,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const items = cart.items;
|
const items = cart.items;
|
||||||
@@ -357,6 +366,7 @@ class PaymentService {
|
|||||||
|
|
||||||
const priceDetails: Partial<IPriceDetails> = {
|
const priceDetails: Partial<IPriceDetails> = {
|
||||||
shipping_cost: price.shipping_cost,
|
shipping_cost: price.shipping_cost,
|
||||||
|
shipping_cost_on_delivery: price.shipping_cost_on_delivery,
|
||||||
process_cost: price.process_cost,
|
process_cost: price.process_cost,
|
||||||
total_retail_price: price.cart_retail_price,
|
total_retail_price: price.cart_retail_price,
|
||||||
total_discount: price.total_discount,
|
total_discount: price.total_discount,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Expose, Type } from "class-transformer";
|
import { Expose, Type } from "class-transformer";
|
||||||
import { ArrayMinSize, IsArray, IsNotEmpty, IsNumber, IsString, ValidateNested } from "class-validator";
|
import { ArrayMinSize, IsArray, IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString, ValidateNested } from "class-validator";
|
||||||
|
|
||||||
import { ApiProperty } from "../../../common/decorator/swggerDocs";
|
import { ApiProperty } from "../../../common/decorator/swggerDocs";
|
||||||
|
|
||||||
@@ -71,4 +71,14 @@ export class CreateShipProvidersDTO {
|
|||||||
@IsNumber()
|
@IsNumber()
|
||||||
@ApiProperty({ type: "number", description: "Delivery time in days", example: 3 })
|
@ApiProperty({ type: "number", description: "Delivery time in days", example: 3 })
|
||||||
deliveryTime: number;
|
deliveryTime: number;
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
@IsOptional()
|
||||||
|
@IsBoolean()
|
||||||
|
@ApiProperty({
|
||||||
|
type: "boolean",
|
||||||
|
description: "If true, delivery fee must be paid at delivery time instead of checkout",
|
||||||
|
example: false,
|
||||||
|
})
|
||||||
|
payDeliveryFeeOnDelivery?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ class ShipperInfoDTO {
|
|||||||
@Expose()
|
@Expose()
|
||||||
totalShippingCost: number;
|
totalShippingCost: number;
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
payDeliveryFeeOnDelivery: boolean;
|
||||||
|
|
||||||
@Expose()
|
@Expose()
|
||||||
@Type(() => ShipperItemsDTO)
|
@Type(() => ShipperItemsDTO)
|
||||||
items: ShipperItemsDTO[];
|
items: ShipperItemsDTO[];
|
||||||
@@ -89,4 +92,7 @@ export class ShipmentMethodDTO {
|
|||||||
|
|
||||||
@Expose()
|
@Expose()
|
||||||
deliveryType: string;
|
deliveryType: string;
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
payDeliveryFeeOnDelivery: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export interface IShipmentProviders {
|
|||||||
costs: IShipmentCost[];
|
costs: IShipmentCost[];
|
||||||
deliveryType: DeliveryType;
|
deliveryType: DeliveryType;
|
||||||
deliveryTime: number;
|
deliveryTime: number;
|
||||||
|
payDeliveryFeeOnDelivery: boolean;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
}
|
}
|
||||||
export interface IShipmentCost {
|
export interface IShipmentCost {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const ShipmentSchema = new Schema<IShipmentProviders>(
|
|||||||
costs: [ShipmentCostSchema],
|
costs: [ShipmentCostSchema],
|
||||||
deliveryType: { type: String, enum: DeliveryType, required: true },
|
deliveryType: { type: String, enum: DeliveryType, required: true },
|
||||||
deliveryTime: { type: Number, required: true },
|
deliveryTime: { type: Number, required: true },
|
||||||
|
payDeliveryFeeOnDelivery: { type: Boolean, default: false },
|
||||||
deleted: { type: Boolean, default: false },
|
deleted: { type: Boolean, default: false },
|
||||||
},
|
},
|
||||||
{ timestamps: true, toJSON: { versionKey: false }, _id: false, id: false },
|
{ timestamps: true, toJSON: { versionKey: false }, _id: false, id: false },
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class ShipmentService {
|
|||||||
//###################################################################
|
//###################################################################
|
||||||
//###################################################################
|
//###################################################################
|
||||||
async createShipmentProviderS(createDto: CreateShipProvidersDTO) {
|
async createShipmentProviderS(createDto: CreateShipProvidersDTO) {
|
||||||
const existShipper = await this.shipmentRepo.model.exists({ name: createDto.name });
|
const existShipper = await this.shipmentRepo.model.exists({ name: createDto.name, deleted: false });
|
||||||
if (existShipper) throw new BadRequestError(CommonMessage.DuplicateName);
|
if (existShipper) throw new BadRequestError(CommonMessage.DuplicateName);
|
||||||
|
|
||||||
const deliveryType = this.getDeliveryType(createDto.deliveryTime);
|
const deliveryType = this.getDeliveryType(createDto.deliveryTime);
|
||||||
@@ -48,7 +48,7 @@ class ShipmentService {
|
|||||||
|
|
||||||
//###################################################################
|
//###################################################################
|
||||||
async updateShipmentProvider(shipmentId: number, updateDto: UpdateShipmentProviderDTO) {
|
async updateShipmentProvider(shipmentId: number, updateDto: UpdateShipmentProviderDTO) {
|
||||||
const existShipper = await this.shipmentRepo.model.exists({ name: updateDto.name, _id: { $ne: shipmentId } });
|
const existShipper = await this.shipmentRepo.model.exists({ name: updateDto.name, _id: { $ne: shipmentId }, deleted: false });
|
||||||
if (existShipper) throw new BadRequestError(CommonMessage.DuplicateName);
|
if (existShipper) throw new BadRequestError(CommonMessage.DuplicateName);
|
||||||
|
|
||||||
if (updateDto.deliveryTime) {
|
if (updateDto.deliveryTime) {
|
||||||
@@ -157,6 +157,7 @@ class ShipmentService {
|
|||||||
shipperName: shipmentProvider.name,
|
shipperName: shipmentProvider.name,
|
||||||
shippingDaysRange: this.calculateShippingDays(shipmentProvider.deliveryType, shipmentProvider.deliveryTime),
|
shippingDaysRange: this.calculateShippingDays(shipmentProvider.deliveryType, shipmentProvider.deliveryTime),
|
||||||
totalShippingCost,
|
totalShippingCost,
|
||||||
|
payDeliveryFeeOnDelivery: shipmentProvider.payDeliveryFeeOnDelivery ?? false,
|
||||||
items: shipperItems,
|
items: shipperItems,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -230,15 +231,25 @@ class ShipmentService {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
if (existingCartShipmentItem) {
|
if (existingCartShipmentItem) {
|
||||||
// update the existing CartShipmentItem
|
const totalSellingPrice = shipmentItems.reduce((acc, item) => acc + item.selling_price * item.quantity, 0);
|
||||||
|
const totalRetailPrice = shipmentItems.reduce((acc, item) => acc + item.retail_price * item.quantity, 0);
|
||||||
|
const totalShipmentCost = shipmentItems.reduce((acc, item) => acc + item.shipmentCost, 0);
|
||||||
|
const onlineShipmentCost = selectedShipper.payDeliveryFeeOnDelivery ? 0 : totalShipmentCost;
|
||||||
|
|
||||||
existingCartShipmentItem.shipmentItems = shipmentItems;
|
existingCartShipmentItem.shipmentItems = shipmentItems;
|
||||||
existingCartShipmentItem.shipper = selectedShipper.shipperId;
|
existingCartShipmentItem.shipper = selectedShipper.shipperId;
|
||||||
|
existingCartShipmentItem.totalSellingPrice = totalSellingPrice;
|
||||||
|
existingCartShipmentItem.totalRetailPrice = totalRetailPrice;
|
||||||
|
existingCartShipmentItem.totalShipmentCost = totalShipmentCost;
|
||||||
|
existingCartShipmentItem.totalPaymentPrice = totalSellingPrice + onlineShipmentCost;
|
||||||
|
existingCartShipmentItem.itemsCount = shipmentItems.reduce((acc, item) => acc + item.quantity, 0);
|
||||||
|
|
||||||
await existingCartShipmentItem.save();
|
await existingCartShipmentItem.save();
|
||||||
} else {
|
} else {
|
||||||
const totalSellingPrice = shipmentItems.reduce((acc, item) => acc + item.selling_price * item.quantity, 0);
|
const totalSellingPrice = shipmentItems.reduce((acc, item) => acc + item.selling_price * item.quantity, 0);
|
||||||
const totalRetailPrice = shipmentItems.reduce((acc, item) => acc + item.retail_price * item.quantity, 0);
|
const totalRetailPrice = shipmentItems.reduce((acc, item) => acc + item.retail_price * item.quantity, 0);
|
||||||
const totalShipmentCost = shipmentItems.reduce((acc, item) => acc + item.shipmentCost, 0);
|
const totalShipmentCost = shipmentItems.reduce((acc, item) => acc + item.shipmentCost, 0);
|
||||||
|
const onlineShipmentCost = selectedShipper.payDeliveryFeeOnDelivery ? 0 : totalShipmentCost;
|
||||||
|
|
||||||
// create a new CartShipmentItem
|
// create a new CartShipmentItem
|
||||||
await this.cartShipItemRepo.model.create({
|
await this.cartShipItemRepo.model.create({
|
||||||
@@ -249,7 +260,7 @@ class ShipmentService {
|
|||||||
totalSellingPrice,
|
totalSellingPrice,
|
||||||
totalRetailPrice,
|
totalRetailPrice,
|
||||||
totalShipmentCost,
|
totalShipmentCost,
|
||||||
totalPaymentPrice: totalShipmentCost + totalSellingPrice,
|
totalPaymentPrice: totalSellingPrice + onlineShipmentCost,
|
||||||
itemsCount: shipmentItems.reduce((acc, item) => acc + item.quantity, 0),
|
itemsCount: shipmentItems.reduce((acc, item) => acc + item.quantity, 0),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -261,12 +272,15 @@ class ShipmentService {
|
|||||||
(sum, item) => sum + item.shipmentItems.reduce((subSum, shipmentItem) => subSum + shipmentItem.shipmentCost, 0),
|
(sum, item) => sum + item.shipmentItems.reduce((subSum, shipmentItem) => subSum + shipmentItem.shipmentCost, 0),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
const onlineShippingCost = cartShipmentItems.reduce((sum, item) => sum + (item.totalPaymentPrice - item.totalSellingPrice), 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cartShipmentItems: cartShipmentItems,
|
cartShipmentItems: cartShipmentItems,
|
||||||
shippingCost: totalShippingCost,
|
shippingCost: totalShippingCost,
|
||||||
processCost: totalShippingCost,
|
onlineShippingCost,
|
||||||
payable_price: cart.payable_price + totalShippingCost * 2,
|
shippingCostOnDelivery: totalShippingCost - onlineShippingCost,
|
||||||
|
processCost: onlineShippingCost,
|
||||||
|
payable_price: cart.payable_price + onlineShippingCost,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user