|
@@ -113,7 +113,7 @@ export class OrderService {
|
|
|
.addOrderBy('items.createdAt', 'ASC')
|
|
.addOrderBy('items.createdAt', 'ASC')
|
|
|
.getOne();
|
|
.getOne();
|
|
|
if (order) {
|
|
if (order) {
|
|
|
- order.lines.forEach(line => {
|
|
|
|
|
|
|
+ order.lines.forEach((line) => {
|
|
|
line.productVariant = translateDeep(
|
|
line.productVariant = translateDeep(
|
|
|
this.productVariantService.applyChannelPriceAndTax(line.productVariant, ctx),
|
|
this.productVariantService.applyChannelPriceAndTax(line.productVariant, ctx),
|
|
|
ctx.languageCode,
|
|
ctx.languageCode,
|
|
@@ -145,8 +145,8 @@ export class OrderService {
|
|
|
.andWhere('order.customer.id = :customerId', { customerId })
|
|
.andWhere('order.customer.id = :customerId', { customerId })
|
|
|
.getManyAndCount()
|
|
.getManyAndCount()
|
|
|
.then(([items, totalItems]) => {
|
|
.then(([items, totalItems]) => {
|
|
|
- items.forEach(item => {
|
|
|
|
|
- item.lines.forEach(line => {
|
|
|
|
|
|
|
+ items.forEach((item) => {
|
|
|
|
|
+ item.lines.forEach((line) => {
|
|
|
line.productVariant = translateDeep(line.productVariant, ctx.languageCode, [
|
|
line.productVariant = translateDeep(line.productVariant, ctx.languageCode, [
|
|
|
'options',
|
|
'options',
|
|
|
]);
|
|
]);
|
|
@@ -231,7 +231,7 @@ export class OrderService {
|
|
|
this.assertAddingItemsState(order);
|
|
this.assertAddingItemsState(order);
|
|
|
this.assertNotOverOrderItemsLimit(order, quantity);
|
|
this.assertNotOverOrderItemsLimit(order, quantity);
|
|
|
const productVariant = await this.getProductVariantOrThrow(ctx, productVariantId);
|
|
const productVariant = await this.getProductVariantOrThrow(ctx, productVariantId);
|
|
|
- let orderLine = order.lines.find(line => {
|
|
|
|
|
|
|
+ let orderLine = order.lines.find((line) => {
|
|
|
return (
|
|
return (
|
|
|
idsAreEqual(line.productVariant.id, productVariantId) &&
|
|
idsAreEqual(line.productVariant.id, productVariantId) &&
|
|
|
JSON.stringify(line.customFields) === JSON.stringify(customFields)
|
|
JSON.stringify(line.customFields) === JSON.stringify(customFields)
|
|
@@ -254,11 +254,15 @@ export class OrderService {
|
|
|
quantity?: number | null,
|
|
quantity?: number | null,
|
|
|
customFields?: { [key: string]: any },
|
|
customFields?: { [key: string]: any },
|
|
|
): Promise<Order> {
|
|
): Promise<Order> {
|
|
|
|
|
+ const { priceCalculationStrategy } = this.configService.orderOptions;
|
|
|
const order =
|
|
const order =
|
|
|
orderIdOrOrder instanceof Order
|
|
orderIdOrOrder instanceof Order
|
|
|
? orderIdOrOrder
|
|
? orderIdOrOrder
|
|
|
: await this.getOrderOrThrow(ctx, orderIdOrOrder);
|
|
: await this.getOrderOrThrow(ctx, orderIdOrOrder);
|
|
|
const orderLine = this.getOrderLineOrThrow(order, orderLineId);
|
|
const orderLine = this.getOrderLineOrThrow(order, orderLineId);
|
|
|
|
|
+ if (customFields != null) {
|
|
|
|
|
+ orderLine.customFields = customFields;
|
|
|
|
|
+ }
|
|
|
this.assertAddingItemsState(order);
|
|
this.assertAddingItemsState(order);
|
|
|
if (quantity != null) {
|
|
if (quantity != null) {
|
|
|
this.assertQuantityIsPositive(quantity);
|
|
this.assertQuantityIsPositive(quantity);
|
|
@@ -269,12 +273,16 @@ export class OrderService {
|
|
|
orderLine.items = [];
|
|
orderLine.items = [];
|
|
|
}
|
|
}
|
|
|
const productVariant = orderLine.productVariant;
|
|
const productVariant = orderLine.productVariant;
|
|
|
|
|
+ const calculatedPrice = await priceCalculationStrategy.calculateUnitPrice(
|
|
|
|
|
+ productVariant,
|
|
|
|
|
+ orderLine.customFields || {},
|
|
|
|
|
+ );
|
|
|
for (let i = currentQuantity; i < quantity; i++) {
|
|
for (let i = currentQuantity; i < quantity; i++) {
|
|
|
const orderItem = await this.connection.getRepository(OrderItem).save(
|
|
const orderItem = await this.connection.getRepository(OrderItem).save(
|
|
|
new OrderItem({
|
|
new OrderItem({
|
|
|
- unitPrice: productVariant.price,
|
|
|
|
|
|
|
+ unitPrice: calculatedPrice.price,
|
|
|
pendingAdjustments: [],
|
|
pendingAdjustments: [],
|
|
|
- unitPriceIncludesTax: productVariant.priceIncludesTax,
|
|
|
|
|
|
|
+ unitPriceIncludesTax: calculatedPrice.priceIncludesTax,
|
|
|
taxRate: productVariant.priceIncludesTax
|
|
taxRate: productVariant.priceIncludesTax
|
|
|
? productVariant.taxRateApplied.value
|
|
? productVariant.taxRateApplied.value
|
|
|
: 0,
|
|
: 0,
|
|
@@ -286,9 +294,6 @@ export class OrderService {
|
|
|
orderLine.items = orderLine.items.slice(0, quantity);
|
|
orderLine.items = orderLine.items.slice(0, quantity);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- if (customFields != null) {
|
|
|
|
|
- orderLine.customFields = customFields;
|
|
|
|
|
- }
|
|
|
|
|
await this.connection.getRepository(OrderLine).save(orderLine, { reload: false });
|
|
await this.connection.getRepository(OrderLine).save(orderLine, { reload: false });
|
|
|
return this.applyPriceAdjustments(ctx, order, orderLine);
|
|
return this.applyPriceAdjustments(ctx, order, orderLine);
|
|
|
}
|
|
}
|
|
@@ -297,7 +302,7 @@ export class OrderService {
|
|
|
const order = await this.getOrderOrThrow(ctx, orderId);
|
|
const order = await this.getOrderOrThrow(ctx, orderId);
|
|
|
this.assertAddingItemsState(order);
|
|
this.assertAddingItemsState(order);
|
|
|
const orderLine = this.getOrderLineOrThrow(order, orderLineId);
|
|
const orderLine = this.getOrderLineOrThrow(order, orderLineId);
|
|
|
- order.lines = order.lines.filter(line => !idsAreEqual(line.id, orderLineId));
|
|
|
|
|
|
|
+ order.lines = order.lines.filter((line) => !idsAreEqual(line.id, orderLineId));
|
|
|
const updatedOrder = await this.applyPriceAdjustments(ctx, order);
|
|
const updatedOrder = await this.applyPriceAdjustments(ctx, order);
|
|
|
await this.connection.getRepository(OrderLine).remove(orderLine);
|
|
await this.connection.getRepository(OrderLine).remove(orderLine);
|
|
|
return updatedOrder;
|
|
return updatedOrder;
|
|
@@ -325,7 +330,7 @@ export class OrderService {
|
|
|
async removeCouponCode(ctx: RequestContext, orderId: ID, couponCode: string) {
|
|
async removeCouponCode(ctx: RequestContext, orderId: ID, couponCode: string) {
|
|
|
const order = await this.getOrderOrThrow(ctx, orderId);
|
|
const order = await this.getOrderOrThrow(ctx, orderId);
|
|
|
if (order.couponCodes.includes(couponCode)) {
|
|
if (order.couponCodes.includes(couponCode)) {
|
|
|
- order.couponCodes = order.couponCodes.filter(cc => cc !== couponCode);
|
|
|
|
|
|
|
+ order.couponCodes = order.couponCodes.filter((cc) => cc !== couponCode);
|
|
|
await this.historyService.createHistoryEntryForOrder({
|
|
await this.historyService.createHistoryEntryForOrder({
|
|
|
ctx,
|
|
ctx,
|
|
|
orderId: order.id,
|
|
orderId: order.id,
|
|
@@ -359,7 +364,7 @@ export class OrderService {
|
|
|
async getEligibleShippingMethods(ctx: RequestContext, orderId: ID): Promise<ShippingMethodQuote[]> {
|
|
async getEligibleShippingMethods(ctx: RequestContext, orderId: ID): Promise<ShippingMethodQuote[]> {
|
|
|
const order = await this.getOrderOrThrow(ctx, orderId);
|
|
const order = await this.getOrderOrThrow(ctx, orderId);
|
|
|
const eligibleMethods = await this.shippingCalculator.getEligibleShippingMethods(ctx, order);
|
|
const eligibleMethods = await this.shippingCalculator.getEligibleShippingMethods(ctx, order);
|
|
|
- return eligibleMethods.map(eligible => ({
|
|
|
|
|
|
|
+ return eligibleMethods.map((eligible) => ({
|
|
|
id: eligible.method.id as string,
|
|
id: eligible.method.id as string,
|
|
|
price: eligible.result.price,
|
|
price: eligible.result.price,
|
|
|
priceWithTax: eligible.result.priceWithTax,
|
|
priceWithTax: eligible.result.priceWithTax,
|
|
@@ -372,7 +377,7 @@ export class OrderService {
|
|
|
const order = await this.getOrderOrThrow(ctx, orderId);
|
|
const order = await this.getOrderOrThrow(ctx, orderId);
|
|
|
this.assertAddingItemsState(order);
|
|
this.assertAddingItemsState(order);
|
|
|
const eligibleMethods = await this.shippingCalculator.getEligibleShippingMethods(ctx, order);
|
|
const eligibleMethods = await this.shippingCalculator.getEligibleShippingMethods(ctx, order);
|
|
|
- const selectedMethod = eligibleMethods.find(m => idsAreEqual(m.method.id, shippingMethodId));
|
|
|
|
|
|
|
+ const selectedMethod = eligibleMethods.find((m) => idsAreEqual(m.method.id, shippingMethodId));
|
|
|
if (!selectedMethod) {
|
|
if (!selectedMethod) {
|
|
|
throw new UserInputError(`error.shipping-method-unavailable`);
|
|
throw new UserInputError(`error.shipping-method-unavailable`);
|
|
|
}
|
|
}
|
|
@@ -413,7 +418,7 @@ export class OrderService {
|
|
|
|
|
|
|
|
function totalIsCovered(state: PaymentState): boolean {
|
|
function totalIsCovered(state: PaymentState): boolean {
|
|
|
return (
|
|
return (
|
|
|
- order.payments.filter(p => p.state === state).reduce((sum, p) => sum + p.amount, 0) ===
|
|
|
|
|
|
|
+ order.payments.filter((p) => p.state === state).reduce((sum, p) => sum + p.amount, 0) ===
|
|
|
order.total
|
|
order.total
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
@@ -456,7 +461,7 @@ export class OrderService {
|
|
|
}
|
|
}
|
|
|
const { items, orders } = await this.getOrdersAndItemsFromLines(
|
|
const { items, orders } = await this.getOrdersAndItemsFromLines(
|
|
|
input.lines,
|
|
input.lines,
|
|
|
- i => !i.fulfillment,
|
|
|
|
|
|
|
+ (i) => !i.fulfillment,
|
|
|
'error.create-fulfillment-items-already-fulfilled',
|
|
'error.create-fulfillment-items-already-fulfilled',
|
|
|
);
|
|
);
|
|
|
|
|
|
|
@@ -491,8 +496,8 @@ export class OrderService {
|
|
|
}
|
|
}
|
|
|
const allOrderItemsFulfilled = orderWithFulfillments.lines
|
|
const allOrderItemsFulfilled = orderWithFulfillments.lines
|
|
|
.reduce((orderItems, line) => [...orderItems, ...line.items], [] as OrderItem[])
|
|
.reduce((orderItems, line) => [...orderItems, ...line.items], [] as OrderItem[])
|
|
|
- .filter(orderItem => !orderItem.cancelled)
|
|
|
|
|
- .every(orderItem => {
|
|
|
|
|
|
|
+ .filter((orderItem) => !orderItem.cancelled)
|
|
|
|
|
+ .every((orderItem) => {
|
|
|
return !!orderItem.fulfillment;
|
|
return !!orderItem.fulfillment;
|
|
|
});
|
|
});
|
|
|
if (allOrderItemsFulfilled) {
|
|
if (allOrderItemsFulfilled) {
|
|
@@ -522,7 +527,7 @@ export class OrderService {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
const items = lines.reduce((acc, l) => [...acc, ...l.items], [] as OrderItem[]);
|
|
const items = lines.reduce((acc, l) => [...acc, ...l.items], [] as OrderItem[]);
|
|
|
- return unique(items.map(i => i.fulfillment).filter(notNullOrUndefined), 'id');
|
|
|
|
|
|
|
+ return unique(items.map((i) => i.fulfillment).filter(notNullOrUndefined), 'id');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async getFulfillmentOrderItems(id: ID): Promise<OrderItem[]> {
|
|
async getFulfillmentOrderItems(id: ID): Promise<OrderItem[]> {
|
|
@@ -550,7 +555,7 @@ export class OrderService {
|
|
|
if (order.state === 'AddingItems' || order.state === 'ArrangingPayment') {
|
|
if (order.state === 'AddingItems' || order.state === 'ArrangingPayment') {
|
|
|
return true;
|
|
return true;
|
|
|
} else {
|
|
} else {
|
|
|
- const lines: OrderLineInput[] = order.lines.map(l => ({
|
|
|
|
|
|
|
+ const lines: OrderLineInput[] = order.lines.map((l) => ({
|
|
|
orderLineId: l.id as string,
|
|
orderLineId: l.id as string,
|
|
|
quantity: l.quantity,
|
|
quantity: l.quantity,
|
|
|
}));
|
|
}));
|
|
@@ -568,7 +573,7 @@ export class OrderService {
|
|
|
}
|
|
}
|
|
|
const { items, orders } = await this.getOrdersAndItemsFromLines(
|
|
const { items, orders } = await this.getOrdersAndItemsFromLines(
|
|
|
lines,
|
|
lines,
|
|
|
- i => !i.cancelled,
|
|
|
|
|
|
|
+ (i) => !i.cancelled,
|
|
|
'error.cancel-order-lines-quantity-too-high',
|
|
'error.cancel-order-lines-quantity-too-high',
|
|
|
);
|
|
);
|
|
|
if (1 < orders.length) {
|
|
if (1 < orders.length) {
|
|
@@ -586,7 +591,7 @@ export class OrderService {
|
|
|
|
|
|
|
|
// Perform the cancellation
|
|
// Perform the cancellation
|
|
|
await this.stockMovementService.createCancellationsForOrderItems(items);
|
|
await this.stockMovementService.createCancellationsForOrderItems(items);
|
|
|
- items.forEach(i => (i.cancelled = true));
|
|
|
|
|
|
|
+ items.forEach((i) => (i.cancelled = true));
|
|
|
await this.connection.getRepository(OrderItem).save(items, { reload: false });
|
|
await this.connection.getRepository(OrderItem).save(items, { reload: false });
|
|
|
|
|
|
|
|
const orderWithItems = await this.connection.getRepository(Order).findOne(order.id, {
|
|
const orderWithItems = await this.connection.getRepository(Order).findOne(order.id, {
|
|
@@ -600,13 +605,13 @@ export class OrderService {
|
|
|
orderId: order.id,
|
|
orderId: order.id,
|
|
|
type: HistoryEntryType.ORDER_CANCELLATION,
|
|
type: HistoryEntryType.ORDER_CANCELLATION,
|
|
|
data: {
|
|
data: {
|
|
|
- orderItemIds: items.map(i => i.id),
|
|
|
|
|
|
|
+ orderItemIds: items.map((i) => i.id),
|
|
|
reason: input.reason || undefined,
|
|
reason: input.reason || undefined,
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
|
const allOrderItemsCancelled = orderWithItems.lines
|
|
const allOrderItemsCancelled = orderWithItems.lines
|
|
|
.reduce((orderItems, line) => [...orderItems, ...line.items], [] as OrderItem[])
|
|
.reduce((orderItems, line) => [...orderItems, ...line.items], [] as OrderItem[])
|
|
|
- .every(orderItem => orderItem.cancelled);
|
|
|
|
|
|
|
+ .every((orderItem) => orderItem.cancelled);
|
|
|
return allOrderItemsCancelled;
|
|
return allOrderItemsCancelled;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -621,7 +626,7 @@ export class OrderService {
|
|
|
}
|
|
}
|
|
|
const { items, orders } = await this.getOrdersAndItemsFromLines(
|
|
const { items, orders } = await this.getOrdersAndItemsFromLines(
|
|
|
input.lines,
|
|
input.lines,
|
|
|
- i => !i.cancelled,
|
|
|
|
|
|
|
+ (i) => !i.cancelled,
|
|
|
'error.refund-order-lines-quantity-too-high',
|
|
'error.refund-order-lines-quantity-too-high',
|
|
|
);
|
|
);
|
|
|
if (1 < orders.length) {
|
|
if (1 < orders.length) {
|
|
@@ -643,7 +648,7 @@ export class OrderService {
|
|
|
state: order.state,
|
|
state: order.state,
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
- if (items.some(i => !!i.refundId)) {
|
|
|
|
|
|
|
+ if (items.some((i) => !!i.refundId)) {
|
|
|
throw new IllegalOperationError('error.refund-order-item-already-refunded');
|
|
throw new IllegalOperationError('error.refund-order-item-already-refunded');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -680,7 +685,7 @@ export class OrderService {
|
|
|
try {
|
|
try {
|
|
|
await this.promotionService.validateCouponCode(couponCode, customer.id);
|
|
await this.promotionService.validateCouponCode(couponCode, customer.id);
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
- order.couponCodes = order.couponCodes.filter(c => c !== couponCode);
|
|
|
|
|
|
|
+ order.couponCodes = order.couponCodes.filter((c) => c !== couponCode);
|
|
|
codesRemoved = true;
|
|
codesRemoved = true;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -761,7 +766,7 @@ export class OrderService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private getOrderLineOrThrow(order: Order, orderLineId: ID): OrderLine {
|
|
private getOrderLineOrThrow(order: Order, orderLineId: ID): OrderLine {
|
|
|
- const orderItem = order.lines.find(line => idsAreEqual(line.id, orderLineId));
|
|
|
|
|
|
|
+ const orderItem = order.lines.find((line) => idsAreEqual(line.id, orderLineId));
|
|
|
if (!orderItem) {
|
|
if (!orderItem) {
|
|
|
throw new UserInputError(`error.order-does-not-contain-line-with-id`, { id: orderLineId });
|
|
throw new UserInputError(`error.order-does-not-contain-line-with-id`, { id: orderLineId });
|
|
|
}
|
|
}
|
|
@@ -841,14 +846,15 @@ export class OrderService {
|
|
|
const orders = new Map<ID, Order>();
|
|
const orders = new Map<ID, Order>();
|
|
|
const items = new Map<ID, OrderItem>();
|
|
const items = new Map<ID, OrderItem>();
|
|
|
|
|
|
|
|
- const lines = await this.connection
|
|
|
|
|
- .getRepository(OrderLine)
|
|
|
|
|
- .findByIds(orderLinesInput.map(l => l.orderLineId), {
|
|
|
|
|
|
|
+ const lines = await this.connection.getRepository(OrderLine).findByIds(
|
|
|
|
|
+ orderLinesInput.map((l) => l.orderLineId),
|
|
|
|
|
+ {
|
|
|
relations: ['order', 'items', 'items.fulfillment'],
|
|
relations: ['order', 'items', 'items.fulfillment'],
|
|
|
order: { id: 'ASC' },
|
|
order: { id: 'ASC' },
|
|
|
- });
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
for (const line of lines) {
|
|
for (const line of lines) {
|
|
|
- const inputLine = orderLinesInput.find(l => idsAreEqual(l.orderLineId, line.id));
|
|
|
|
|
|
|
+ const inputLine = orderLinesInput.find((l) => idsAreEqual(l.orderLineId, line.id));
|
|
|
if (!inputLine) {
|
|
if (!inputLine) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
@@ -860,7 +866,7 @@ export class OrderService {
|
|
|
if (matchingItems.length < inputLine.quantity) {
|
|
if (matchingItems.length < inputLine.quantity) {
|
|
|
throw new IllegalOperationError(noMatchesError);
|
|
throw new IllegalOperationError(noMatchesError);
|
|
|
}
|
|
}
|
|
|
- matchingItems.slice(0, inputLine.quantity).forEach(item => {
|
|
|
|
|
|
|
+ matchingItems.slice(0, inputLine.quantity).forEach((item) => {
|
|
|
items.set(item.id, item);
|
|
items.set(item.id, item);
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|