Skip to content

Commit

Permalink
Merge branch '4.3' into feature/purchasables-order-edit-event
Browse files Browse the repository at this point in the history
  • Loading branch information
lukeholder authored Jul 13, 2023
2 parents aaf924e + a2e4754 commit 174c17c
Show file tree
Hide file tree
Showing 30 changed files with 838 additions and 260 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@
- It’s now possible to modify the purchasables shown in the add line item table on the Edit Order page. ([#3194](https://github.com/craftcms/commerce/issues/3194))
- Added `craft\commerce\events\ModifyPurchasablesQueryEvent`.
- Added `craft\commerce\controllers\OrdersController::EVENT_MODIFY_PURCHASABLES_QUERY`.
- Guest customers registering during checkout now have their addresses saved to their account.

## Unreleased

- Fixed a bug where `hasMatchingAddresses()` was incorrectly returning `false`. ([#3183](https://github.com/craftcms/commerce/issues/3183))
- Fixed a bug where changing a user’s email would cause extra user elements to be created. ([#3138](https://github.com/craftcms/commerce/issues/3138))
- Fixed a bug where related sales were showing when creating a new product.
- Fixed a bug where related sales were showing when creating a new product.
- Fixed a bug where Commerce wasn’t invoking `craft\services\Elements::EVENT_AUTHORIZE_*` event handlers.
- Fixed a bug where discounts’ per user usage counters weren’t getting migrated properly when upgrading to Commerce 4.
- Fixed a bug where address changes weren’t being synced to carts using them as a source. ([#3178](https://github.com/craftcms/commerce/issues/3178))
- Added `craft\commerce\services\Orders::afterSaveAddressHandler()`.
- Added `craft\commerce\elements\Order::$orderCompletedEmail`. ([#3138](https://github.com/craftcms/commerce/issues/3138))
- Added the `commerce/cart/forget-cart` action. ([#3206](https://github.com/craftcms/commerce/issues/3206))

## 4.2.11 - 2023-06-05

Expand Down
26 changes: 26 additions & 0 deletions example-templates/dist/shop/_private/address/fields.twig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{#
Outputs address form fields for editing an address.
#}
{% set showPrimaryCheckboxes = showPrimaryCheckboxes is defined ? showPrimaryCheckboxes : false %}
{% set addressFieldLayout = craft.app.getAddresses().getLayout() %}
{% set addressCustomFields = addressFieldLayout.getCustomFields()|filter(f => className(f) == 'craft\\fields\\PlainText') %}
{# @var address \craft\elements\Address #}
Expand Down Expand Up @@ -193,9 +194,34 @@ Outputs address form fields for editing an address.
{% endfor %}
</div>
{% endif %}

{% if showPrimaryCheckboxes %}
<hr class="my-2">
<div class="my-2">
{{ input('text', 'isPrimaryBilling', address.isPrimarybilling ? 1 : 0) }}
<label>{{ input('checkbox', 'isPrimaryBillingBox', 1, { checked: address.isPrimaryBilling, 'data-primary-input': 'isPrimaryBilling' }) }} {{ 'Use as the primary billing address'|t('commerce') }}</label>
</div>
<div class="my-2">
{{ input('text', 'isPrimaryShipping', address.isPrimaryShipping ? 1 : 0) }}
<label>{{ input('checkbox', 'isPrimaryShippingBox', 1, { checked: address.isPrimaryShipping, 'data-primary-input': 'isPrimaryShipping' }) }} {{ 'Use as the primary shipping address'|t('commerce') }}</label>
</div>
{% endif %}
</div>

{% js %}
{% if showPrimaryCheckboxes %}
document.querySelectorAll('input[type=checkbox][data-primary-input]').forEach(el => {
el.addEventListener('change', ev => {
let primaryInput = document.querySelector(`input[name="${ev.target.dataset.primaryInput}"]`);
if (ev.target.checked) {
primaryInput.value = 1;
} else {
primaryInput.value = 0;
}
});
});
{% endif %}

document.querySelector('select#{{ 'countryCode'|namespaceInputId(addressName) }}').addEventListener('change', ev => {
const countryCode = ev.target.value;
const stateSelect = document.querySelector('select#{{ 'administrativeArea'|namespaceInputId(addressName) }}');
Expand Down
11 changes: 7 additions & 4 deletions example-templates/dist/shop/_private/address/fieldset.twig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Outputs a fieldset for selecting one of a user’s available addresses or creati
@var title string
@var currentUser \craft\elements\User
#}
{% set sourceAttribute = 'source' ~ (name|slice(0, 1)|capitalize) ~ (name|slice(1) ~ 'Id') %}
<div class="js-address-fieldset {{ classes }}">
<h2 class="text-lg font-bold mb-4">
{{- title -}}
Expand All @@ -19,22 +20,24 @@ Outputs a fieldset for selecting one of a user’s available addresses or creati
selectable: true,
primaryBillingAddressId: cart and cart.customer ? cart.customer.primaryBillingAddressId : null,
primaryShippingAddressId: cart and cart.customer ? cart.customer.primaryShippingAddressId : null,
showAdd: true,
}) }}

<div class="js-address-select">
<div class="js-address-select" data-model-name="{{ name }}">
{% if attribute(cart, name ~ 'Id') %}
{% set addressHasErrors = attribute(cart, name) and attribute(cart, name).hasErrors() %}
{% else %}
{% set addressHasErrors = false %}
{% endif %}

{% if currentUser %}
{# Show the custom toggle if there is a custom address on the order #}
{% if currentUser and cart and attribute(cart, name ~ 'Id') and not attribute(cart, sourceAttribute) %}
<div class="js-radio">
<label>
{{ input('radio', name ~ 'Id', '', {
checked: not addresses|length or addressHasErrors
checked: true
}) }}
{{ 'New {title}'|t({ title: title }) }}
{{ 'Custom {title}'|t({ title: title }) }}
</label>
</div>
{% endif %}
Expand Down
112 changes: 62 additions & 50 deletions example-templates/dist/shop/_private/address/list.twig
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,73 @@
{% set primaryShippingAddressId = primaryShippingAddressId ?? null %}
{% set cardWidth = cardWidth ?? 'md:w-1/2' %}
{% set showDelete = showDelete ?? false %}
{% set showAdd = showAdd ?? false %}
{% set addUrl = '/shop/customer/addresses/edit?redirect=' ~ craft.app.request.fullPath %}

{% if addresses and currentUser %}
<div class="md:flex md:flex-wrap md:-mx-2 pb-4">
{% for address in addresses %}
{% set editUrl = '/shop/customer/addresses/edit?addressId=' ~ address.id ~ '&redirect=' ~ craft.app.request.fullPath %}
<div class="pb-2 my-4 md:px-2 {{ cardWidth }} md:my-0">
{% tag selectable ? 'label' : 'div' with {
class: 'block relative address-select js-address-select border-blue-300 border-b-2 px-6 py-4 rounded-md shadow-md hover:shadow-lg',
data: {
'address-id': address.id,
},
} %}
<span class="js-radio flex py-2">
{% if selectable %}
{{ input('radio', name ~ 'Id', address.id, {
checked: (attribute(cart, sourceIdName) == address.id) or (not attribute(cart, sourceIdName) and address.id == attribute(_context, primaryIdName)),
}) }}
{% endif %}
<span class="-mt-2 ml-3 mb-2">
{{ address|address }}
</span>
</span>
<span class="block mb-1">
<a href="{{ url(editUrl) }}" class="cursor-pointer rounded px-2 py-1 text-sm inline-block bg-gray-500 hover:bg-gray-600 text-white hover:text-white">
{{- 'Edit'|t -}}
</a>
{% if showDelete and not selectable %}
<form method="post" action="" class="js-address-delete inline-block">
{{ csrfInput() }}
{{ actionInput('users/delete-address') }}
{{ redirectInput('shop/customer/addresses') }}
{{ hiddenInput('addressId', address.id) }}
{{ tag('button', {
type: 'submit',
class: 'cursor-pointer rounded px-2 py-1 text-sm inline-block bg-gray-500 hover:bg-gray-600 text-white hover:text-white',
text: 'Delete'|t
{% if currentUser %}
{% if addresses|length %}
<div class="md:flex md:flex-wrap md:-mx-2 pb-4">
{% for address in addresses %}
{% set editUrl = '/shop/customer/addresses/edit?addressId=' ~ address.id ~ '&redirect=' ~ craft.app.request.fullPath %}
<div class="pb-2 my-4 md:px-2 {{ cardWidth }} md:my-0">
{% tag selectable ? 'label' : 'div' with {
class: 'block relative address-select js-address-select border-blue-300 border-b-2 px-6 py-4 rounded-md shadow-md hover:shadow-lg',
data: {
'address-id': address.id,
},
} %}
<span class="js-radio flex py-2">
{% if selectable %}
{{ input('radio', name ~ 'Id', address.id, {
data: {
'model-name': name,
},
checked: (attribute(cart, sourceIdName) == address.id) or (not attribute(cart, sourceIdName) and address.id == attribute(_context, primaryIdName)),
}) }}
</form>
{% endif %}
</span>

{% if primaryBillingAddressId == address.id or primaryShippingAddressId == address.id %}
<span class="absolute top-4 right-4">
{% if primaryBillingAddressId == address.id %}
<span title="{{ 'Primary billing address'|t }}">💳</span>
{% endif %}
{% if primaryShippingAddressId == address.id %}
<span title="{{ 'Primary shipping address'|t }}">📦</span>
<span class="-mt-2 ml-3 mb-2">
{{ address|address }}
</span>
</span>
<span class="block mb-1">
<a href="{{ url(editUrl) }}" class="cursor-pointer rounded px-2 py-1 text-sm inline-block bg-gray-500 hover:bg-gray-600 text-white hover:text-white">
{{- 'Edit'|t -}}
</a>
{% if showDelete and not selectable %}
<form method="post" action="" class="js-address-delete inline-block">
{{ csrfInput() }}
{{ actionInput('users/delete-address') }}
{{ redirectInput('shop/customer/addresses') }}
{{ hiddenInput('addressId', address.id) }}
{{ tag('button', {
type: 'submit',
class: 'cursor-pointer rounded px-2 py-1 text-sm inline-block bg-gray-500 hover:bg-gray-600 text-white hover:text-white',
text: 'Delete'|t
}) }}
</form>
{% endif %}
</span>
{% endif %}
{% endtag %}
</div>
{% endfor %}
</div>

{% if primaryBillingAddressId == address.id or primaryShippingAddressId == address.id %}
<span class="absolute top-4 right-4">
{% if primaryBillingAddressId == address.id %}
<span title="{{ 'Primary billing address'|t }}">💳</span>
{% endif %}
{% if primaryShippingAddressId == address.id %}
<span title="{{ 'Primary shipping address'|t }}">📦</span>
{% endif %}
</span>
{% endif %}
{% endtag %}
</div>
{% endfor %}
</div>
{% endif %}
{% if showAdd %}
<div class="pt-2">
<a href="{{ url(addUrl) }}" class="cursor-pointer rounded px-2 py-1 text-sm inline-block bg-blue-500 hover:bg-blue-600 text-white hover:text-white">{{ 'Add address'|t }}</a>
</div>
{% endif %}
{% endif %}

{% js %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ Outputs the site’s global main navigation based on path and included `pages` a
<div class="absolute right-2 top-0">
{% if currentUser %}
<a href="{{ url('shop/customer') }}"
class="mx-2 px-2 py-1 rounded text-sm font-semibold">
{{- currentUser.email -}}
class="relative text-lg cursor-pointer inline-block mx-4 my-5 px-2 py-1 bg-white rounded-lg hover:shadow">
👤&nbsp;My Account
</a>
{% else %}
<a href="{{ url('shop/customer/sign-in') }}"
Expand All @@ -47,12 +47,15 @@ Outputs the site’s global main navigation based on path and included `pages` a
{% endif %}
<a href="{{ url('shop/cart') }}"
class="relative text-lg cursor-pointer inline-block mx-4 my-5 px-2 py-1 bg-white rounded-lg hover:shadow">
{% if cart.totalQty %}
<span class="absolute -mr-3 -mt-3 right-0 top-0 py-1 px-2 rounded-full text-white text-xs bg-blue-800">
{{- cart.totalQty -}}
{% if cart.totalQty %}
{{- cart.totalQty -}}
{% else %}
Empty
{% endif %}
</span>
{% endif %}
<p role="img" aria-label="{{ 'Shopping Cart'|t }}">🛒</p>

<span aria-label="{{ 'View Cart'|t }}">🛒 Cart</span>
</a>
</div>
</nav>
Expand Down
48 changes: 25 additions & 23 deletions example-templates/dist/shop/checkout/addresses.twig
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ Outputs a form for collecting an order’s shipping and billing address.
sourceIdName: 'sourceShippingAddressId',
}) }}

{% if currentUser and addresses|length %}
<div class="mt-3">
<label>
{{ input('checkbox', 'makePrimaryShippingAddress', 1, {
id: 'makePrimaryShippingAddress',
}) }}
{{ 'Make this my default shipping address'|t }}
</label>
</div>
{% endif %}

<hr class="my-5">

{{ hiddenInput('billingAddressSameAsShipping', 0) }}
Expand All @@ -57,17 +68,6 @@ Outputs a form for collecting an order’s shipping and billing address.
</label>
</div>

{% if currentUser %}
<div class="mt-3">
<label>
{{ input('checkbox', 'makePrimaryShippingAddress', 1, {
id: 'makePrimaryShippingAddress',
}) }}
{{ 'Make this my default shipping address'|t }}
</label>
</div>
{% endif %}

<div class="mt-8">
{{ include('shop/_private/address/fieldset', {
title: 'Billing Address'|t,
Expand All @@ -78,19 +78,19 @@ Outputs a form for collecting an order’s shipping and billing address.
}) }}
</div>

<hr class="my-5">

{% if currentUser %}
<div class="mt-3">
<label>
{{ input('checkbox', 'makePrimaryBillingAddress', 1, {
id: 'makePrimaryBillingAddress',
}) }}
{{ 'Make this my default billing address'|t }}
</label>
</div>
{% if currentUser and addresses|length %}
<div class="mt-3">
<label>
{{ input('checkbox', 'makePrimaryBillingAddress', 1, {
id: 'makePrimaryBillingAddress',
}) }}
{{ 'Make this my default billing address'|t }}
</label>
</div>
{% endif %}

<hr class="my-5">

<div class="mt-3 flex justify-end">
{{ tag('button', {
type: 'submit',
Expand Down Expand Up @@ -123,6 +123,7 @@ Outputs a form for collecting an order’s shipping and billing address.
if ($body) {
// Creating new address
$radio.addEventListener('change', function(ev) {
console.log('changing');
if (ev.target.checked) {
$body.classList.remove('hidden');
} else {
Expand All @@ -136,6 +137,7 @@ Outputs a form for collecting an order’s shipping and billing address.
} else {
// Selecting existing address
$radio.addEventListener('change', function(ev) {
console.log(ev.target.checked);
if (ev.target.checked) {
var $newBox = document.querySelector('.js-address-select[data-model-name="' + ev.target.dataset.modelName + '"]');
if ($newBox) {
Expand All @@ -156,7 +158,7 @@ Outputs a form for collecting an order’s shipping and billing address.
var $billingFieldSet = document.querySelector('.js-address-fieldset.BillingAddress');
$billingFieldSet.classList.toggle('hidden');

for (let input of document.querySelector('.js-address-box.BillingAddress').querySelectorAll('input')) {
for (let input of $billingFieldSet.querySelectorAll('.js-address-select input')) {
if ($billingSameAs.checked === true) {
input.setAttribute('disabled', '');
} else if (input.hasAttribute('disabled')) {
Expand Down
23 changes: 11 additions & 12 deletions example-templates/dist/shop/checkout/payment.twig
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,24 @@
{% set params = { currency: cart.paymentCurrency } %}
{% endif %}

{# Special params for Stripe Elements and Checkout#}
{# see https://stripe.com/docs/elements/appearance-api #}
{% if className(cart.gateway) == 'craft\\commerce\\stripe\\gateways\\PaymentIntents' %}
{% set params = {
paymentFormType: 'elements',
appearance: {
theme: 'stripe'
},
layout: {
type: 'tabs',
defaultCollapsed: false,
radios: false,
spacedAccordionItems: false
elementOptions: {
layout: {
type: 'accordion',
defaultCollapsed: false,
radios: false,
spacedAccordionItems: false
}
},
'submitButtonClasses': 'cursor-pointer rounded px-4 py-2 inline-block bg-blue-500 hover:bg-blue-600 text-white hover:text-white my-2',
'submitButtonLabel': 'Pay',
'errorMessageClasses': 'bg-red-200 text-red-600 my-2 p-2 rounded',
submitButtonClasses: 'cursor-pointer rounded px-4 py-2 inline-block bg-blue-500 hover:bg-blue-600 text-white hover:text-white my-2',
submitButtonLabel: 'Pay',
errorMessageClasses: 'bg-red-200 text-red-600 my-2 p-2 rounded',
} %}
{% dump params %}
{% endif %}
<div class="gateway-payment-form max-w-3/4">
{% namespace cart.gateway.handle|commercePaymentFormNamespace %}
Expand Down Expand Up @@ -131,7 +130,7 @@

{{ include('shop/checkout/_includes/partial-payment') }}

{% if cart.gateway.showPaymentFormSubmitButton() %}
{% if cart.paymentSourceId or cart.gateway.showPaymentFormSubmitButton() %}
<div class="mt-3 text-right">
{{ tag('button', {
type: 'submit',
Expand Down
Loading

0 comments on commit 174c17c

Please sign in to comment.