Commit f0f80eea authored by Damian Kryger's avatar Damian Kryger
Browse files

#780 Mapping in Family2Family - key attribute mapped

parent cbc60301
Pipeline #265207934 failed with stages
in 13 minutes and 35 seconds
......@@ -18,7 +18,6 @@ services:
- './pim/crontab:/srv/pim/crontab'
- './pim/ecs.yml:/srv/pim/ecs.yml'
- './pim/phpunit.xml.dist:/srv/pim/phpunit.xml.dist:ro'
- './pim/crontab:/srv/pim/crontab'
- './pim/infection.json.dist:/srv/pim/infection.json.dist'
environment:
- PHP_XDEBUG_ENABLED=1
......
......@@ -12,6 +12,7 @@ namespace PcmtRulesBundle\Connector\Job\ConstraintCollectionProvider;
use Akeneo\Tool\Component\Batch\Job\JobParameters\ConstraintCollectionProviderInterface;
use PcmtRulesBundle\Constraints\AttributeExistsInBothFamiliesConstraint;
use PcmtRulesBundle\Constraints\CorrectAttributeMappingConstraint;
use PcmtRulesBundle\Constraints\CorrectKeyAttributeConstraint;
use PcmtRulesBundle\Constraints\DifferentFamilyConstraint;
use PcmtSharedBundle\Connector\Job\JobParameters\SupportedJobsTrait;
use Symfony\Component\Validator\Constraints\Collection;
......@@ -43,9 +44,8 @@ class FamilyToFamilyConstraintCollectionProvider implements ConstraintCollection
new DifferentFamilyConstraint(),
],
'keyAttribute' => [
new NotBlank(),
new Type('string'),
new AttributeExistsInBothFamiliesConstraint(),
new Type('array'),
new CorrectKeyAttributeConstraint()
],
'user_to_notify' => [
new Type('string'),
......
......@@ -26,7 +26,7 @@ class FamilyToFamilyDefaultValueProvider implements DefaultValuesProviderInterfa
return [
'sourceFamily' => '',
'destinationFamily' => '',
'keyAttribute' => '',
'keyAttribute' => ['sourceKeyAttribute' => '', 'destinationKeyAttribute' => ''],
'user_to_notify' => null,
'attributeMapping' => [],
];
......
<?php
namespace PcmtRulesBundle\Constraints;
use Symfony\Component\Validator\Constraint;
class CorrectKeyAttributeConstraint extends Constraint
{
/** {@inheritdoc} */
public function getTargets()
{
return self::PROPERTY_CONSTRAINT;
}
/** {@inheritdoc} */
public function validatedBy()
{
return 'pcmt_correct_key_attribute_constraint_validator';
}
/** @var string */
public $message = 'The key attribute mapping is incorrect';
}
\ No newline at end of file
......@@ -124,7 +124,7 @@ extensions:
position: 165
targetZone: properties
config:
fetcher: key-attribute-for-rule
fetcher: attributes-for-f2f-mapping
sourceFamily: 'configuration.sourceFamily'
destinationFamily: 'configuration.destinationFamily'
fieldCode: configuration.keyAttribute
......
......@@ -20,6 +20,7 @@ config:
pcmt/rules/template/group: pcmtrules/templates/group.html
pcmt/rules/template/job/field/select: pcmtrules/templates/job/field/select.html
pcmt/rules/template/job/field/multiple-select: pcmtrules/templates/job/field/multiple-select.html
pcmt/rules/template/job/rules/edit/mapping-tab: pcmtrules/templates/job/rules/edit/mapping-tab.html
pcmt/rules/template/job/rules/edit/attribute-mapping: pcmtrules/templates/job/rules/edit/attribute-mapping.html
pcmt/rules/template/job/rules/edit/attribute-mapping-row: pcmtrules/templates/job/rules/edit/attribute-mapping-row.html
......
......@@ -126,6 +126,13 @@ services:
tags:
- { name: validator.constraint_validator, alias: pcmt_correct_attribute_mapping_constraint_validator }
pcmt.constraints.correct_key_attribute_mapping_constraint_validator:
class: PcmtRulesBundle\Validators\CorrectKeyAttributeConstraintValidator
arguments:
- '@pim_catalog.repository.family'
tags:
- { name: validator.constraint_validator, alias: pcmt_correct_key_attribute_constraint_validator }
pcmt.constraints.family_has_variants_constraint_validator:
class: PcmtRulesBundle\Validators\FamilyHasVariantsConstraintValidator
arguments:
......
......@@ -12,18 +12,30 @@
define([
'underscore',
'pcmt/rules/job/field/select',
'pcmt/rules/template/job/field/multiple-select',
'pim/common/property',
'pim/fetcher-registry'
'pim/fetcher-registry',
'pim/user-context',
'pim/i18n'
], function (
_,
BaseField,
fieldTemplate,
propertyAccessor,
FetcherRegistry
FetcherRegistry,
UserContext,
i18n
) {
return BaseField.extend({
fieldTemplate: _.template(fieldTemplate),
sourceFamily: '',
destinationFamily: '',
sourceOptions: [],
destinationOptions: [],
sourceKeyAttribute: '',
destinationKeyAttribute: '',
configure: function() {
this.listenTo(this.getRoot(), this.postUpdateEventName, this.onUpdateField);
......@@ -43,17 +55,33 @@ define([
options.validationRule = this.config.validationRule;
}
FetcherRegistry.getFetcher(this.config.fetcher).fetchForOptions(options).then(
function (options) {
this.selectOptions = options;
function (result) {
this.sourceOptions = result.sourceAttributeList;
this.destinationOptions = result.destinationAttributeList;
this.render();
this.updateState();
}.bind(this)
);
} else {
this.selectOptions = [];
this.sourceOptions = [];
this.destinationOptions = [];
}
},
/**
* {@inheritdoc}
*/
renderInput: function (templateContext) {
return this.fieldTemplate(_.extend(templateContext, {
sourceOptions: this.sourceOptions,
sourcePlaceholder: 'Source key attribute',
destinationOptions: this.destinationOptions,
destinationPlaceholder: 'Destination key attribute',
i18n: i18n,
locale: UserContext.get('catalogLocale')
}));
},
onUpdateField: function() {
const newSourceFamily = propertyAccessor.accessProperty(this.getFormData(), this.config.sourceFamily);
const newDestinationFamily = propertyAccessor.accessProperty(this.getFormData(), this.config.destinationFamily);
......@@ -71,5 +99,17 @@ define([
}
},
/**
* Get the field dom value
*
* @return {string}
*/
getFieldValue: function () {
return {
sourceKeyAttribute: this.$('.sourceKeyAttribute').val(),
destinationKeyAttribute: this.$('.destinationKeyAttribute').val()
}
}
});
});
<select
class="select2 sourceKeyAttribute"
id="jobInstance_<%- config.fieldCode.replace('.', '_') %>"
title="<%- __(config.label) %>"
name=""
data-placeholder="<%- sourcePlaceholder %>"
<%- true === config.readOnly ? 'readonly disabled' : '' %>
>
<option value=""<% if (!value) { %> selected<% } %>></option>
<% _.each(sourceOptions, function (option) { %>
<option
value="<%- option.code %>"
<% if (option.code === value) { %> selected<% } %>
>
<%- i18n.getLabel(option.labels, locale, option.code) %>
</option>
<% }); %>
</select>
=>
<select
class="select2 destinationKeyAttribute"
id="jobInstance_<%- config.fieldCode.replace('.', '_') %>"
title="<%- __(config.label) %>"
name=""
data-placeholder="<%- destinationPlaceholder %>"
<%- true === config.readOnly ? 'readonly disabled' : '' %>
>
<option value=""<% if (!value) { %> selected<% } %>></option>
<% _.each(destinationOptions, function (option) { %>
<option
value="<%- option.code %>"
<% if (option.code === value) { %> selected<% } %>
>
<%- i18n.getLabel(option.labels, locale, option.code) %>
</option>
<% }); %>
</select>
\ No newline at end of file
......@@ -82,7 +82,7 @@ class RuleProcessor
$keyAttributeCode = $stepExecution->getJobParameters()->get('keyAttribute');
$keyValue = $sourceProduct->getValue($keyAttributeCode);
$keyValue = $sourceProduct->getValue($keyAttributeCode['sourceKeyAttribute']);
if (!$keyValue) {
return;
}
......@@ -94,12 +94,12 @@ class RuleProcessor
'limit' => self::MAX_DESTINATION_PRODUCTS,
]);
try {
$pqb->addFilter($keyAttributeCode, Operators::EQUALS, $keyValue->getData());
$pqb->addFilter($keyAttributeCode['destinationKeyAttribute'], Operators::EQUALS, $keyValue->getData());
} catch (\Throwable $e) {
try {
$pqb->addFilter($keyAttributeCode, Operators::IN_LIST, [$keyValue->getData()]);
$pqb->addFilter($keyAttributeCode['destinationKeyAttribute'], Operators::IN_LIST, [$keyValue->getData()]);
} catch (\Throwable $e) {
throw new \Exception('Unsupported attribute type used as key attribute in a rule: ' . $keyAttributeCode);
throw new \Exception('Unsupported attribute type used as key attribute in a rule: ' . $keyAttributeCode['destinationKeyAttribute']);
}
}
$pqb->addFilter('family', Operators::IN_LIST, [$destinationFamily->getCode()]);
......
<?php
/*
* Copyright (c) 2020, VillageReach
* Licensed under the Non-Profit Open Software License version 3.0.
* SPDX-License-Identifier: NPOSL-3.0
*/
declare(strict_types=1);
namespace PcmtRulesBundle\Validators;
use Akeneo\Pim\Structure\Component\Model\AttributeInterface;
use Akeneo\Pim\Structure\Component\Model\FamilyInterface;
use Akeneo\Pim\Structure\Component\Repository\FamilyRepositoryInterface;
use PcmtRulesBundle\Constraints\CorrectAttributeMappingConstraint;
use PcmtRulesBundle\Constraints\CorrectKeyAttributeConstraint;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class CorrectKeyAttributeConstraintValidator extends ConstraintValidator
{
/** @var FamilyRepositoryInterface */
private $familyRepository;
public function __construct(
FamilyRepositoryInterface $familyRepository
) {
$this->familyRepository = $familyRepository;
}
/**
* {@inheritdoc}
*/
public function validate($entity, Constraint $constraint): void
{
if (!$constraint instanceof CorrectKeyAttributeConstraint) {
throw new UnexpectedTypeException($constraint, CorrectKeyAttributeConstraint::class);
}
$sourceFamilyCode = $this->getStringValueFromRoot('sourceFamily');
$destinationFamilyCode = $this->getStringValueFromRoot('destinationFamily');
$sourceFamily = $this->getFamilyByCode($sourceFamilyCode);
$destinationFamily = $this->getFamilyByCode($destinationFamilyCode);
if (!$sourceFamily || !$destinationFamily) {
return;
}
try {
$keyAttributeMapping = $this->getArrayValueFromRoot('keyAttribute');
$this->doValidate($sourceFamily, $destinationFamily, $keyAttributeMapping['sourceKeyAttribute'], $keyAttributeMapping['destinationKeyAttribute']);
} catch (\Throwable $e) {
$this->context->buildViolation($constraint->message. ' | '. $e->getMessage())
->addViolation();
}
}
private function doValidate(
FamilyInterface $sourceFamily,
FamilyInterface $destinationFamily,
string $sourceAttributeCode,
string $destinationAttributeCode
): void {
if (!$sourceAttributeCode && !$destinationAttributeCode) {
return;
}
if (!$sourceFamily->hasAttributeCode($sourceAttributeCode)) {
throw new \Exception('Source family does not have attribute '. $sourceAttributeCode);
}
if (!$destinationFamily->hasAttributeCode($destinationAttributeCode)) {
throw new \Exception('Destination family does not have attribute '. $destinationAttributeCode);
}
$sourceAttributes = $sourceFamily->getAttributes();
/** @var AttributeInterface $sourceAttribute */
$sourceAttribute = $sourceAttributes->filter(function (AttributeInterface $attribute) use ($sourceAttributeCode) {
return $attribute->getCode() === $sourceAttributeCode;
})->first();
$destinationAttributes = $destinationFamily->getAttributes();
/** @var AttributeInterface $destinationAttribute */
$destinationAttribute = $destinationAttributes->filter(function (AttributeInterface $attribute) use ($destinationAttributeCode) {
return $attribute->getCode() === $destinationAttributeCode;
})->first();
if ($sourceAttribute->getType() !== $destinationAttribute->getType()) {
throw new \Exception(sprintf(
'Attributes %s and %s are of different types.',
$sourceAttribute->getLabel(),
$destinationAttribute->getLabel()
));
}
}
private function getStringValueFromRoot(string $code): string
{
$root = $this->context->getRoot();
return $root[$code] ?? '';
}
private function getArrayValueFromRoot(string $code): array
{
$root = $this->context->getRoot();
return $root[$code] ?? [];
}
private function getFamilyByCode(string $code): ?FamilyInterface
{
return $this->familyRepository->findOneBy(['code' => $code]);
}
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment