Skip to content

Commit

Permalink
Merge branch 'master' into feat/product-variant-price-cf-ui-new
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbromley authored Jan 21, 2025
2 parents 72d448d + 1bddbcc commit 2cb2af4
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 4 deletions.
10 changes: 10 additions & 0 deletions packages/core/e2e/custom-fields.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,16 @@ const customConfig = mergeConfig(testConfig(), {
{
name: 'costPrice',
type: 'int',
}
],
// Single readonly Address custom field to test
// https://github.com/vendure-ecommerce/vendure/issues/3326
Address: [
{
name: 'hereId',
type: 'string',
readonly: true,
nullable: true,
},
],
} as CustomFields,
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/api/config/graphql-custom-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ export function addGraphQLCustomFields(
const publicAddressFields = customFieldConfig.Address?.filter(
config => !config.internal && (publicOnly === true ? config.public !== false : true),
);
const writeablePublicAddressFields = publicAddressFields?.filter(field => !field.readonly);
if (publicAddressFields?.length) {
// For custom fields on the Address entity, we also extend the OrderAddress
// type (which is used to store address snapshots on Orders)
Expand All @@ -257,7 +258,7 @@ export function addGraphQLCustomFields(
}
`;
}
if (schema.getType('UpdateOrderAddressInput')) {
if (schema.getType('UpdateOrderAddressInput') && writeablePublicAddressFields?.length) {
customFieldTypeDefs += `
extend input UpdateOrderAddressInput {
customFields: UpdateAddressCustomFieldsInput
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,71 @@ describe('OrderCalculator', () => {
expect(order.subTotalWithTax).toBe(5719);
});
});

it(`clear previous promotion state before testing`, async () => {
const noLineDiscountsCondition = new PromotionCondition({
args: {},
code: 'no_other_discounts_condition',
description: [{ languageCode: LanguageCode.en, value: '' }],
check(_ctx, _order) {
const linesToDiscount = _order.lines
.filter(line => !line.adjustments.length)
.map(line => line.id);
return linesToDiscount.length ? { lines: linesToDiscount } : false;
},
});
const discountMatchedLinesAction = new PromotionItemAction({
code: 'discount_matched_lines_action',
conditions: [noLineDiscountsCondition],
description: [{ languageCode: LanguageCode.en, value: '' }],
args: { discount: { type: 'int' } },
execute(_ctx, orderLine, args, state) {
if (state.no_other_discounts_condition.lines.includes(orderLine.id)) {
return -args.discount;
}
return 0;
},
});

const discountAllUndiscountedItems = new Promotion({
id: 1,
name: 'Discount all undiscounted items',
conditions: [
{
code: noLineDiscountsCondition.code,
args: [],
},
],
promotionConditions: [noLineDiscountsCondition],
actions: [
{
code: discountMatchedLinesAction.code,
args: [{ name: 'discount', value: '50' }],
},
],
promotionActions: [discountMatchedLinesAction],
});

const ctx = createRequestContext({ pricesIncludeTax: false });
const order = createOrder({
ctx,
lines: [
{
listPrice: 500,
taxCategory: taxCategoryStandard,
quantity: 2,
},
],
});

await orderCalculator.applyPriceAdjustments(ctx, order, [discountAllUndiscountedItems]);
// everything gets discounted by 50
expect(order.subTotal).toBe(900);
// should still be discounted after changing quantity
order.lines[0].quantity = 3;
await orderCalculator.applyPriceAdjustments(ctx, order, [discountAllUndiscountedItems]);
expect(order.subTotal).toBe(1350);
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ export class OrderCalculator {
for (const line of order.lines) {
// Must be re-calculated for each line, since the previous lines may have triggered promotions
// which affected the order price.
const applicablePromotions = await filterAsync(promotions, p => p.test(ctx, order).then(Boolean));
line.clearAdjustments();
const applicablePromotions = await filterAsync(promotions, p => p.test(ctx, order).then(Boolean));

for (const promotion of applicablePromotions) {
let priceAdjusted = false;
Expand Down
4 changes: 4 additions & 0 deletions packages/payments-plugin/e2e/stripe-payment.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ describe('Stripe payments', () => {
'metadata[channelToken]': E2E_DEFAULT_CHANNEL_TOKEN,
'metadata[orderId]': '1',
'metadata[orderCode]': activeOrder?.code,
'metadata[languageCode]': 'en',
});
expect(createStripePaymentIntent).toEqual('test-client-secret');
});
Expand Down Expand Up @@ -207,6 +208,7 @@ describe('Stripe payments', () => {
'metadata[channelToken]': E2E_DEFAULT_CHANNEL_TOKEN,
'metadata[orderId]': '1',
'metadata[orderCode]': activeOrder?.code,
'metadata[languageCode]': 'en',
'metadata[customerEmail]': customers[0].emailAddress,
});
expect(createStripePaymentIntent).toEqual('test-client-secret');
Expand Down Expand Up @@ -240,6 +242,7 @@ describe('Stripe payments', () => {
description: `Order #${activeOrder!.code} for ${activeOrder!.customer!.emailAddress}`,
'automatic_payment_methods[enabled]': 'true',
'metadata[channelToken]': E2E_DEFAULT_CHANNEL_TOKEN,
'metadata[languageCode]': 'en',
'metadata[orderId]': '1',
'metadata[orderCode]': activeOrder?.code,
});
Expand Down Expand Up @@ -280,6 +283,7 @@ describe('Stripe payments', () => {
'automatic_payment_methods[enabled]': 'true',
'metadata[channelToken]': E2E_DEFAULT_CHANNEL_TOKEN,
'metadata[orderId]': '1',
'metadata[languageCode]': 'en',
'metadata[orderCode]': activeOrder?.code,
});
expect(connectedAccountHeader).toEqual('acct_connected');
Expand Down
4 changes: 2 additions & 2 deletions packages/payments-plugin/src/braintree/braintree.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ async function processPayment(
},
});
const extractMetadataFn = pluginOptions.extractMetadata ?? defaultExtractMetadataFn;
const metadata = extractMetadataFn(response.transaction);
const metadata = response.transaction && extractMetadataFn(response.transaction);
if (!response.success) {
return {
amount,
state: 'Declined' as const,
transactionId: response.transaction.id,
transactionId: response.transaction?.id,
errorMessage: response.message,
metadata,
};
Expand Down

0 comments on commit 2cb2af4

Please sign in to comment.