ContactService validation
This commit is contained in:
parent
6ea7520b7b
commit
2f01256a66
6 changed files with 230 additions and 25 deletions
|
|
@ -0,0 +1,106 @@
|
|||
package ru.spcex.clearing.company.config.validation;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import ru.clearing.classes.statics.data.company.Company;
|
||||
import ru.clearing.classes.statics.data.profile.Contact;
|
||||
import ru.clearing.platform.dictionary.ContactTypeDictionary;
|
||||
import ru.spcex.clearing.company.error.CompanyErrors;
|
||||
import ru.spcex.clearing.imdg.IMDGDistributedNames;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.company.ContactNewRequest;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.company.ContactUpdateRequest;
|
||||
import ru.spcex.clearing.validation.common.rules.DictionaryPresentRule;
|
||||
import ru.spcex.clearing.validation.common.rules.EnumPresentRule;
|
||||
import ru.spcex.clearing.validation.common.rules.FieldRequiredRule;
|
||||
import ru.spcex.clearing.validation.common.rules.IdPresentRule;
|
||||
import ru.spcex.platform.classes.base.SpcexObjectBase;
|
||||
import ru.spcex.platform.enumeration.ContactTypes;
|
||||
import ru.spcex.platform.imdg.api.Imdg;
|
||||
import ru.spcex.platform.imdg.validation.ImdgValidationContext;
|
||||
import ru.spcex.platform.utils.validation.IValidator;
|
||||
import ru.spcex.platform.utils.validation.ValidatorImpl;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Configuration
|
||||
public class ContactValidationConfig {
|
||||
|
||||
@Bean("contactNewRequestValidator")
|
||||
public Function<ContactNewRequest, IValidator> contactNewRequestValidator(
|
||||
Map<String, Imdg<? extends SpcexObjectBase>> imdgForValidation
|
||||
) {
|
||||
return contactNewRequest -> {
|
||||
ImdgValidationContext<ContactNewRequest> context = new ImdgValidationContext<>();
|
||||
context.setValidatedObject(contactNewRequest);
|
||||
Consumer<String> addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s));
|
||||
addImdg.accept(IMDGDistributedNames.Map_Company);
|
||||
addImdg.accept(IMDGDistributedNames.Map_ContactTypeDictionary);
|
||||
return new ValidatorImpl<>(context,
|
||||
IdPresentRule.instance("companyId",
|
||||
ContactNewRequest::getCompanyId,
|
||||
IMDGDistributedNames.Map_Company,
|
||||
Company.class,
|
||||
CompanyErrors.RequiredFieldEmpty,
|
||||
CompanyErrors.CompanyNotFound),
|
||||
EnumPresentRule.instance("contactType",
|
||||
ContactNewRequest::getContactType,
|
||||
ContactTypes.values(),
|
||||
CompanyErrors.WrongFieldValue,
|
||||
CompanyErrors.RequiredFieldEmpty),
|
||||
DictionaryPresentRule.instance("contactType",
|
||||
ContactNewRequest::getContactType,
|
||||
IMDGDistributedNames.Map_ContactTypeDictionary,
|
||||
ContactTypeDictionary.class,
|
||||
CompanyErrors.RequiredFieldEmpty,
|
||||
CompanyErrors.WrongFieldValue),
|
||||
FieldRequiredRule.instance("contactValue",
|
||||
ContactNewRequest::getContactValue,
|
||||
CompanyErrors.RequiredFieldEmpty)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@Bean("contactUpdateRequestValidator")
|
||||
public Function<ContactUpdateRequest, IValidator> contactUpdateRequestValidator(
|
||||
Map<String, Imdg<? extends SpcexObjectBase>> imdgForValidation
|
||||
) {
|
||||
return contactUpdateRequest -> {
|
||||
ImdgValidationContext<ContactUpdateRequest> context = new ImdgValidationContext<>();
|
||||
context.setValidatedObject(contactUpdateRequest);
|
||||
Consumer<String> addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s));
|
||||
addImdg.accept(IMDGDistributedNames.Map_Contact);
|
||||
addImdg.accept(IMDGDistributedNames.Map_ContactTypeDictionary);
|
||||
return new ValidatorImpl<>(context,
|
||||
IdPresentRule.instance("id",
|
||||
ContactUpdateRequest::getId,
|
||||
IMDGDistributedNames.Map_Contact,
|
||||
Contact.class,
|
||||
CompanyErrors.RequiredFieldEmpty,
|
||||
CompanyErrors.CompanyNotFound,
|
||||
contact -> {
|
||||
if (contactUpdateRequest.getContactType() == null) return null;
|
||||
if (!contactUpdateRequest.getContactType().equalsIgnoreCase(contact.getContactType())) {
|
||||
return CompanyErrors.EditContactType;
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
EnumPresentRule.instance("contactType",
|
||||
ContactUpdateRequest::getContactType,
|
||||
ContactTypes.values(),
|
||||
false,
|
||||
CompanyErrors.WrongFieldValue,
|
||||
CompanyErrors.RequiredFieldEmpty),
|
||||
DictionaryPresentRule.instance("contactType",
|
||||
ContactUpdateRequest::getContactType,
|
||||
IMDGDistributedNames.Map_ContactTypeDictionary,
|
||||
ContactTypeDictionary.class,
|
||||
CompanyErrors.RequiredFieldEmpty,
|
||||
CompanyErrors.WrongFieldValue,
|
||||
false)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,8 +4,10 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import ru.clearing.classes.statics.data.company.Company;
|
||||
import ru.clearing.classes.statics.data.generated.ClearingMemberCategory;
|
||||
import ru.clearing.classes.statics.data.profile.Contact;
|
||||
import ru.clearing.classes.statics.data.profile.ProfileDocument;
|
||||
import ru.clearing.platform.dictionary.ClearingCategoryDictionary;
|
||||
import ru.clearing.platform.dictionary.ContactTypeDictionary;
|
||||
import ru.clearing.platform.dictionary.DocumentTypeDictionary;
|
||||
import ru.spcex.clearing.imdg.IMDGDistributedNames;
|
||||
import ru.spcex.clearing.validation.common.ValidationHelper;
|
||||
|
|
@ -31,6 +33,8 @@ public class ValidationConfig {
|
|||
addImdg.accept(IMDGDistributedNames.Map_ClearingCategoryDictionary, ClearingCategoryDictionary.class);
|
||||
addImdg.accept(IMDGDistributedNames.Map_ProfileDocument, ProfileDocument.class);
|
||||
addImdg.accept(IMDGDistributedNames.Map_DocumentTypeDictionary, DocumentTypeDictionary.class);
|
||||
addImdg.accept(IMDGDistributedNames.Map_ContactTypeDictionary, ContactTypeDictionary.class);
|
||||
addImdg.accept(IMDGDistributedNames.Map_Contact, Contact.class);
|
||||
|
||||
addImdg.accept(IMDGDistributedNames.Map_WorkflowStatusDictionary, DocumentTypeDictionary.class);
|
||||
addImdg.accept(IMDGDistributedNames.Map_CompanySymbolDictionary, DocumentTypeDictionary.class);
|
||||
|
|
|
|||
|
|
@ -6,44 +6,90 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.clearing.classes.statics.data.profile.Contact;
|
||||
import ru.spcex.clearing.imdg.IMDGDistributedNames;
|
||||
import ru.spcex.clearing.platform.messaging.domain.BaseRequest;
|
||||
import ru.spcex.clearing.platform.messaging.domain.Consts;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.company.ContactNewRequest;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.company.ContactUpdateRequest;
|
||||
import ru.spcex.clearing.platform.messaging.service.QueueConsumer;
|
||||
import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate;
|
||||
import ru.spcex.clearing.validation.common.ValidationHelper;
|
||||
import ru.spcex.platform.imdg.api.Imdg;
|
||||
import ru.spcex.platform.imdg.api.ImdgProvider;
|
||||
import ru.spcex.platform.utils.validation.IValidator;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@Service
|
||||
public class ContactService extends QueueConsumer implements InitializingBean {
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
private final Imdg<Contact> contactMap;
|
||||
private final ValidationHelper validationHelper;
|
||||
private final Function<ContactNewRequest, IValidator> contactNewRequestValidator;
|
||||
private final Function<ContactUpdateRequest, IValidator> contactUpdateRequestValidator;
|
||||
|
||||
@Autowired
|
||||
public ContactService(Consumer<String, Object> kafkaQueue, Producer<String, Object> kafkaProducer,
|
||||
ImdgProvider imdgProvider) {
|
||||
public ContactService(Consumer<String, Object> kafkaQueue,
|
||||
Producer<String, Object> kafkaProducer,
|
||||
ImdgProvider imdgProvider,
|
||||
ValidationHelper validationHelper,
|
||||
@Qualifier("contactNewRequestValidator") Function<ContactNewRequest, IValidator> contactNewRequestValidator,
|
||||
@Qualifier("contactUpdateRequestValidator") Function<ContactUpdateRequest, IValidator> contactUpdateRequestValidator) {
|
||||
super(kafkaQueue, kafkaProducer);
|
||||
this.contactMap = imdgProvider.getImdg(IMDGDistributedNames.Map_Contact, Contact.class);
|
||||
this.validationHelper = validationHelper;
|
||||
this.contactNewRequestValidator = contactNewRequestValidator;
|
||||
this.contactUpdateRequestValidator = contactUpdateRequestValidator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
callback(ContactNewRequest.class)
|
||||
.setConsumer(this::contactNew)
|
||||
.forDestination(Consts.DESTINATION_CONTACT_NEW, callbacks::put);
|
||||
callback(ContactUpdateRequest.class)
|
||||
.setConsumer(this::contactUpdate)
|
||||
.forDestination(Consts.DESTINATION_CONTACT_UPDATE, callbacks::put);
|
||||
init();
|
||||
}
|
||||
|
||||
public void contactUpdate(BaseRequest<ContactUpdateRequest> userRequest) {
|
||||
ContactUpdateRequest req = userRequest.getRequestPayload();
|
||||
public RequestInfoUpdate contactNew(BaseRequest<ContactNewRequest> userRequest) {
|
||||
log.debug("ContactNewRequest received");
|
||||
|
||||
RequestInfoUpdate requestInfoUpdate = validationHelper.validateTillFirstError(
|
||||
userRequest, contactNewRequestValidator
|
||||
);
|
||||
if (requestInfoUpdate != null) return requestInfoUpdate;
|
||||
|
||||
ContactNewRequest contactNewRequest = userRequest.getRequestPayload();
|
||||
Contact contact = new Contact();
|
||||
contact.setCompanyId(contactNewRequest.getCompanyId());
|
||||
contact.setContactType(contactNewRequest.getContactType());
|
||||
contact.setContactValue(contactNewRequest.getContactValue());
|
||||
Long id = contactMap.insert(contact);
|
||||
|
||||
log.debug("successfully processed, id {}", id);
|
||||
return null;
|
||||
}
|
||||
|
||||
public RequestInfoUpdate contactUpdate(BaseRequest<ContactUpdateRequest> userRequest) {
|
||||
log.debug("ContactUpdateRequest received");
|
||||
|
||||
RequestInfoUpdate requestInfoUpdate = validationHelper.validateTillFirstError(
|
||||
userRequest, contactUpdateRequestValidator
|
||||
);
|
||||
if (requestInfoUpdate != null) return requestInfoUpdate;
|
||||
|
||||
// Тип контакта не меняем, при валидации кидаем ошибку.
|
||||
ContactUpdateRequest req = userRequest.getRequestPayload();
|
||||
Contact contact = contactMap.getSingleObjectByID(req.getId());
|
||||
// contact.setContactType(req.getContactType()); по ТЗ не должен менятся при обновлении
|
||||
contact.setContactValue(req.getContactValue());
|
||||
|
||||
contactMap.update(contact);
|
||||
log.debug("successfully processed, id {}", contact.getId());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ class ClearingMemberCategoryServiceTest {
|
|||
@SpyBean
|
||||
private MockProducer<String, Object> mockProducer;
|
||||
|
||||
private final long COMPANY_ID = 777L;
|
||||
private long COMPANY_ID;
|
||||
private Company TEST_COMPANY;
|
||||
|
||||
private final String CLEARING_CATEGORY_DICT_CODE = ClearingCategory.B.getKey();
|
||||
|
|
@ -87,9 +87,8 @@ class ClearingMemberCategoryServiceTest {
|
|||
|
||||
Imdg<Company> companyImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_Company, Company.class);
|
||||
TEST_COMPANY = new Company();
|
||||
TEST_COMPANY.setId(COMPANY_ID);
|
||||
TEST_COMPANY.setWorkflowStatus(WorkflowStatus.Active.getKey());
|
||||
companyImdg.insert(TEST_COMPANY);
|
||||
COMPANY_ID = companyImdg.insert(TEST_COMPANY);
|
||||
|
||||
clearingCategoryDictionary = new ClearingCategoryDictionary();
|
||||
clearingCategoryDictionary.setCode(CLEARING_CATEGORY_DICT_CODE);
|
||||
|
|
|
|||
|
|
@ -12,14 +12,22 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
|||
import org.springframework.boot.test.mock.mockito.SpyBean;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import ru.clearing.classes.statics.data.company.Company;
|
||||
import ru.clearing.classes.statics.data.profile.Contact;
|
||||
import ru.clearing.platform.dictionary.ContactTypeDictionary;
|
||||
import ru.spcex.clearing.company.config.BeanConfiguration;
|
||||
import ru.spcex.clearing.company.config.HazelcastServiceTestConfiguration;
|
||||
import ru.spcex.clearing.company.config.KafkaConfigTest;
|
||||
import ru.spcex.clearing.company.config.validation.ContactValidationConfig;
|
||||
import ru.spcex.clearing.company.config.validation.ValidationConfig;
|
||||
import ru.spcex.clearing.company.utils.MatcherFactory;
|
||||
import ru.spcex.clearing.imdg.IMDGDistributedNames;
|
||||
import ru.spcex.clearing.platform.messaging.domain.BaseRequest;
|
||||
import ru.spcex.clearing.platform.messaging.domain.Consts;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.company.ContactNewRequest;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.company.ContactUpdateRequest;
|
||||
import ru.spcex.platform.enumeration.ContactTypes;
|
||||
import ru.spcex.platform.enumeration.WorkflowStatus;
|
||||
import ru.spcex.platform.imdg.api.Imdg;
|
||||
import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService;
|
||||
|
||||
|
|
@ -30,6 +38,9 @@ import static ru.spcex.clearing.company.utils.TestUtils.*;
|
|||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = {
|
||||
BeanConfiguration.class,
|
||||
ValidationConfig.class,
|
||||
ContactValidationConfig.class,
|
||||
ContactService.class,
|
||||
KafkaConfigTest.class,
|
||||
HazelcastServiceTestConfiguration.class})
|
||||
|
|
@ -38,6 +49,7 @@ class ContactServiceTest {
|
|||
public static final MatcherFactory.Matcher<Contact> CONTACT_MATCHER = usingIgnoringFieldsComparator();
|
||||
private static final int PARTITION = 0;
|
||||
private static final String TOPIC_CONTACT_UPDATE = Consts.DESTINATION_CONTACT_UPDATE;
|
||||
private static final String TOPIC_CONTACT_NEW = Consts.DESTINATION_CONTACT_NEW;
|
||||
private static final Long ID = 0L;
|
||||
|
||||
@Autowired
|
||||
|
|
@ -53,10 +65,50 @@ class ContactServiceTest {
|
|||
@SpyBean
|
||||
private MockProducer<String, Object> mockProducer;
|
||||
|
||||
private Company TEST_COMPANY;
|
||||
private Long COMPANY_ID;
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
hazelcastServiceTest.waitAvailable();
|
||||
contactImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_Contact, Contact.class);
|
||||
|
||||
Imdg<Company> companyImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_Company, Company.class);
|
||||
TEST_COMPANY = new Company();
|
||||
TEST_COMPANY.setWorkflowStatus(WorkflowStatus.Active.getKey());
|
||||
COMPANY_ID = companyImdg.insert(TEST_COMPANY);
|
||||
|
||||
ContactTypeDictionary contactTypeDictionary = new ContactTypeDictionary();
|
||||
contactTypeDictionary.setCode(ContactTypes.adrs.getKey());
|
||||
contactTypeDictionary.setName("name");
|
||||
Imdg<ContactTypeDictionary> contactTypeDictionaryImdg = hazelcastServiceTest.getImdg(
|
||||
IMDGDistributedNames.Map_ContactTypeDictionary, ContactTypeDictionary.class
|
||||
);
|
||||
contactTypeDictionaryImdg.insert(contactTypeDictionary);
|
||||
}
|
||||
@Test
|
||||
void contactNew() throws InterruptedException {
|
||||
//ARRANGE
|
||||
ContactNewRequest contactNewRequest = new ContactNewRequest();
|
||||
contactNewRequest.setCompanyId(COMPANY_ID);
|
||||
contactNewRequest.setContactType(ContactTypes.adrs.getKey());
|
||||
contactNewRequest.setContactValue("value");
|
||||
|
||||
Contact predictableContact = new Contact();
|
||||
predictableContact.setCompanyId(COMPANY_ID);
|
||||
predictableContact.setContactType(ContactTypes.adrs.getKey());
|
||||
predictableContact.setContactValue("value");
|
||||
|
||||
//ACT
|
||||
String jsonString = getJsonStringForNew(contactNewRequest, ID);
|
||||
addRecordToKafka((MockConsumer) contactService.getConsumer(), TOPIC_CONTACT_NEW, PARTITION, 0, jsonString);
|
||||
|
||||
//ASSERT
|
||||
waitingWhenAddedRecordAndCheckIt(ID, mockProducer, producerRecord);
|
||||
|
||||
Contact resultNew = contactImdg.getAllValues().iterator().next();
|
||||
predictableContact.setId(resultNew.getId());
|
||||
CONTACT_MATCHER.assertMatch(resultNew, predictableContact);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -65,39 +117,38 @@ class ContactServiceTest {
|
|||
* Входной запрос {@link ContactUpdateRequest}:<br>
|
||||
* {@link ContactUpdateRequest#id} - Идентификатор записи<br>
|
||||
* {@link ContactUpdateRequest#companyId} - Идентификатор записи<br>
|
||||
* {@link ContactUpdateRequest#contactType} - 1234<br>
|
||||
* {@link ContactUpdateRequest#contactType} - ADDR<br>
|
||||
* {@link ContactUpdateRequest#contactValue} - new ContactValue<br>
|
||||
*/
|
||||
@Test
|
||||
void contactUpdate() throws InterruptedException {
|
||||
//ARRANGE
|
||||
Long existContactID;
|
||||
Contact existsContact = new Contact();
|
||||
existsContact.setId(ID);
|
||||
existsContact.setCompanyId(ID);
|
||||
existsContact.setContactType("0000");
|
||||
existsContact.setCompanyId(COMPANY_ID);
|
||||
existsContact.setContactType(ContactTypes.adrs.getKey());
|
||||
existsContact.setContactValue("exists ContactValue");
|
||||
contactImdg.insert(existsContact);
|
||||
existContactID = contactImdg.insert(existsContact);
|
||||
|
||||
ContactUpdateRequest contactUpdateRequest = new ContactUpdateRequest();
|
||||
contactUpdateRequest.setId(ID);
|
||||
contactUpdateRequest.setCompanyId(1L);
|
||||
contactUpdateRequest.setContactType("1234");
|
||||
contactUpdateRequest.setId(existContactID);
|
||||
contactUpdateRequest.setContactType(ContactTypes.adrs.getKey());
|
||||
contactUpdateRequest.setContactValue("new ContactValue");
|
||||
|
||||
Contact predictableContact = new Contact();
|
||||
predictableContact.setId(ID);
|
||||
predictableContact.setCompanyId(ID);
|
||||
predictableContact.setContactType("0000");
|
||||
predictableContact.setId(existContactID);
|
||||
predictableContact.setCompanyId(COMPANY_ID);
|
||||
predictableContact.setContactType(ContactTypes.adrs.getKey());
|
||||
predictableContact.setContactValue("new ContactValue");
|
||||
|
||||
//ACT
|
||||
String jsonString = getJsonStringForUPDATE(contactUpdateRequest, ID);
|
||||
String jsonString = getJsonStringForUPDATE(contactUpdateRequest, existContactID);
|
||||
addRecordToKafka((MockConsumer) contactService.getConsumer(), TOPIC_CONTACT_UPDATE, PARTITION, 0, jsonString);
|
||||
|
||||
//ASSERT
|
||||
waitingWhenAddedRecordAndCheckIt(ID, mockProducer, producerRecord);
|
||||
waitingWhenAddedRecordAndCheckIt(existContactID, mockProducer, producerRecord);
|
||||
|
||||
Contact resultUpdating = contactImdg.getSingleObjectByID(ID);
|
||||
Contact resultUpdating = contactImdg.getSingleObjectByID(existContactID);
|
||||
CONTACT_MATCHER.assertMatch(resultUpdating, predictableContact);
|
||||
}
|
||||
}
|
||||
|
|
@ -68,7 +68,7 @@ class ProfileDocumentServiceTest {
|
|||
@SpyBean
|
||||
private MockProducer<String, Object> mockProducer;
|
||||
|
||||
private final long COMPANY_ID = 777L;
|
||||
private long COMPANY_ID;
|
||||
private Company TEST_COMPANY;
|
||||
|
||||
private DocumentTypes TEST_DOCUMENT_TYPE = DocumentTypes.cio;
|
||||
|
|
@ -86,9 +86,8 @@ class ProfileDocumentServiceTest {
|
|||
this.companyMap = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_Company, Company.class);
|
||||
|
||||
TEST_COMPANY = new Company();
|
||||
TEST_COMPANY.setId(COMPANY_ID);
|
||||
TEST_COMPANY.setWorkflowStatus(WorkflowStatus.Active.getKey());
|
||||
companyMap.insert(TEST_COMPANY);
|
||||
COMPANY_ID = companyMap.insert(TEST_COMPANY);
|
||||
|
||||
Imdg<DocumentTypeDictionary> documentTypeDictionaryImdg = hazelcastServiceTest.getImdg(
|
||||
IMDGDistributedNames.Map_DocumentTypeDictionary,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue