Skip to content

Commit

Permalink
NEXT-9714 - Merchant Registration
Browse files Browse the repository at this point in the history
  • Loading branch information
shyim committed Aug 20, 2020
1 parent d6decfb commit dec0b22
Show file tree
Hide file tree
Showing 67 changed files with 2,446 additions and 72 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG-6.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ NEXT
* Added property `productStreamSorting` with default value `name:ASC`
* Added property `productStreamLimit` with default value `10`
* Added method `addGroupField` to `platform/src/Administration/Resources/app/administration/src/core/data-new/criteria.data.js`
* Added new component `sw-customer-group-registration-seo-modal` to generate urls for customer group registration
* Changed `sw-settings-customer-group-detail` to add
* as data
* `openSeoModal`
* `seoUrls`
* as computed
* `customerGroupRegistrationRepository`
* `seoUrlRepository`
* `hasRegistration`
* as methods:
* `loadSeoUrls`
* `getSeoUrl`

#### Core

Expand Down Expand Up @@ -146,6 +158,14 @@ NEXT
* Added new constructor parameter `Shopware\Core\Content\ProductStream\Service\ProductStreamBuilder` to `Shopware\Core\Content\Product\Cms\ProductSliderCmsElementResolver`
* Added new private property `$productStreamBuilder` to `Shopware\Core\Content\Product\Cms\ProductSliderCmsElementResolver`
* Added new private method `collectByProductStream` to `Shopware\Core\Content\Product\Cms\ProductSliderCmsElementResolver`
* Added new store-api route `/store-api/v3/customer-group-registration/config/{customerGroupId}` to fetch the configuration of the registration form
* Added new controller `src/Core/Checkout/Customer/Api/CustomerGroupRegistrationActionController.php` to handle accept and decline of customer group requests
* Added new events `Shopware\Core\Checkout\Customer\Event\CustomerGroupRegistrationAccepted`
* Added new events `Shopware\Core\Checkout\Customer\Event\CustomerGroupRegistrationDeclined`
* Added new association `requestedGroup` to `customer` entity
* Added new association `registration` to `customer_group` entity
* Added method `createCustomSeoUrls` to `Shopware/Core/Content/Seo/Api/SeoActionController` to allow creating custom seo urls in the Administration
* Added new property `primaryProperty` to `Shopware/Core/Framework/DataAbstractionLayer/Validation/EntityExists` to allow checking existence of entities with another key

#### Storefront

Expand Down
32 changes: 32 additions & 0 deletions adr/2020-08-14-merchant-registration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 2020-08-14 - Merchant Registration

## Context

We have to provide a registration for merchant.
The definition of a customer, which is defined as a merchant, we want to realize via customer groups.
However, this is not "merchant" specific, because we do not react to "merchant customer groups" in any way in the core. In other words, we implement a customer group registration system.

The process should work as follows:
* The shop owner enables customer group registration for a customer group and generates an url
* This url must be shared by the shop owner to customers (footer, social media, mails, etc.)
* Customer registers on an individual registration page on an individual url
* The customer will be created with the default customer group
* The shop operator can accept / decline the "merchant" registration in the admin

For this I would suggest the following:
* At the customer we store another Foreign Key (desired customer group)
* This is then considered in the StoreApiRoute and stored at the customer
* In Administration we extend the current customer module with an accept / decline button
* Upon activation, we switch the customer group of the customer and set "desired customer group" back to zero.

## Decision

### Headless Frontend

* Headless sales channel can resolve the url to get the foreign key using the seo-url store api route
* Call the customer-group-registration config endpoint with the foreign key to get the form configuration
* Sends a registration to customer registration endpoint with the `requestedGroupId`

## Consequences

* Registration always creates customer accounts even when the request will be declined.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@
"src/Core/Flag/feature_next6009.php",
"src/Core/Flag/feature_next9278.php",
"src/Core/Flag/feature_next10286.php",
"src/Core/Flag/feature_next9279.php"
"src/Core/Flag/feature_next9279.php",
"src/Core/Flag/feature_next6010.php"
],
"exclude-from-classmap": [
"src/**/Test/"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import LicenseViolationsService from 'src/app/service/license-violations.service
import LocaleToLanguageService from 'src/app/service/locale-to-language.service';
import addPluginUpdatesListener from 'src/core/service/plugin-updates-listener.service';
import addShopwareUpdatesListener from 'src/core/service/shopware-updates-listener.service';
import addCustomerGroupRegistrationListener from 'src/core/service/customer-group-registration-listener.service';
import LocaleHelperService from 'src/app/service/locale-helper.service';

/** Import decorators */
Expand Down Expand Up @@ -71,6 +72,7 @@ Application

addPluginUpdatesListener(loginService, serviceContainer);
addShopwareUpdatesListener(loginService, serviceContainer);
addCustomerGroupRegistrationListener(loginService, serviceContainer);

return loginService;
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import ApiService from '../api.service';

/**
* Gateway for the API end point "customer-group-registration"
* @class
* @extends ApiService
*/
class CustomerGroupRegistrationApiService extends ApiService {
constructor(httpClient, loginService, apiEndpoint = 'customer-group-registration') {
super(httpClient, loginService, apiEndpoint);
this.name = 'customerGroupRegistrationService';
}

accept(customerId, additionalParams = {}, additionalHeaders = {}) {
const apiRoute = `/_action/${this.getApiBasePath()}/accept/${customerId}`;

return this.httpClient.post(
apiRoute,
{},
{
params: additionalParams,
headers: this.getBasicHeaders(additionalHeaders)
}
).then((response) => {
return ApiService.handleResponse(response);
});
}

decline(customerId, additionalParams = {}, additionalHeaders = {}) {
const apiRoute = `/_action/${this.getApiBasePath()}/decline/${customerId}`;

return this.httpClient.post(
apiRoute,
{},
{
params: additionalParams,
headers: this.getBasicHeaders(additionalHeaders)
}
).then((response) => {
return ApiService.handleResponse(response);
});
}
}

export default CustomerGroupRegistrationApiService;
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ class SeoUrlApiService extends ApiService {
return ApiService.handleResponse(response);
});
}

createCustomUrl(routeName, urls, additionalParams = {}, additionalHeaders = {}) {
const apiRoute = `/_action/${this.getApiBasePath()}/create-custom-url`;

return this.httpClient.post(
apiRoute,
{ routeName, urls },
{
params: additionalParams,
headers: this.getBasicHeaders(additionalHeaders)
}
).then((response) => {
return ApiService.handleResponse(response);
});
}
}

export default SeoUrlApiService;
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { next6010 } from 'src/flag/feature_next6010';

const { Application, Service } = Shopware;
const { Criteria } = Shopware.Data;


/**
* @module core/service/customer-group-registration-listener
*/

/**
*
* @memberOf module:core/service/customer-group-registration-listener
* @method addCustomerGroupRegistrationListener
* @param loginService
*/
export default function addCustomerGroupRegistrationListener(loginService) {
if (!next6010()) {
return;
}

let applicationRoot = null;

loginService.addOnLoginListener(checkForUpdates);

async function checkForUpdates() {
const customerRepository = Service('repositoryFactory').create('customer');
const criteria = new Criteria();
criteria.addAssociation('requestedGroup');
criteria.addFilter(Criteria.not('AND', [Criteria.equals('requestedGroupId', null)]));

const customers = await customerRepository.search(criteria, Shopware.Context.api);

customers.forEach(createNotification);
}

function createNotification(customer) {
const notification = {
title: getApplicationRootReference().$tc(
'global.default.info'
),
message: getApplicationRootReference().$tc(
'sw-customer.customerGroupRegistration.notification.message',
0,
{ name: `${customer.firstName} ${customer.lastName}`, groupName: customer.requestedGroup.name }
),
actions: [{
label: getApplicationRootReference().$tc(
'sw-customer.customerGroupRegistration.notification.openCustomer',
),
route: { name: 'sw.customer.detail', params: { id: customer.id } }
}],
variant: 'info',
appearance: 'notification',
growl: true
};

getApplicationRootReference().$store.dispatch(
'notification/createNotification',
notification
);
}

function getApplicationRootReference() {
if (!applicationRoot) {
applicationRoot = Application.getApplicationRoot();
}

return applicationRoot;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const NEXT6010 = 'next6010';
export default {
next6010,
ifNext6010,
ifNext6010Call,
NEXT6010
};

export function next6010() {
return Shopware.FeatureConfig.isActive('next6010');
}

export function ifNext6010(closure) {
if (next6010()) {
closure();
}
}

export function ifNext6010Call(object, methodName) {
const closure = () => {
object[methodName]();
};

ifNext6010(closure);
}

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import './sw-customer-detail.scss';
import template from './sw-customer-detail.html.twig';
import errorConfig from './error-config.json';

Expand All @@ -8,7 +9,7 @@ const { mapPageErrors } = Shopware.Component.getComponentHelper();
Component.register('sw-customer-detail', {
template,

inject: ['systemConfigApiService', 'repositoryFactory'],
inject: ['systemConfigApiService', 'repositoryFactory', 'customerGroupRegistrationService'],

mixins: [
Mixin.getByName('notification'),
Expand Down Expand Up @@ -71,6 +72,7 @@ Component.register('sw-customer-detail', {
criteria
.addAssociation('addresses')
.addAssociation('group')
.addAssociation('requestedGroup')
.addAssociation('salutation')
.addAssociation('salesChannel')
.addAssociation('defaultPaymentMethod')
Expand Down Expand Up @@ -217,6 +219,38 @@ Component.register('sw-customer-detail', {
}

return true;
},

acceptCustomerGroupRegistration() {
this.customerGroupRegistrationService.accept(this.customer.id).then(() => {
this.createNotificationSuccess({
title: this.$tc('global.default.success'),
message: this.$tc('sw-customer.customerGroupRegistration.acceptMessage')
});
}).catch(() => {
this.createNotificationError({
title: this.$tc('global.default.error'),
message: this.$tc('sw-customer.customerGroupRegistration.errorMessage')
});
}).finally(() => {
this.createdComponent();
});
},

declineCustomerGroupRegistration() {
this.customerGroupRegistrationService.decline(this.customer.id).then(() => {
this.createNotificationSuccess({
title: this.$tc('global.default.success'),
message: this.$tc('sw-customer.customerGroupRegistration.declineMessage')
});
}).catch(() => {
this.createNotificationError({
title: this.$tc('global.default.error'),
message: this.$tc('sw-customer.customerGroupRegistration.errorMessage')
});
}).finally(() => {
this.createdComponent();
});
}
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,31 @@
{% block sw_customer_detail_content %}
<template #content>
<sw-card-view>
{% block sw_customer_detail_content_customer_group_registration %}
<div class="sw-customer-detail__customer-registration-alert">
<sw-alert variant="info" v-if="customer && customer.requestedGroup">
{% block sw_customer_detail_content_customer_group_registration_content %}
<div class="sw-customer-detail__customer-registration-alert-text">
{% block sw_customer_detail_content_customer_group_registration_message %}
{{ $tc('sw-customer.customerGroupRegistration.alert', 0, { name: customer.requestedGroup.translated.name }) }}
{% endblock %}
</div>
<div class="sw-customer-detail__customer-registration-alert-actions">
{% block sw_customer_detail_content_customer_group_registration_actions %}
<sw-button @click="declineCustomerGroupRegistration" variant="danger" size="small">
{{ $tc('sw-customer.customerGroupRegistration.decline') }}
</sw-button>

<sw-button @click="acceptCustomerGroupRegistration" variant="primary" size="small">
{{ $tc('sw-customer.customerGroupRegistration.accept') }}
</sw-button>
{% endblock %}
</div>
{% endblock %}
</sw-alert>
</div>
{% endblock %}

{% block sw_customer_detail_content_tabs %}
<sw-tabs class="sw-customer-detail-page__tabs">
{% block sw_customer_detail_content_tab_general %}
Expand Down Expand Up @@ -84,4 +109,4 @@
</template>
{% endblock %}
</sw-page>
{% endblock %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.sw-customer-detail__customer-registration-alert {
max-width: 800px;
margin: 0 auto;

.sw-alert__message {
display: flex;
align-items: center;
}

.sw-alert .sw-alert__icon {
top: 50%;
transform: translateY(-50%);
}
}

.sw-customer-detail__customer-registration-alert-actions {
margin-left: auto;

.sw-button:not(:last-child) {
margin-right: 8px;
}
}
Loading

0 comments on commit dec0b22

Please sign in to comment.