Skip to content

Commit

Permalink
NEXT-25264 - Changed salutation to optional
Browse files Browse the repository at this point in the history
NEXT-25264 - ADR for salutation default not specified
  • Loading branch information
nguyenytran committed Jul 6, 2023
1 parent 325349c commit e561c3f
Show file tree
Hide file tree
Showing 43 changed files with 1,244 additions and 117 deletions.
28 changes: 28 additions & 0 deletions adr/2023-06-28-default-handle-for-non-specified-salutations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: Default handling for non specified salutations
date: 2023-06-28
area: core
tags: [adr, salutation]
---

## Context
The current implementation of the salutation in Shopware 6 needs to handle cases where the salutation is not specified by the customer or administrator. To address this requirement and promote inclusivity, we have updated the default salutation to "not_specified" for unspecified salutations in our Shopware 6 platform.

## Decision
We have modified the existing salutation handling in Shopware 6 to update the default value to "not_specified" when the salutation is null. This decision was made based on the following considerations:

* Inclusivity: By offering a default salutation of "not_specified" for null values, we promote inclusivity and ensure that all customers are appropriately addressed, even when salutation data is missing.
* Customer Experience: Providing a default salutation ensures consistency in customer communications and prevents any confusion or misinterpretation when a salutation is not explicitly specified.
* Non-Deletable Default Salutation: It has been decided that the "not_specified" salutation, being the default value for unspecified salutations, should not be deletable by the shop owner. This ensures that there is always a fallback option available, guaranteeing a consistent experience for customers.

## Consequences
As a result of this decision, the following consequences will occur:

* Improved Default Handling: When a customer or administrator does not specify a salutation, the default value will be automatically set to "not_specified." This default value itself is configurable by the shop owner. They have the flexibility to customize the "not_specified" value to their preferred salutation or leave it as it is to use the generic "not_specified" salutation.
* Enhanced Inclusivity: Customers who have not specified their salutation will be addressed using the default "not_specified" salutation, reflecting our commitment to inclusivity and respect within our platform.
* Code Changes: The necessary code changes have been implemented to update the default handling of null salutations. This includes validation checks, database updates, and modifications to relevant logic to accommodate the "not_specified" default value.
* Different Default Values in Specific Locations: The default values used in specific locations within the platform are as follows:
* Letters and Documents: When generating letters or documents where a salutation is required, the default value will be "Dear Customer" or an appropriate alternative if customization is allowed. This ensures a professional and personalized approach in written communications.
* Email Communications: In email communications, the default value will be "Hello" or an alternative greeting if customization is allowed. This provides a friendly and welcoming tone in electronic correspondences.
* User Interfaces: Within the user interfaces of the Shopware 6 platform, the default value will be displayed as "not_specified" for customers who have not specified a salutation. This allows for a neutral and inclusive representation in the platform's user-facing components.
* Testing and Quality Assurance: Rigorous testing procedures will be conducted to ensure the accuracy and reliability of the updated default handling. Quality assurance measures will be in place to identify and address any potential issues.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: Fix salutation auto set default when create customer in admin
issue: NEXT-25264
---
# Administration
* Added computed `defaultSalutationId`, `salutationCriteria`, `salutationRepository` in to get default salutation
* `sw-customer-create` component.
* `sw-customer-detail` component.
* `sw-order-new-customer-modal` component.
* `sw-customer-detail-addresses` component.
* Changed `createdComponent` method in to set default salutation for customer
* `sw-customer-create` component.
* `sw-customer-detail` component.
* `sw-order-new-customer-modal` component.
___
# Core
* Changed `Shopware\Core\Checkout\Customer\SalesChannel\ChangeCustomerProfileRoute::change` to set default to `salutationId`
* Changed `Shopware\Core\Checkout\Customer\SalesChannel\RegisterRoute::register` to set default to `salutationId`
* Changed `Shopware\Core\Checkout\Customer\SalesChannel\UpsertAddressRoute::upsert` to set default to `salutationId`
* Changed `Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressDefinition` to remove flag required with salutationId.
* Added `Shopware\Core\System\Salutation\SalutationSorter` to sort salutations
* Added subscribers to set salutation with default not specified
* `Shopware\Core\Checkout\Order\Subscriber\OrderSalutationSubscriber`
* `Shopware\Core\Checkout\Customer\Subscriber\CustomerSalutationSubscriber`
* `Shopware\Core\Content\Newsletter\Subscriber\NewsletterRecipientSalutationSubscriber`
___
# Storefront
* Changed function `register` in `Shopware\Storefront\Controller\RegisterController` to remove `definition` `salutationId`.
* Changed `Shopware\Storefront\Page\Account\Login\AccountLoginPageLoader::load` to sort salutations by `salutation_key` not specified.
* Changed `Shopware\Storefront\Page\Account\Profile\AccountProfilePageLoader::load` to sort salutations by `salutation_key` not specified.
* Changed `Shopware\Storefront\Page\Address\Detail\AddressDetailPageLoader::load` to sort salutations by `salutation_key` not specified.
* Changed `storefront/component/address/address-personal.html.twig` to remove attribute `required` with `salutationId`.
10 changes: 0 additions & 10 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -16304,11 +16304,6 @@ parameters:
count: 1
path: tests/integration/php/Core/Checkout/Cart/LineItemFactoryRegistryTest.php

-
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNotNull\\(\\) with mixed will always evaluate to true\\.$#"
count: 1
path: tests/integration/php/Core/Checkout/Cart/SalesChannel/CartOrderRouteTest.php

-
message: "#^Method Shopware\\\\Tests\\\\Integration\\\\Core\\\\Checkout\\\\Customer\\\\Rule\\\\DaysSinceLastOrderRuleTest\\:\\:createTestOrderAndReturnCustomer\\(\\) should return Shopware\\\\Core\\\\Checkout\\\\Customer\\\\CustomerEntity but returns Shopware\\\\Core\\\\Framework\\\\DataAbstractionLayer\\\\Entity\\|null\\.$#"
count: 1
Expand Down Expand Up @@ -16871,11 +16866,6 @@ parameters:
count: 1
path: tests/unit/php/Core/Checkout/Customer/Rule/DaysSinceLastOrderRuleTest.php

-
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertInstanceOf\\(\\) with 'Shopware\\\\\\\\Core\\\\\\\\System\\\\\\\\SalesChannel\\\\\\\\SuccessResponse' and Shopware\\\\Core\\\\System\\\\SalesChannel\\\\SuccessResponse will always evaluate to true\\.$#"
count: 2
path: tests/unit/php/Core/Checkout/Customer/SalesChannel/ChangeCustomerProfileRouteTest.php

-
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertInstanceOf\\(\\) with 'Shopware\\\\\\\\Core\\\\\\\\Checkout\\\\\\\\Customer\\\\\\\\SalesChannel\\\\\\\\CustomerResponse' and Shopware\\\\Core\\\\Checkout\\\\Customer\\\\SalesChannel\\\\CustomerResponse will always evaluate to true\\.$#"
count: 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
:disabled="disabled"
:criteria="salutationCriteria"
label-property="displayName"
show-clearable-button
/>
{% endblock %}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
:error="customerSalutationIdError"
:criteria="salutationCriteria"
label-property="displayName"
show-clearable-button
/>
{% endblock %}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
label-property="displayName"
:label="$tc('sw-customer.card.labelSalutation')"
:criteria="salutationCriteria"
show-clearable-button
/>
{% endblock %}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ export default {
languageId() {
return this.loadLanguage(this.customer?.salesChannelId);
},

salutationRepository() {
return this.repositoryFactory.create('salutation');
},

salutationCriteria() {
const criteria = new Criteria(1, 1);

criteria.addFilter(Criteria.equals('salutationKey', 'not_specified'));

return criteria;
},
},

watch: {
Expand Down Expand Up @@ -119,9 +131,10 @@ export default {
},

methods: {
createdComponent() {
Shopware.State.commit('context/resetLanguageToDefault');
async createdComponent() {
const defaultSalutationId = await this.getDefaultSalutation();

Shopware.State.commit('context/resetLanguageToDefault');
this.customer = this.customerRepository.create();

const addressRepository = this.repositoryFactory.create(
Expand All @@ -137,6 +150,8 @@ export default {
this.customer.defaultShippingAddressId = this.address.id;
this.customer.password = '';
this.customer.vatIds = [];
this.customer.salutationId = defaultSalutationId;
this.address.salutationId = defaultSalutationId;
},

saveFinish() {
Expand Down Expand Up @@ -266,5 +281,11 @@ export default {

return res.data[0];
},

async getDefaultSalutation() {
const res = await this.salutationRepository.searchIds(this.salutationCriteria);

return res.data?.[0];
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ async function createWrapper() {
};
}

if (entity === 'salutation') {
return {
searchIds: () => Promise.resolve({
total: 1,
data: ['salutationId'],
}),
};
}

return {
create: () => Promise.resolve(),
};
Expand Down Expand Up @@ -83,6 +92,9 @@ describe('module/sw-customer/page/sw-customer-create', () => {

it('should have company validation when customer type is commercial', async () => {
const wrapper = await createWrapper();

await flushPromises();

wrapper.vm.createNotificationError = jest.fn();
wrapper.vm.validateEmail = jest.fn().mockImplementation(() => Promise.resolve({ isValid: true }));
const notificationMock = wrapper.vm.createNotificationError;
Expand Down Expand Up @@ -168,4 +180,12 @@ describe('module/sw-customer/page/sw-customer-create', () => {

expect(context.languageId).toEqual(Shopware.Context.api.languageId);
});

it('should get default salutation is value not specified', async () => {
const wrapper = await createWrapper();
await flushPromises();

expect(wrapper.vm.customer.salutationId).toBe('salutationId');
expect(wrapper.vm.address.salutationId).toBe('salutationId');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,18 @@ export default {
this.customer.company?.trim().length : true;
},

salutationRepository() {
return this.repositoryFactory.create('salutation');
},

salutationCriteria() {
const criteria = new Criteria(1, 1);

criteria.addFilter(Criteria.equals('salutationKey', 'not_specified'));

return criteria;
},

...mapPageErrors(errorConfig),
},

Expand All @@ -157,7 +169,9 @@ export default {
},

methods: {
createdComponent() {
async createdComponent() {
const defaultSalutationId = await this.getDefaultSalutation();

Shopware.ExtensionAPI.publishData({
id: 'sw-customer-detail__customer',
path: 'customer',
Expand All @@ -171,6 +185,18 @@ export default {
this.defaultCriteria,
).then((customer) => {
this.customer = customer;
if (!this.customer?.salutationId) {
this.customer.salutationId = defaultSalutationId;
}

this.customer.addresses?.map((address) => {
if (!address.salutationId) {
address.salutationId = defaultSalutationId;
}

return address;
});

this.isLoading = false;
});
},
Expand Down Expand Up @@ -359,5 +385,11 @@ export default {
),
});
},

async getDefaultSalutation() {
const res = await this.salutationRepository.searchIds(this.salutationCriteria);

return res.data?.[0];
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ async function createWrapper(privileges = []) {
},
},
}),

searchIds: () => Promise.resolve({
total: 1,
data: ['1'],
}),
};
},
},
Expand Down Expand Up @@ -94,6 +99,7 @@ async function createWrapper(privileges = []) {
'sw-form-field-renderer': await Shopware.Component.build('sw-form-field-renderer'),
'sw-inherit-wrapper': await Shopware.Component.build('sw-inherit-wrapper'),
'sw-skeleton': true,
'sw-loader': true,
},
});
}
Expand Down Expand Up @@ -209,4 +215,10 @@ describe('module/sw-customer/page/sw-customer-detail', () => {

wrapperWithPrivileges.vm.createNotificationError.mockRestore();
});

it('should get default salutation is value not specified', async () => {
await flushPromises();

expect(wrapper.vm.customer.salutationId).toBe('1');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ export default {

return this.activeCustomer.addresses;
},

salutationRepository() {
return this.repositoryFactory.create('salutation');
},

salutationCriteria() {
const criteria = new Criteria(1, 1);

criteria.addFilter(Criteria.equals('salutationKey', 'not_specified'));

return criteria;
},
},

created() {
Expand Down Expand Up @@ -183,9 +195,12 @@ export default {
this.createNewCustomerAddress();
},

createNewCustomerAddress() {
async createNewCustomerAddress() {
const defaultSalutationId = await this.getDefaultSalutation();

const newAddress = this.addressRepository.create();
newAddress.customerId = this.activeCustomer.id;
newAddress.salutationId = defaultSalutationId;

this.currentAddress = newAddress;
},
Expand Down Expand Up @@ -333,5 +348,11 @@ export default {

return `${preFix.charAt(0).toUpperCase()}${preFix.slice(1)}`;
},

async getDefaultSalutation() {
const res = await this.salutationRepository.searchIds(this.salutationCriteria);

return res.data?.[0];
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,21 @@ async function createWrapper() {

stubs: {
'sw-card': {
template: '<div><slot></slot><slot name="grid"></slot></div>',
template: `<div class="sw-card">
<slot name="toolbar"></slot>
<slot name="grid"></slot>
<slot></slot>
</div>`,
},
'sw-card-filter': {
template: '<div class="sw-card-filter"><slot name="filter"></slot></div>',
},
'sw-field': true,
'sw-button': true,
'sw-button': {
template: '<div class="sw-button" @click="$emit(`click`)"></div>',
},
'sw-modal': true,
'sw-icon': true,
'sw-one-to-many-grid': {
props: ['collection'],
template: `
Expand All @@ -56,6 +66,8 @@ async function createWrapper() {
</div>
`,
},
'sw-customer-address-form': true,
'sw-customer-address-form-options': true,
},
});
}
Expand Down Expand Up @@ -92,4 +104,16 @@ describe('module/sw-customer/view/sw-customer-detail-addresses.spec.js', () => {
expect(lastNameCell.find('a').exists()).toBeTruthy();
expect(lastNameCell.find('a').text()).toContain('Nguyen');
});

it('should set not_specified salutation key when creating a new address', async () => {
wrapper.vm.salutationRepository.searchIds = jest.fn(() => Promise.resolve({ data: ['1'] }));

expect(wrapper.vm.currentAddress).toBeNull();

const swButton = wrapper.find('.sw-button');
await swButton.trigger('click');
await flushPromises();

expect(wrapper.vm.currentAddress.salutationId).toBe('1');
});
});
Loading

0 comments on commit e561c3f

Please sign in to comment.