MapstructMapper.java

/*
 * Copyright 2023 Global Crop Diversity Trust
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gringlobal.api.v2.mapper;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

import javax.validation.constraints.NotNull;

import org.apache.commons.lang3.Strings;
import org.genesys.blocks.auditlog.model.AuditLog;
import org.genesys.blocks.model.ClassPK;
import org.genesys.blocks.model.EmptyModel;
import org.genesys.blocks.oauth.model.Authorization;
import org.genesys.blocks.oauth.model.OAuthClient;
import org.genesys.blocks.security.SecurityContextUtil;
import org.genesys.blocks.security.model.AclAwareModel;
import org.genesys.blocks.security.model.AclClass;
import org.genesys.blocks.security.model.AclEntry;
import org.genesys.blocks.security.model.AclObjectIdentity;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.blocks.security.serialization.AclEntriesToPermissions;
import org.genesys.blocks.security.serialization.Permissions;
import org.genesys.blocks.security.serialization.SidPermissions;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.genesys.filerepository.metadata.ImageMetadata;
import org.genesys.filerepository.model.ImageGallery;
import org.genesys.filerepository.model.RepositoryFile;
import org.genesys.filerepository.model.RepositoryFolder;
import org.genesys.filerepository.model.RepositoryImage;
import org.gringlobal.api.MultiOp;
import org.gringlobal.api.model.*;
import org.gringlobal.api.model.integration.GenesysAttachmentDTO;
import org.gringlobal.api.model.kpi.BooleanDimensionDTO;
import org.gringlobal.api.model.kpi.DimensionDTO;
import org.gringlobal.api.model.kpi.DimensionKeyInfo;
import org.gringlobal.api.model.kpi.ExecutionDTO;
import org.gringlobal.api.model.kpi.ExecutionDimensionDTO;
import org.gringlobal.api.model.kpi.ExecutionGroupInfo;
import org.gringlobal.api.model.kpi.ExecutionRunDTO;
import org.gringlobal.api.model.kpi.ExecutionRunInfo;
import org.gringlobal.api.model.kpi.JpaDimensionDTO;
import org.gringlobal.api.model.kpi.KPIParameterDTO;
import org.gringlobal.api.model.kpi.NumericListDimensionDTO;
import org.gringlobal.api.model.kpi.ObservationDTO;
import org.gringlobal.api.model.kpi.StringListDimensionDTO;
import org.gringlobal.api.v2.facade.CropApiService;
import org.gringlobal.api.v2.facade.CropAttachmentApiService;
import org.gringlobal.api.v2.facade.CropTraitAttachmentApiService;
import org.gringlobal.api.v2.facade.CropTraitCodeAttachmentApiService;
import org.gringlobal.api.v2.facade.CropTraitObservationApiService;
import org.gringlobal.api.v2.facade.CropTraitObservationAttachmentApiService;
import org.gringlobal.api.v2.facade.CropTraitObservationDataApiService;
import org.gringlobal.api.v2.facade.CropTraitObservationDataAttachmentApiService;
import org.gringlobal.api.v2.facade.InventoryApiService.InventoryHarvestDTO;
import org.gringlobal.api.v2.facade.InventoryApiService;
import org.gringlobal.api.v2.facade.InventoryAttachmentApiService;
import org.gringlobal.api.v2.facade.InventoryQualityStatusAttachmentApiService;
import org.gringlobal.api.v2.facade.InventorySymptomMapAttachApiService;
import org.gringlobal.api.v2.facade.InventoryViabilityAttachmentApiService;
import org.gringlobal.api.v2.facade.InvitroInventoryApiService;
import org.gringlobal.api.v2.facade.KPIApiService;
import org.gringlobal.api.v2.facade.OrderRequestAttachApiService;
import org.gringlobal.api.v2.facade.PathogenAttachApiService;
import org.gringlobal.api.v2.model.Traits.CropTraitObservationValue;
import org.gringlobal.model.Accession;
import org.gringlobal.model.AccessionAction;
import org.gringlobal.model.AccessionInvAnnotation;
import org.gringlobal.model.AccessionInvAttach;
import org.gringlobal.model.AccessionInvGroup;
import org.gringlobal.model.AccessionInvGroupMap;
import org.gringlobal.model.AccessionInvName;
import org.gringlobal.model.AccessionIpr;
import org.gringlobal.model.AccessionPedigree;
import org.gringlobal.model.AccessionQuarantine;
import org.gringlobal.model.AccessionSource;
import org.gringlobal.model.AccessionSourceMap;
import org.gringlobal.model.AppResource;
import org.gringlobal.model.AppSetting;
import org.gringlobal.model.AppUserGuiSetting;
import org.gringlobal.model.Citation;
import org.gringlobal.model.CodeValue;
import org.gringlobal.model.CodeValueLang;
import org.gringlobal.model.Cooperator;
import org.gringlobal.model.Crop;
import org.gringlobal.model.CropAttach;
import org.gringlobal.model.CropTrait;
import org.gringlobal.model.CropTraitAttach;
import org.gringlobal.model.CropTraitCode;
import org.gringlobal.model.CropTraitCodeAttach;
import org.gringlobal.model.CropTraitCodeLang;
import org.gringlobal.model.CropTraitLang;
import org.gringlobal.model.CropTraitObservation;
import org.gringlobal.model.CropTraitObservationAttach;
import org.gringlobal.model.CropTraitObservationData;
import org.gringlobal.model.CropTraitObservationDataAttach;
import org.gringlobal.model.Exploration;
import org.gringlobal.model.Feedback;
import org.gringlobal.model.GeneticMarker;
import org.gringlobal.model.Geography;
import org.gringlobal.model.Inventory;
import org.gringlobal.model.InventoryAction;
import org.gringlobal.model.InventoryExtra;
import org.gringlobal.model.InventoryMaintenancePolicy;
import org.gringlobal.model.InventoryQualityStatus;
import org.gringlobal.model.InventoryViability;
import org.gringlobal.model.InventoryViabilityAction;
import org.gringlobal.model.InventoryViabilityAttach;
import org.gringlobal.model.InventoryViabilityData;
import org.gringlobal.model.InventoryViabilityDataEnvironmentMap;
import org.gringlobal.model.InventoryViabilityEnvironment;
import org.gringlobal.model.InventoryViabilityRule;
import org.gringlobal.model.InventoryViabilityRuleMap;
import org.gringlobal.model.Literature;
import org.gringlobal.model.Materiel;
import org.gringlobal.model.Method;
import org.gringlobal.model.NameGroup;
import org.gringlobal.model.OrderRequest;
import org.gringlobal.model.OrderRequestAction;
import org.gringlobal.model.OrderRequestAttach;
import org.gringlobal.model.OrderRequestItem;
import org.gringlobal.model.OrderRequestItemAction;
import org.gringlobal.model.SeedInventoryExtra;
import org.gringlobal.model.Site;
import org.gringlobal.model.SourceDescObservation;
import org.gringlobal.model.SourceDescriptor;
import org.gringlobal.model.SourceDescriptorCode;
import org.gringlobal.model.SourceDescriptorCodeLang;
import org.gringlobal.model.SourceDescriptorLang;
import org.gringlobal.model.SysDataview;
import org.gringlobal.model.SysDataviewField;
import org.gringlobal.model.SysDataviewFieldLang;
import org.gringlobal.model.SysDataviewLang;
import org.gringlobal.model.SysDataviewParam;
import org.gringlobal.model.SysDataviewSql;
import org.gringlobal.model.SysGroup;
import org.gringlobal.model.SysGroupUserMap;
import org.gringlobal.model.SysLang;
import org.gringlobal.model.SysTableField;
import org.gringlobal.model.SysUser;
import org.gringlobal.model.TaxonomyCropMap;
import org.gringlobal.model.TaxonomyFamily;
import org.gringlobal.model.TaxonomyGenus;
import org.gringlobal.model.TaxonomySpecies;
import org.gringlobal.model.TissueCultureExtra;
import org.gringlobal.model.WebCooperator;
import org.gringlobal.model.WebOrderRequest;
import org.gringlobal.model.WebUser;
import org.gringlobal.model.community.InventoryQualityStatusAttach;
import org.gringlobal.model.community.InventorySymptomMap;
import org.gringlobal.model.community.InventorySymptomMapAttach;
import org.gringlobal.model.community.Location;
import org.gringlobal.model.community.LocationAction;
import org.gringlobal.model.community.LocationData;
import org.gringlobal.model.community.LocationReservation;
import org.gringlobal.model.community.OrderRequestItemPathogenMap;
import org.gringlobal.model.community.Pathogen;
import org.gringlobal.model.community.PathogenAttach;
import org.gringlobal.model.community.PathogenGeographyMap;
import org.gringlobal.model.community.PathogenSymptomMap;
import org.gringlobal.model.community.PathogenTaxonomySpeciesMap;
import org.gringlobal.model.community.SecuredAction;
import org.gringlobal.model.integration.GenesysAttachment;
import org.gringlobal.model.community.Symptom;
import org.gringlobal.model.community.SymptomAttach;
import org.gringlobal.model.kpi.BooleanDimension;
import org.gringlobal.model.kpi.Dimension;
import org.gringlobal.model.kpi.DimensionKey;
import org.gringlobal.model.kpi.Execution;
import org.gringlobal.model.kpi.ExecutionDimension;
import org.gringlobal.model.kpi.ExecutionGroup;
import org.gringlobal.model.kpi.ExecutionRun;
import org.gringlobal.model.kpi.JpaDimension;
import org.gringlobal.model.kpi.KPIParameter;
import org.gringlobal.model.kpi.NumericListDimension;
import org.gringlobal.model.kpi.Observation;
import org.gringlobal.model.kpi.StringListDimension;
import org.gringlobal.model.notification.NotificationSchedule;
import org.gringlobal.model.notification.NotificationScheduleSubscriber;
import org.gringlobal.model.workflow.Workflow;
import org.gringlobal.model.workflow.WorkflowActionStep;
import org.gringlobal.model.workflow.WorkflowEndStep;
import org.gringlobal.model.workflow.WorkflowStartStep;
import org.gringlobal.model.workflow.WorkflowStep;
import org.gringlobal.model.workflow.WorkflowTransition;
import org.gringlobal.persistence.InventoryRepositoryCustom;
import org.gringlobal.service.AccessionActionService;
import org.gringlobal.service.AccessionService;
import org.gringlobal.service.CodeValueTranslationService;
import org.gringlobal.service.CropAttachmentService;
import org.gringlobal.service.CropService;
import org.gringlobal.service.CropTraitAttachmentService;
import org.gringlobal.service.CropTraitCodeAttachmentService;
import org.gringlobal.service.CropTraitCodeTranslationService;
import org.gringlobal.service.CropTraitObservationAttachmentService;
import org.gringlobal.service.CropTraitObservationDataAttachmentService;
import org.gringlobal.service.CropTraitObservationDataService;
import org.gringlobal.service.CropTraitObservationService;
import org.gringlobal.service.CropTraitObservationService.TranslatedCropTraitObservation;
import org.gringlobal.service.CropTraitTranslationService;
import org.gringlobal.service.InventoryActionService.InventoryActionRequest;
import org.gringlobal.service.InventoryAttachmentService;
import org.gringlobal.service.InventoryQualityStatusAttachmentService;
import org.gringlobal.service.InventoryService;
import org.gringlobal.service.InventorySymptomMapAttachmentService;
import org.gringlobal.service.InventoryViabilityActionService.InventoryViabilityActionRequest;
import org.gringlobal.service.InventoryViabilityAttachmentService;
import org.gringlobal.service.InventoryViabilityService.InventoryViabilityDetails;
import org.gringlobal.service.InvitroInventoryService;
import org.gringlobal.service.KPIService;
import org.gringlobal.service.OrderRequestAttachmentService;
import org.gringlobal.service.OrderRequestService;
import org.gringlobal.service.InventoryService.InventoryHarvest;
import org.gringlobal.service.LocationActionService;
import org.gringlobal.service.OrderRequestActionService.OrderRequestActionRequest;
import org.gringlobal.service.OrderRequestItemActionService.OrderRequestItemActionRequest;
import org.gringlobal.service.PathogenAttachmentService;
import org.gringlobal.service.SourceDescObservationService;
import org.gringlobal.service.SourceDescriptorCodeTranslationService;
import org.gringlobal.service.SourceDescriptorTranslationService;
import org.gringlobal.service.SysDataviewFieldTranslationService;
import org.gringlobal.service.SysDataviewTranslationService;
import org.gringlobal.service.SysDataviewTranslationService.SysDataviewDetails;
import org.gringlobal.service.SysTableFieldTranslationService;
import org.hibernate.Hibernate;
import org.mapstruct.BeanMapping;
import org.mapstruct.InjectionStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.SubclassMapping;
import org.springframework.data.domain.Page;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import static org.gringlobal.model.community.CommunityCodeValues.ORDER_REQUEST_ITEM_STATUS_CANCEL;
import static org.gringlobal.model.community.CommunityCodeValues.ORDER_REQUEST_ITEM_STATUS_SHIPPED;
import static org.gringlobal.model.community.CommunityCodeValues.ORDER_REQUEST_ITEM_STATUS_SPLIT;
import static org.gringlobal.service.impl.InventoryServiceImpl.GRAM_UNITS;
import static org.gringlobal.service.impl.InventoryServiceImpl.SEED_UNITS;

@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.FIELD,
	imports = { Objects.class, SecurityContextUtil.class, ImageMetadata.class }
)
public interface MapstructMapper {

	AclEntriesToPermissions aclEntriesToPermissions = new AclEntriesToPermissions();
	String Y = "Y"; // GG true
	String N = "N"; // GG false

	default <A, B> List<B> map(@NotNull List<A> source, @NotNull Function<A, B> mapper) {
		var res = new ArrayList<B>(source.size());
		source.forEach(x -> res.add(mapper.apply(x)));
		return res;
	}

	default <A, B> Set<B> map(@NotNull Set<A> source, @NotNull Function<A, B> mapper) {
		var res = new HashSet<B>(source.size());
		source.forEach(x -> res.add(mapper.apply(x)));
		return res;
	}

	default <A, B> Page<B> map(@NotNull Page<A> source, @NotNull Function<A, B> mapper) {
		return source.map(mapper);
	}

	default <A, B> MultiOp<B> map(@NotNull MultiOp<A> source, @NotNull Function<A, B> mapper) {
		return new MultiOp<>(this.map(source.success, mapper), source.errors);
	}

	@Named("mapStringYN")
	default boolean mapStringYN(String ynValue) {
		return Objects.equals("Y", ynValue);
	}

	@Named("mapNullableStringYN")
	default Boolean mapNullableStringYN(String ynValue) {
		return ynValue == null ? null : Objects.equals("Y", ynValue);
	}

	@Named("mapBooleanYN")
	default String mapBooleanYN(boolean value) {
		return value ? Y : N;
	}

	@Named("mapNullableBooleanYN")
	default String mapNullableBooleanYN(Boolean value) {
		return value == null ? null : value ? Y : N;
	}

	@Named("getClassName")
	default String getClassName(EmptyModel entity) {
		return entity.getClass().getSimpleName();
	}

	default Long mapSidId(AclSid value) {
		return value == null ? null : value.getId();
	}

	default AclSid mapSidId(Long id) {
		AclSid sid = null;
		if (id != null) {
			sid = new AclSid();
			sid.setId(id);
		}
		return sid;
	}

	@Named("mapAclEntriesToPermissionsInfo")
	default List<SidPermissionsInfo> mapAclEntriesToPermissions(List<AclEntry> aclEntries) {
		return map(aclEntriesToPermissions.convert(aclEntries), this::mapInfo);
	}

	@Named("getPermissions")
	static Permissions getPermissions(AclAwareModel model) {
		if (model.getId() == null) {
			// Don't write permissions for non-persisted objects
			return null;
		}

		// StopWatch stopWatch = StopWatch.createStarted();
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication == null) {
			return new Permissions().grantNone();
		}

		Permissions perms;
		if (SecurityContextUtil.hasRole("ADMINISTRATOR")) {
			perms = new Permissions().grantAll();
			try {
				perms.isPublic = SecurityContextUtil.anyoneHasPermission(model, "READ");
			} catch (Throwable e) {
				perms.isPublic = false;
			}
		} else {
			try {
				perms = SecurityContextUtil.getPermissions(authentication, model);
			} catch (Throwable e) {
				throw new IllegalArgumentException("Could not read current permissions " + e.getMessage(), e);
			}
		}
		return perms;
	}

	@Mapping(source = "isBackedUp", target = "isBackedUp", qualifiedByName = "mapStringYN")
	@Mapping(source = "isCore", target = "isCore", qualifiedByName = "mapStringYN")
	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	AccessionDTO map(Accession accession);

	@Mapping(source = "isBackedUp", target = "isBackedUp", qualifiedByName = "mapBooleanYN")
	@Mapping(source = "isCore", target = "isCore", qualifiedByName = "mapBooleanYN")
	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "accessionSources", ignore = true)
	@Mapping(target = "inventories", ignore = true)
	@Mapping(target = "accessionActions", ignore = true)
	@Mapping(target = "accessionIprs", ignore = true)
	@Mapping(target = "accessionPedigree", ignore = true)
	@Mapping(target = "accessionQuarantines", ignore = true)
	@Mapping(target = "citations", ignore = true)
	@Mapping(target = "names", ignore = true)
	Accession map(AccessionDTO accession);

	@Mapping(source = "isBackedUp", target = "isBackedUp", qualifiedByName = "mapBooleanYN")
	@Mapping(source = "isCore", target = "isCore", qualifiedByName = "mapBooleanYN")
	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "accessionSources", ignore = true)
	@Mapping(target = "inventories", ignore = true)
	@Mapping(target = "accessionActions", ignore = true)
	@Mapping(target = "accessionIprs", ignore = true)
	@Mapping(target = "accessionPedigree", ignore = true)
	@Mapping(target = "accessionQuarantines", ignore = true)
	@Mapping(target = "citations", ignore = true)
	Accession map(AcquisitionAccessionDTO dto);

	@Mapping(target = "isDistributionSite", source = "isDistributionSite", qualifiedByName = "mapStringYN")
	@Mapping(target = "isInternal", source = "isInternal", qualifiedByName = "mapStringYN")
	SiteDTO map(Site site);

	@Mapping(source = "isDistributionSite", target = "isDistributionSite", qualifiedByName = "mapBooleanYN")
	@Mapping(source = "isInternal", target = "isInternal", qualifiedByName = "mapBooleanYN")
	Site map(SiteDTO site);

	SiteInfo mapInfo(Site site);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Site mapInfo(SiteInfo site);

	@Mapping(target = "isFormaHybrid", source = "isFormaHybrid", qualifiedByName = "mapStringYN")
	@Mapping(target = "isNamePending", source = "isNamePending", qualifiedByName = "mapStringYN")
	@Mapping(target = "isSpecificHybrid", source = "isSpecificHybrid", qualifiedByName = "mapStringYN")
	@Mapping(target = "isSubspecificHybrid", source = "isSubspecificHybrid", qualifiedByName = "mapStringYN")
	@Mapping(target = "isVarietalHybrid", source = "isVarietalHybrid", qualifiedByName = "mapStringYN")
	@Mapping(target = "isSubvarietalHybrid", source = "isSubvarietalHybrid", qualifiedByName = "mapStringYN")
	@Mapping(target = "isWebVisible", source = "isWebVisible", qualifiedByName = "mapStringYN")
	TaxonomySpeciesDTO map(TaxonomySpecies ts);

	@Mapping(target = "isFormaHybrid", source = "isFormaHybrid", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isNamePending", source = "isNamePending", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isSpecificHybrid", source = "isSpecificHybrid", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isSubspecificHybrid", source = "isSubspecificHybrid", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isVarietalHybrid", source = "isVarietalHybrid", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isSubvarietalHybrid", source = "isSubvarietalHybrid", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isWebVisible", source = "isWebVisible", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "taxonomyCrops", ignore = true)
	TaxonomySpecies map(TaxonomySpeciesDTO dto);

	@Mapping(target = "hybridCode", expression = "java(ts.getTaxonomyGenus() == null ? null : ts.getTaxonomyGenus().getHybridCode())")
	@Mapping(target = "genusName", expression = "java(ts.getTaxonomyGenus() == null ? null : ts.getTaxonomyGenus().getGenusName())")
	@Mapping(source = "isSpecificHybrid", target = "isSpecificHybrid", qualifiedByName = "mapStringYN")
	@Mapping(source = "isSubspecificHybrid", target = "isSubspecificHybrid", qualifiedByName = "mapStringYN")
	@Mapping(target = "isCurrent", expression = "java(ts.getCurrentTaxonomySpecies() != null && Objects.equals(ts.getId(), ts.getCurrentTaxonomySpecies().getId()))")
	TaxonomySpeciesInfo mapInfo(TaxonomySpecies ts);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	@Mapping(source = "speciesName", target = "speciesName")
	@Mapping(source = "name", target = "name")
	@Mapping(source = "genusName", target = "taxonomyGenus", qualifiedByName = "taxonomyGenusFromGenusName")
	TaxonomySpecies mapInfo(TaxonomySpeciesInfo info);

	@Named("taxonomyGenusFromGenusName")
	default TaxonomyGenus taxonomyGenusFromGenusName(String genusName) {
		TaxonomyGenus genus = null;
		if (genusName != null) {
			genus = new TaxonomyGenus();
			genus.setGenusName(genusName);
		}
		return genus;
	}

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	@Mapping(source = "isDone", target = "isDone", qualifiedByName = "mapNullableBooleanYN")
	AccessionAction map(AccessionActionDTO dto);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "isDone", target = "isDone", qualifiedByName = "mapNullableStringYN")
	AccessionActionDTO map(AccessionAction accessionAction);

	@SubclassMapping(target = SysUser.class, source = SysUserDTO.class)
	@SubclassMapping(target = OAuthClient.class, source = OAuthClientDTO.class)
	@SubclassMapping(target = SysGroup.class, source = SysGroupDTO.class)
	@SubclassMapping(target = WebUser.class, source = WebUserDTO.class)
	@Mapping(target = "aclEntries", ignore = true)
	@Mapping(target = "objectIdentities", ignore = true)
	AclSid map(AclSidDTO aclSidDTO);

	@SubclassMapping(target = SysUserDTO.class, source = SysUser.class)
	@SubclassMapping(target = OAuthClientDTO.class, source = OAuthClient.class)
	@SubclassMapping(target = SysGroupDTO.class, source = SysGroup.class)
	@SubclassMapping(target = WebUserDTO.class, source = WebUser.class)
	@Mapping(target = "_class", source = ".", qualifiedByName = "getClassName")
	AclSidDTO map(AclSid aclSid);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	@Mapping(source = "sid", target = "sid")
	AclSid mapInfo (AclSidInfo aclSidInfo);

	AclSidInfo mapInfo (AclSid aclSid);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	AclClass mapInfo (AclClassInfo info);

	AclClassInfo mapInfo (AclClass aclSid);

	@Mapping(target = "parentObject.id", source = "parentObject")
	@Mapping(target = "aclEntries", ignore = true)
	AclObjectIdentity map(AclObjectIdentityDTO dto);

	@Mapping(source = "parentObject.id", target = "parentObject")
	@Mapping(source = "aclEntries", target = "aclEntries", qualifiedByName = "mapAclEntriesToPermissionsInfo")
	AclObjectIdentityDTO map(AclObjectIdentity aclObjectIdentity);

	@Mapping(source = "inherited", target = "inherited", qualifiedByName = "mapAclEntriesToPermissionsInfo")
	AclObjectIdentityExtDTO map(CustomAclService.AclObjectIdentityExt ext);

	@Mapping(target = "site", expression = "java(mapSiteById(securedActionDTO.getSiteId()))")
	@Mapping(target = "parentAction", expression = "java(mapParentActionById(securedActionDTO.getParentActionId()))")
	SecuredAction map(SecuredActionDTO securedActionDTO);

	default Site mapSiteById(Long siteId) {
		if (siteId == null) {
			return null;
		}
		Site site = new Site();
		site.setId(siteId);
		return site;
	}

	default SecuredAction mapParentActionById(Long parentActionId) {
		if (parentActionId == null) {
			return null;
		}
		SecuredAction securedAction = new SecuredAction();
		securedAction.setId(parentActionId);
		return securedAction;
	}

	@Mapping(source = "site.id", target = "siteId")
	@Mapping(source = "parentAction.id", target = "parentActionId")
	@Mapping(target = "_permissions", source = ".", qualifiedByName = "getPermissions")
	SecuredActionDTO map(SecuredAction securedAction);

	@Mapping(source = "site.id", target = "siteId")
	@Mapping(source = "parentAction.id", target = "parentActionId")
	SecuredActionDTO mapWithoutPermissions(SecuredAction securedAction);

	@Mapping(target = "sid.sid", source = "sid")
	SidPermissions map(SidPermissionsDTO dto);

	@Mapping(source = "isPublic", target = "isPublic")
	SidPermissionsDTO map(SidPermissions sidPermissions);

	@Mapping(source = "sid", target = "sid.sid")
	@Mapping(source = "isPublic", target = "isPublic")
	SidPermissions mapInfo(SidPermissionsInfo info);

	@Mapping(source = "sid.sid", target = "sid")
	@Mapping(source = "isPublic", target = "isPublic")
	SidPermissionsInfo mapInfo (SidPermissions sidPermissions);

	@Mapping(source = "isEnabled", target = "isEnabled", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "aclEntries", ignore = true)
	@Mapping(target = "objectIdentities", ignore = true)
	@Mapping(target = "password", ignore = true)
	@Mapping(target = "runtimeAuthorities", ignore = true)
	@Mapping(target = "authorities", ignore = true)
	SysUser map(SysUserDTO dto);

	@Mapping(source = "isEnabled", target = "isEnabled", qualifiedByName = "mapStringYN")
	@Mapping(source = "cooperator.email", target = "cooperatorEmail")
	@Mapping(source = ".", target = "_class", qualifiedByName = "getClassName")
	SysUserDTO map(SysUser user);

	@Mapping(source = "sysUserId", target = "sysUser.id")
	SysGroupUserMap map(SysGroupUserMapDTO dto);

	@Mapping(target = "sysUserId", source = "sysUser.id")
	SysGroupUserMapDTO map(SysGroupUserMap userGroupMap);

	@Mapping(source = "isEnabled", target = "isEnabled", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "aclEntries", ignore = true)
	@Mapping(target = "objectIdentities", ignore = true)
	SysGroup map(SysGroupDTO dto);

	@Mapping(target = "isEnabled", source = "isEnabled", qualifiedByName = "mapStringYN")
	@Mapping(target = "_class", source = ".", qualifiedByName = "getClassName")
	SysGroupDTO map(SysGroup sysGroup);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	SysGroup mapInfo(SysGroupInfo info);

	@Mapping(source = "isEnabled", target = "isEnabled", qualifiedByName = "mapStringYN")
	SysGroupInfo mapInfo(SysGroup group);

	@Mapping(source = "isEnabled", target = "isEnabled", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "aclEntries", ignore = true)
	@Mapping(target = "objectIdentities", ignore = true)
	@Mapping(target = "runtimeAuthorities", ignore = true)
	@Mapping(target = "authorities", ignore = true)
	WebUser map(WebUserDTO dto);

	@Mapping(source = "isEnabled", target = "isEnabled", qualifiedByName = "mapStringYN")
	@Mapping(source = ".", target = "_class", qualifiedByName = "getClassName")
	@Mapping(target = "password", ignore = true)
	WebUserDTO map(WebUser webUser);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	WebCooperator mapInfo(WebCooperatorInfo info);

	@Mapping(source = "isActive", target = "isActive", qualifiedByName = "mapStringYN")
	WebCooperatorInfo mapInfo(WebCooperator cooperator);

	@Mapping(source = "isActive", target = "isActive", qualifiedByName = "mapBooleanYN")
	WebCooperator map(WebCooperatorDTO dto);

	@Mapping(source = "isActive", target = "isActive", qualifiedByName = "mapStringYN")
	WebCooperatorDTO map(WebCooperator cooperator);

	@Mapping(source = "clientScopes", target = "scopes")
	OAuthClient map(OAuthClientDTO dto);

	@Mapping(source = "scopes", target = "clientScopes")
	@Mapping(source = "grantTypes", target = "authorizedGrantTypes")
	@Mapping(source = "grantTypes", target = "grantTypes")
	@Mapping(source = "redirectUris", target = "registeredRedirectUri")
	@Mapping(source = "redirectUris", target = "redirectUris")
	@Mapping(source = ".", target = "_class", qualifiedByName = "getClassName")
	@Mapping(target = "hasClientSecret", expression = "java(client.getClientSecret() != null)")
	OAuthClientDTO map(OAuthClient client);

	CooperatorDTO map(Cooperator c);

	Cooperator map(CooperatorDTO dto);

	@Mapping(target = "isCurrent", expression = "java(c.getCurrentCooperator() == null || Objects.equals(c.getId(), c.getCurrentCooperator().getId()))")
	@Mapping(target = "countryCode", source = "geography.countryCode")
	CooperatorInfo mapInfo(Cooperator c);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Cooperator mapInfo(CooperatorInfo info);

	AccessionInfo mapInfo(Accession a);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	@Mapping(target = "accessionNumber", source = "accessionNumber")
	@Mapping(target = "doi", source = "doi")
	@Mapping(target = "taxonomySpecies", source = "taxonomySpecies")
	@Mapping(target = "site", source = "site")
	Accession mapInfo(AccessionInfo info);

	MethodDTO map(Method method);

	Method map(MethodDTO dto);

	MethodInfo mapInfo(Method method);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Method mapInfo(MethodInfo info);

	@Mapping(target = "webVisible", source = "isWebVisible", qualifiedByName = "mapBooleanYN")
	AccessionActionService.AccessionActionRequest map(AccessionActionRequestDTO dto);

	@Mapping(target = "isWebVisible", source = "webVisible", qualifiedByName = "mapStringYN")
	AccessionActionRequestDTO map(AccessionActionService.AccessionActionRequest request);

	AccessionInvAnnotationDTO map(AccessionInvAnnotation annotation);

	AccessionInvAnnotation map(AccessionInvAnnotationDTO dto);

	@Mapping(target = "finalRecipient", source = "finalRecipientCooperator")
	OrderRequestInfo mapInfo(OrderRequest orderRequest);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	OrderRequest mapInfo(OrderRequestInfo info);

	@Mapping(source = ".", target = "willAutoDeduct", qualifiedByName = "willAutoDeduct")
	OrderRequestItemDTO map(OrderRequestItem orderRequestItem);

	@Named("willAutoDeduct")
	default Boolean willAutoDeduct(OrderRequestItem item) {
		if (item == null) return null;
		Inventory inventory = item.getInventory();
		if (inventory == null) return null;

		if (ORDER_REQUEST_ITEM_STATUS_SPLIT.value.equals(item.getStatusCode()) || ORDER_REQUEST_ITEM_STATUS_CANCEL.value.equals(item.getStatusCode()) 
			|| ORDER_REQUEST_ITEM_STATUS_SHIPPED.value.equals(item.getStatusCode())) {
			return null;
		}

		return
			// inventory is auto-deductible
			Strings.CS.equals(inventory.getIsAutoDeducted(), "Y")
				// item form type code matches inventory distribtion form code
				&& Strings.CS.equals(item.getDistributionFormCode(), inventory.getFormTypeCode())
				// item units match inventory units
				&& areCompatibleUnits(item.getQuantityShippedUnitCode(), inventory.getQuantityOnHandUnitCode(), inventory.getHundredSeedWeight())
			;
	}

	default boolean areCompatibleUnits(String unitCode1, String unitCode2, Double hundredSeedWeight) {
		if (unitCode1 == null || unitCode2 == null) return false;
		if (Strings.CS.equals(unitCode1, unitCode2)) return true;

		if (SEED_UNITS.contains(unitCode1)) {
			if (SEED_UNITS.contains(unitCode2)) return true;
			if (GRAM_UNITS.contains(unitCode2) && hundredSeedWeight != null && hundredSeedWeight != 0d) return true;
		} else if (GRAM_UNITS.contains(unitCode1)) {
			if (GRAM_UNITS.contains(unitCode2)) return true;
			if (SEED_UNITS.contains(unitCode2) && hundredSeedWeight != null && hundredSeedWeight != 0d) return true;
		}
		return false;
	}
	
	@Mapping(target = "actions", ignore = true)
	@Mapping(target = "webOrderRequestItem", ignore = true)
	@Mapping(target = "pathogens", ignore = true)
	OrderRequestItem map(OrderRequestItemDTO dto);

	@Mapping(target = "inventoryNumber", source = "inventory.inventoryNumber")
	@Mapping(target = "accessionNumber", source = "inventory.accession.accessionNumber")
	OrderRequestItemInfo mapInfo(OrderRequestItem orderRequestItem);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	OrderRequestItem mapInfo(OrderRequestItemInfo orderRequestItem);

	@Mapping(source = "isDone", target = "isDone", qualifiedByName = "mapNullableStringYN")
	OrderRequestActionDTO map(OrderRequestAction action);
	@Mapping(source = "isDone", target = "isDone", qualifiedByName = "mapNullableBooleanYN")
	OrderRequestAction map(OrderRequestActionDTO action);
	OrderRequestActionRequest map(OrderRequestActionRequestDTO dto);
	OrderRequestActionRequestDTO map(OrderRequestActionRequest dto);

	@Mapping(source = "isDone", target = "isDone", qualifiedByName = "mapNullableStringYN")
	OrderRequestItemActionDTO map(OrderRequestItemAction action);

	@Mapping(source = "isDone", target = "isDone", qualifiedByName = "mapNullableBooleanYN")
	OrderRequestItemAction map(OrderRequestItemActionDTO action);

	OrderRequestItemActionRequest map(OrderRequestItemActionRequestDTO dto);
	OrderRequestItemActionRequestDTO map(OrderRequestItemActionRequest dto);

	OrderRequestDetailsDTO map(OrderRequestService.OrderRequestDetails details);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	OrderRequestAttachDTO map(OrderRequestAttach attach);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	OrderRequestAttach map(OrderRequestAttachDTO dto);

	OrderRequestAttachmentService.OrderRequestAttachmentRequest map(OrderRequestAttachApiService.OrderRequestAttachmentRequestDTO metadata);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	AccessionInvAttachDTO map(AccessionInvAttach attach);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	AccessionInvAttach map(AccessionInvAttachDTO dto);

	@SubclassMapping(target = RepositoryImageDTO.class, source = RepositoryImage.class)
	@Mapping(source = "folder.path", target = "folder")
	@Mapping(target = "isPublic", expression = "java(SecurityContextUtil.anyoneHasPermission(repositoryFile, \"READ\"))")
	@Mapping(target = "isManage", expression = "java(SecurityContextUtil.hasRole(\"ADMINISTRATOR\") || SecurityContextUtil.hasPermission(repositoryFile, \"ADMINISTRATION\"))")
	RepositoryFileDTO map(RepositoryFile repositoryFile);

	@Named("mapUnproxiedRepositoryFile")
	default RepositoryFileDTO mapUnproxiedRepositoryFile(RepositoryFile repositoryFile) {
		repositoryFile = (RepositoryFile) Hibernate.unproxy(repositoryFile);
		return map(repositoryFile);
	}

	@SubclassMapping(target = RepositoryImage.class, source = RepositoryImageDTO.class)
	@Mapping(target = "apply", ignore = true)
	@Mapping(target = "folder", ignore = true)
	RepositoryFile map(RepositoryFileDTO dto);

	@Mapping(source = "folder.path", target = "folder")
	@Mapping(target = "isPublic", expression = "java(SecurityContextUtil.anyoneHasPermission(repositoryImage, \"READ\"))")
	@Mapping(target = "isManage", expression = "java(SecurityContextUtil.hasRole(\"ADMINISTRATOR\") || SecurityContextUtil.hasPermission(repositoryImage, \"ADMINISTRATION\"))")
	RepositoryImageDTO map(RepositoryImage repositoryImage);

	@Mapping(target = "apply", ignore = true)
	@Mapping(target = "folder", ignore = true)
	RepositoryImage map(RepositoryImageDTO dto);

	RepositoryFolderInfo mapInfo(RepositoryFolder repositoryFolder);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	RepositoryFolder mapInfo(RepositoryFolderInfo info) throws InvalidRepositoryPathException;

	@Mapping(target = "_permissions", source = ".", qualifiedByName = "getPermissions")
	RepositoryFolderDTO map(RepositoryFolder repositoryFolder);

	@Mapping(target = "files", ignore = true)
	@Mapping(target = "gallery", ignore = true)
	@Mapping(target = "parent", ignore = true)
	@Mapping(target = "parentOid", ignore = true)
	RepositoryFolder map(RepositoryFolderDTO dto) throws InvalidRepositoryPathException;

	@Mapping(target = "_permissions", source = ".", qualifiedByName = "getPermissions")
	@Mapping(source = "folder.path", target = "folder")
	ImageGalleryDTO map(ImageGallery imageGallery);

	@Mapping(target = "folder", ignore = true)
	ImageGallery map(ImageGalleryDTO dto);

	RepositoryImageInfo mapInfo(RepositoryImage repositoryImage);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	RepositoryImage mapInfo(RepositoryImageInfo info);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "members", ignore = true)
	AccessionInvGroup map(AccessionInvGroupDTO dto);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	AccessionInvGroupDTO map(AccessionInvGroup group);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	AccessionInvGroup mapInfo(AccessionInvGroupInfo info);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	AccessionInvGroupInfo mapInfo(AccessionInvGroup group);

	AccessionInvGroupMap map(AccessionInvGroupMapDTO dto);
	AccessionInvGroupMapDTO map(AccessionInvGroupMap map);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	AccessionInvNameDTO map(AccessionInvName invName);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	AccessionInvName map(AccessionInvNameDTO dto);

	NameGroupDTO map(NameGroup nameGroup);
	NameGroup map(NameGroupDTO dto);

	NameGroupInfo mapInfo(NameGroup nameGroup);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	NameGroup mapInfo(NameGroupInfo info);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	AccessionInvNameInfo mapInfo(AccessionInvName invName);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	AccessionInvName mapInfo(AccessionInvNameInfo info);

	AccessionIpr map(AccessionIprDTO dto);

	AccessionIprDTO map(AccessionIpr accessionIpr);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	AccessionIpr mapInfo(AccessionIprInfo info);

	AccessionIprInfo mapInfo(AccessionIpr accessionIpr);

	AccessionPedigree map(AccessionPedigreeDTO dto);

	AccessionPedigreeDTO map(AccessionPedigree accessionPedigree);

	AccessionPedigreeInfo mapInfo(AccessionPedigree accessionPedigree);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	AccessionPedigree mapInfo(AccessionPedigreeInfo info);

	AccessionQuarantineDTO map(AccessionQuarantine accessionQuarantine);

	AccessionQuarantine map(AccessionQuarantineDTO dto);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "isOrigin", target = "isOrigin", qualifiedByName = "mapStringYN")
	AccessionSourceDTO map(AccessionSource accessionSource);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	@Mapping(source = "isOrigin", target = "isOrigin", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "cooperators", ignore = true)
	@Mapping(target = "sourceDescObservations", ignore = true)
	AccessionSource map(AccessionSourceDTO dto);

	@Mapping(source = "isValid", target = "isValid", qualifiedByName = "mapStringYN")
	@Mapping(target = "isCurrent", expression = "java(g.getCurrentGeography() == null || Objects.equals(g.getId(), g.getCurrentGeography().getId()))")
	GeographyInfo mapInfo(Geography g);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Geography mapInfo(GeographyInfo dto);

	@Mapping(source = "isOrigin", target = "isOrigin", qualifiedByName = "mapStringYN")
	AccessionSourceInfo mapInfo(AccessionSource accessionSource);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	@Mapping(source = "accession", target = "accession")
	AccessionSource mapInfo(AccessionSourceInfo dto);

	AccessionSourceMapDTO map(AccessionSourceMap accessionSourceMap);

	AccessionSourceMap map(AccessionSourceMapDTO dto);

	AcquisitionDTO map(AccessionService.AcquisitionData acquisitionData);

	AccessionService.AcquisitionData map(AcquisitionDTO info);

	@Mapping(source = "isAcceptedName", target = "isAcceptedName", qualifiedByName = "mapStringYN")
	CitationDTO map(Citation citation);

	@Mapping(source = "isAcceptedName", target = "isAcceptedName", qualifiedByName = "mapBooleanYN")
	Citation map(CitationDTO dto);

	@Mapping(target = "literatureId", source = "literature.id")
	@Mapping(source = "isAcceptedName", target = "isAcceptedName", qualifiedByName = "mapStringYN")
	CitationInfo mapInfo(Citation citation);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	@Mapping(source = "literatureId", target = "literature.id")
	Citation mapInfo(CitationInfo info);

	GeneticMarkerInfo mapInfo(GeneticMarker geneticMarker);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	GeneticMarker mapInfo(GeneticMarkerInfo info);

	LiteratureDTO map(Literature literature);

	Literature map(LiteratureDTO literatureDTO);

	LiteratureInfo mapInfo(Literature literature);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Literature mapInfo(LiteratureInfo info);

	TaxonomyFamilyDTO map(TaxonomyFamily tf);

	TaxonomyFamily map(TaxonomyFamilyDTO dto);

	TaxonomyFamilyInfo mapInfo(TaxonomyFamily taxonomyFamily);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	TaxonomyFamily mapInfo(TaxonomyFamilyInfo info);

	@Mapping(target = "isWebVisible", source = "isWebVisible", qualifiedByName = "mapStringYN")
	TaxonomyGenusDTO map(TaxonomyGenus tg);

	@Mapping(target = "isWebVisible", source = "isWebVisible", qualifiedByName = "mapBooleanYN")
	TaxonomyGenus map(TaxonomyGenusDTO dto);

	@Mapping(target = "familyName", source = "taxonomyFamily.familyName")
	@Mapping(target = "subfamilyName", source = "taxonomyFamily.subfamilyName")
	@Mapping(target = "tribeName", source = "taxonomyFamily.tribeName")
	@Mapping(target = "subtribeName", source = "taxonomyFamily.subtribeName")
	TaxonomyGenusInfo mapInfo(TaxonomyGenus genus);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	TaxonomyGenus mapInfo(TaxonomyGenusInfo info);

	@Mapping(source = "isRestrictedByInventory", target = "isRestrictedByInventory", qualifiedByName = "mapStringYN")
	FeedbackDTO map(Feedback feedback);

	@Mapping(source = "isRestrictedByInventory", target = "isRestrictedByInventory", qualifiedByName = "mapBooleanYN")
	Feedback map(FeedbackDTO dto);

	@Mapping(source = "isRestrictedByInventory", target = "isRestrictedByInventory", qualifiedByName = "mapStringYN")
	FeedbackInfo mapInfo(Feedback feedback);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Feedback mapInfo(FeedbackInfo dto);

	@Mapping(target = "currentGeographyId", expression = "java(g.getCurrentGeography() == null ? null : g.getCurrentGeography().getId())")
	@Mapping(source = "isValid", target = "isValid", qualifiedByName = "mapStringYN")
	GeographyDTO map(Geography g);

	@Mapping(source = "currentGeographyId", target = "currentGeography", qualifiedByName = "currentGeography")
	@Mapping(source = "isValid", target = "isValid", qualifiedByName = "mapBooleanYN")
	Geography map(GeographyDTO dto);

	@Named("currentGeography")
	default Geography currentGeography(Long currentGeographyId) {
		return currentGeographyId == null ? null : new Geography(currentGeographyId);
	}

	@Mapping(target = "isAutoDeducted", source = "isAutoDeducted", qualifiedByName = "mapStringYN")
	@Mapping(target = "isAvailable", source = "isAvailable", qualifiedByName = "mapStringYN")
	@Mapping(target = "isDistributable", source = "isDistributable", qualifiedByName = "mapStringYN")
	@Mapping(target = "parentInventoryId", source = "parentInventory.id")
	@Mapping(target = "parentInventoryNumber", source = "parentInventory.inventoryNumber")
	@Mapping(target = "isSystemInventory", source = "systemInventory")
	InventoryDTO map(Inventory i);

	@Mapping(target = "isAutoDeducted", source = "isAutoDeducted", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isAvailable", source = "isAvailable", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isDistributable", source = "isDistributable", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "parentInventory", source = "parentInventoryId", qualifiedByName = "parentInventory")
	@Mapping(target = "actions", ignore = true)
	@Mapping(target = "quality", ignore = true)
	@Mapping(target = "viability", ignore = true)
	@Mapping(target = "annotations", ignore = true)
	@Mapping(target = "names", ignore = true)
	@Mapping(target = "accessionInvGroupMaps", ignore = true)
	@Mapping(target = "attachments", ignore = true)
	Inventory map(InventoryDTO dto);

	@Mapping(target = "accessionNumber", source="accession.accessionNumber")
	@Mapping(target = "accessionId", source="accession.id")
	@Mapping(target = "taxonomySpecies", source="accession.taxonomySpecies")
	@Mapping(target = "curationTypeCode", source="accession.curationTypeCode")
	@Mapping(target = "siteShortName", source="site.siteShortName")
	@Mapping(target = "siteId", source="site.id")
	@Mapping(target = "isAvailable", source = "isAvailable", qualifiedByName = "mapStringYN")
	@Mapping(target = "isDistributable", source = "isDistributable", qualifiedByName = "mapStringYN")
	InventoryInfo mapInfo(Inventory inventory);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Inventory mapInfo(InventoryInfo info);

	InventoryDetailsDTO map(InventoryService.InventoryDetails details);

	InventoryService.InventoryQuantityRequest map(InventoryApiService.InventoryQuantityRequestDTO requestDTO);

	InventoryService.AssignLocationRequest map(InventoryApiService.AssignLocationRequestDTO assignLocationRequest);

	InventoryApiService.ComparedSitesResponseDTO map(InventoryService.ComparedSitesResponse comparedSitesResponse);

	InventoryService.SplitInventoryRequest map(InventoryApiService.SplitInventoryRequestDTO request);

	InventoryApiService.AggregatedInventoryQuantityDTO map(InventoryRepositoryCustom.AggregatedInventoryQuantity aggregatedInventoryQuantity);

	@Mapping(target = "isDone", source = "isDone", qualifiedByName = "mapNullableBooleanYN")
	InventoryAction map(InventoryActionDTO dto);

	@Mapping(target = "isDone", source = "isDone", qualifiedByName = "mapNullableStringYN")
	InventoryActionDTO map(InventoryAction action);
	InventoryActionRequest map(InventoryActionRequestDTO dto);
	InventoryActionRequestDTO map(InventoryActionRequest request);

	@Named("parentInventory")
	default Inventory parentInventory(Long parentInventoryId) {
		return parentInventoryId == null ? null : new Inventory(parentInventoryId);
	}

	default InventoryExtraDTO map(InventoryExtra model) {
		if (model instanceof TissueCultureExtra)
			return map((TissueCultureExtra) model);
		else if (model instanceof SeedInventoryExtra) {
			return map((SeedInventoryExtra) model);
		} else if (model == null) {
			return null; // Handle null
		} else {
			throw new RuntimeException("Invalid extra type!");
		}
	}

	default InventoryExtra map(InventoryExtraDTO model) {
		if (model instanceof InventoryExtraDTO.TissueCultureExtraDTO)
			return map((InventoryExtraDTO.TissueCultureExtraDTO) model);
		else if (model instanceof InventoryExtraDTO.SeedInventoryExtraDTO) {
			return map((InventoryExtraDTO.SeedInventoryExtraDTO) model);
		} else if (model == null) {
			return null; // Handle null
		} else {
			throw new RuntimeException("Invalid extra DTO type!");
		}
	}

	@Mapping(target = "inventoryId", source = "inventory.id")
	InventoryExtraDTO.TissueCultureExtraDTO map(TissueCultureExtra tissueCulture);

	@Mapping(target = "inventory.id", source = "inventoryId")
	TissueCultureExtra map(InventoryExtraDTO.TissueCultureExtraDTO dto);
	
	@Mapping(target = "inventoryId", source = "inventory.id")
	InventoryExtraDTO.SeedInventoryExtraDTO map(SeedInventoryExtra seedInventory);

	@Mapping(target = "inventory.id", source = "inventoryId")
	SeedInventoryExtra map(InventoryExtraDTO.SeedInventoryExtraDTO dto);

	@Mapping(source = "isAvailable", target = "isAvailable", qualifiedByName = "mapStringYN")
	MaterielDTO map(Materiel materiel);

	@Mapping(source = "isAvailable", target = "isAvailable", qualifiedByName = "mapBooleanYN")
	Materiel map(MaterielDTO dto);

	@Mapping(source = "isAvailable", target = "isAvailable", qualifiedByName = "mapStringYN")
	MaterielInfo mapInfo(Materiel materiel);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Materiel mapInfo(MaterielInfo info);

	@Mapping(source = "isAutoDeducted", target = "isAutoDeducted", qualifiedByName = "mapStringYN")
	InventoryMaintenancePolicyDTO map(InventoryMaintenancePolicy policy);

	@Mapping(source = "isAutoDeducted", target = "isAutoDeducted", qualifiedByName = "mapBooleanYN")
	InventoryMaintenancePolicy map(InventoryMaintenancePolicyDTO dto);

	@Mapping(source = "isAutoDeducted", target = "isAutoDeducted", qualifiedByName = "mapStringYN")
	InventoryMaintenancePolicyInfo mapInfo(InventoryMaintenancePolicy policy);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	InventoryMaintenancePolicy mapInfo(InventoryMaintenancePolicyInfo info);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	InventoryViabilityRule mapInfo(InventoryViabilityRuleInfo info);

	@Mapping(target = "actions", ignore = true)
	@Mapping(target = "datas", ignore = true)
	@Mapping(target = "durationInDays", ignore = true)
	InventoryViability map(InventoryViabilityDTO viability);

	InventoryViabilityDTO map(InventoryViability viability);

	@Mapping(source = "inventoryViability", target = "initialViability", qualifiedByName = "mapInitialViability")
	@Mapping(source = "inventoryViability", target = "initialViabilityDate", qualifiedByName = "mapInitialViabilityDate")
	@Mapping(source = "inventoryViability", target = "initialViabilityDateCode", qualifiedByName = "mapInitialViabilityDateCode")
	InventoryViabilityDetailsDTO map(InventoryViabilityDetails details);

	@Named("mapInitialViability")
	default Double mapInitialViability(InventoryViability inventoryViability) {
		Double initViability = null;
		if (inventoryViability != null && inventoryViability.getInventory() != null) {
			var extra = inventoryViability.getInventory().getExtra();
			if (extra instanceof SeedInventoryExtra) {
				initViability = ((SeedInventoryExtra)extra).getInitialViability();
			}
		}
		return initViability;
	}

	@Named("mapInitialViabilityDate")
	default Date mapInitialViabilityDate(InventoryViability inventoryViability) {
		Date initViabilityDate = null;
		if (inventoryViability != null && inventoryViability.getInventory() != null) {
			var extra = inventoryViability.getInventory().getExtra();
			if (extra instanceof SeedInventoryExtra) {
				initViabilityDate = ((SeedInventoryExtra)extra).getInitialViabilityDate();
			}
		}
		return initViabilityDate;
	}

	@Named("mapInitialViabilityDateCode")
	default String mapInitialViabilityDateCode(InventoryViability inventoryViability) {
		String initViabilityDateCode = null;
		if (inventoryViability != null && inventoryViability.getInventory() != null) {
			var extra = inventoryViability.getInventory().getExtra();
			if (extra instanceof SeedInventoryExtra) {
				initViabilityDateCode = ((SeedInventoryExtra)extra).getInitialViabilityDateCode();
			}
		}
		return initViabilityDateCode;
	}

	InventoryViabilityRuleMapDTO map(InventoryViabilityRuleMap inventoryViabilityRuleMap);
	InventoryViabilityRuleMap map(InventoryViabilityRuleMapDTO dto);

	@Mapping(target = "ruleMaps", ignore = true)
	InventoryViabilityRule map(InventoryViabilityRuleDTO source);

	InventoryViabilityRuleDTO map(InventoryViabilityRule source);

	InventoryViabilityInfo mapInfo(InventoryViability viability);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	InventoryViability mapInfo(InventoryViabilityInfo info);

	InventoryViabilityActionRequestDTO map(InventoryViabilityActionRequest dto);
	InventoryViabilityActionRequest map(InventoryViabilityActionRequestDTO dto);

	@Mapping(source = "isDone", target = "isDone", qualifiedByName = "mapNullableStringYN")
	InventoryViabilityActionDTO map(InventoryViabilityAction action);

	@Mapping(source = "isDone", target = "isDone", qualifiedByName = "mapNullableBooleanYN")
	InventoryViabilityAction map(InventoryViabilityActionDTO dto);

	@Mapping(target = "dataEnvironmentMaps", ignore = true)
	@Mapping(target = "orderRequestItem", ignore = true) // FIXME re-enable
	InventoryViabilityData map(InventoryViabilityDataDTO dto);

	InventoryViabilityDataDTO map(InventoryViabilityData viabilityData);

	InventoryViabilityEnvironmentDTO map(InventoryViabilityEnvironment environment);
	InventoryViabilityEnvironment map(InventoryViabilityEnvironmentDTO dto);

	InventoryViabilityEnvironmentInfo mapInfo(InventoryViabilityEnvironment environment);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	InventoryViabilityEnvironment mapInfo(InventoryViabilityEnvironmentInfo info);

	InventoryViabilityDataInfo mapInfo(InventoryViabilityData data);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	InventoryViabilityData mapInfo(InventoryViabilityDataInfo info);

	InventoryViabilityDataEnvironmentMapDTO map(InventoryViabilityDataEnvironmentMap map);
	InventoryViabilityDataEnvironmentMap map(InventoryViabilityDataEnvironmentMapDTO dto);

	InventoryQualityStatusDTO map(InventoryQualityStatus map);
	@Mapping(target = "attachments", ignore = true)
	InventoryQualityStatus map(InventoryQualityStatusDTO dto);

	InventoryQualityStatusInfo mapInfo(InventoryQualityStatus map);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	InventoryQualityStatus mapInfo(InventoryQualityStatusInfo dto);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	InventoryQualityStatusAttachDTO map(InventoryQualityStatusAttach attach);
	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	InventoryQualityStatusAttach map(InventoryQualityStatusAttachDTO dto);
	InventoryQualityStatusAttachmentService.InventoryQualityStatusAttachmentRequest map(InventoryQualityStatusAttachmentApiService.InventoryQualityStatusAttachRequestDTO request);

	OrderRequestDTO map(OrderRequest orderRequest);

	@Mapping(target = "actions", ignore = true)
	@Mapping(target = "attachments", ignore = true)
	@Mapping(target = "lastCompletedAction", ignore = true)
	@Mapping(target = "orderRequestItems", ignore = true)
	OrderRequest map(OrderRequestDTO dto);

	WebOrderRequestInfo mapInfo(WebOrderRequest webOrderRequest);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	WebOrderRequest mapInfo(WebOrderRequestInfo info);

	RepositoryFileInfo mapInfo(RepositoryFile repositoryFile);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	RepositoryFile mapInfo(RepositoryFileInfo info);

	@Mapping(target = "classPk", expression = "java(al.getClassPk() == null ? null : al.getClassPk().getClassname())")
	@Mapping(target = "referencedEntity", expression = "java(al.getReferencedEntity() == null ? null : al.getReferencedEntity().getClassname())")
	AuditLogDTO map(AuditLog al);

	@Mapping(source = "classPk", target = "classPk", qualifiedByName = "classPkFromName")
	@Mapping(source = "referencedEntity", target = "referencedEntity", qualifiedByName = "classPkFromName")
	AuditLog map(AuditLogDTO from);

	@Named("classPkFromName")
	default ClassPK classPkFromName(String classname) {
		ClassPK classPK = null;
		if (classname != null) {
			classPK = new ClassPK();
			classPK.setClassname(classname);
		}
		return classPK;
	}

	SourceDescObservationDTO map(SourceDescObservation sourceDescObservation);

	SourceDescObservation map(SourceDescObservationDTO dto);

	@Mapping(source = "isCoded", target = "isCoded", qualifiedByName = "mapStringYN")
	SourceDescriptorInfo mapInfo(SourceDescriptor sourceDescriptor);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	SourceDescriptor mapInfo(SourceDescriptorInfo sourceDescriptor);

	SourceDescriptorCodeInfo mapInfo(SourceDescriptorCode sourceDescriptorCode);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	@Mapping(source = "code", target = "code")
	SourceDescriptorCode mapInfo(SourceDescriptorCodeInfo info);

	SourceDescriptorCodeDTO map(SourceDescriptorCode sourceDescriptorCode);

	@Mapping(target = "langs", ignore = true)
	SourceDescriptorCode map(SourceDescriptorCodeDTO dto);

	@Mapping(source = "isCoded", target = "isCoded", qualifiedByName = "mapStringYN")
	SourceDescriptorDTO map(SourceDescriptor sourceDescriptor);

	@Mapping(source = "isCoded", target = "isCoded", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "langs", ignore = true)
	SourceDescriptor map(SourceDescriptorDTO dto);

	@Mapping(source = "translatedSourceDescriptor", target = "sourceDescriptor")
	@Mapping(source = "translatedSourceDescriptorCode", target = "sourceDescriptorCode")
	@Mapping(source = "observation.numericValue", target = "numericValue")
	@Mapping(source = "observation.originalValue", target = "originalValue")
	@Mapping(source = "observation.stringValue", target = "stringValue")
	@Mapping(source = "observation.note", target = "note")
	@Mapping(source = "observation.accessionSource", target = "accessionSource")
	@Mapping(source = "observation.ownedBy", target = "ownedBy")
	@Mapping(source = "observation.ownedDate", target = "ownedDate")
	@Mapping(source = "observation.createdBy", target = "createdBy")
	@Mapping(source = "observation.createdDate", target = "createdDate")
	@Mapping(source = "observation.modifiedBy", target = "modifiedBy")
	@Mapping(source = "observation.modifiedDate", target = "modifiedDate")
	TranslatedSourceDescObservationDTO map(SourceDescObservationService.TranslatedSourceDescObservation tsdo);

	TranslatedSourceDescriptorCodeDTO map(SourceDescriptorCodeTranslationService.TranslatedSourceDescriptorCode tsdc);

	@Mapping(target = "id", ignore = true)
	SourceDescriptorCodeTranslationService.TranslatedSourceDescriptorCode map(TranslatedSourceDescriptorCodeDTO dto);

	TranslatedSourceDescriptorCodeInfo mapInfo(SourceDescriptorCodeTranslationService.TranslatedSourceDescriptorCode tsdc);

	@Mapping(target = "id", ignore = true)
	SourceDescriptorCodeTranslationService.TranslatedSourceDescriptorCode mapInfo(TranslatedSourceDescriptorCodeInfo info);

	TranslatedSourceDescriptorDTO map(SourceDescriptorTranslationService.TranslatedSourceDescriptor tsd);

	@Mapping(target = "id", ignore = true)
	@Mapping(target = "codes", ignore = true)
	SourceDescriptorTranslationService.TranslatedSourceDescriptor map(TranslatedSourceDescriptorDTO dto);

	TranslatedSourceDescriptorInfo mapInfo(SourceDescriptorTranslationService.TranslatedSourceDescriptor tsd);

	@Mapping(target = "id", ignore = true)
	@Mapping(target = "codes", ignore = true)
	SourceDescriptorTranslationService.TranslatedSourceDescriptor mapInfo(TranslatedSourceDescriptorInfo info);

	@Mapping(source = "isEnabled", target = "isEnabled", qualifiedByName = "mapStringYN")
	SysLangDTO map(SysLang sysLang);

	@Mapping(source = "isEnabled", target = "isEnabled", qualifiedByName = "mapBooleanYN")
	SysLang map(SysLangDTO dto);

	SourceDescriptorLangDTO map(SourceDescriptorLang lang);

	SourceDescriptorLang map(SourceDescriptorLangDTO dto);

	SourceDescriptorCodeLangDTO map(SourceDescriptorCodeLang lang);

	SourceDescriptorCodeLang map(SourceDescriptorCodeLangDTO dto);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	SysLang mapInfo(SysLangInfo sysLang);

	@Mapping(source = "isEnabled", target = "isEnabled", qualifiedByName = "mapStringYN")
	SysLangInfo mapInfo(SysLang sysLang);

	@Mapping(target = "subscribers", ignore = true)
	@Mapping(target = "isActive", source = "isActive", qualifiedByName = "mapBooleanYN")
	NotificationSchedule map(NotificationScheduleDTO dto);

	@Mapping(target = "isActive", source = "isActive", qualifiedByName = "mapStringYN")
	NotificationScheduleDTO map(NotificationSchedule source);

	@Mapping(target = "sid", source = "sid.sid")
	@Mapping(target = "isPrincipal", source = "sid.principal")
	NotificationScheduleSubscriberInfo mapInfo(NotificationScheduleSubscriber scheduleSubscriber);

	// @BeanMapping(ignoreByDefault = true)
	// @Mapping(target = "sid.sid", source = "sid")
	// NotificationScheduleSubscriber mapInfo(NotificationScheduleSubscriberInfo info);

	@Mapping(target = "isArchived", source = "isArchived", qualifiedByName = "mapStringYN")
	@Mapping(target = "isCoded", source = "isCoded", qualifiedByName = "mapStringYN")
	CropTraitInfo mapInfo(CropTrait ct);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	CropTrait mapInfo(CropTraitInfo info);

	CropTraitCodeInfo mapInfo(CropTraitCode ct);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	CropTraitCode mapInfo(CropTraitCodeInfo info);

	// @Mapping(target = "num", expression = "java(cto.getCropTraitCode() == null ? (cto.getNumericValue() == null ? null : cto.getNumericValue()) : cto.getCropTraitCode().getId())")
	@Mapping(target = "m", source = "method.id")
	@Mapping(target = "num", source = "numericValue")
	@Mapping(target = "str", expression = "java(cto.getCropTraitCode() == null ? cto.getStringValue() : cto.getCropTraitCode().getCode())")
	CropTraitObservationValue mapValue(CropTraitObservation cto);

	@Mapping(target = "isArchived", source = "isArchived", qualifiedByName = "mapStringYN")
	CropTraitObservationDTO map(CropTraitObservation cto);

	@Mapping(target = "observationData", ignore = true)
	@Mapping(target = "isArchived", source = "isArchived", qualifiedByName = "mapBooleanYN")
	CropTraitObservation map(CropTraitObservationDTO dto);

	@Mapping(target = "isArchived", source = "isArchived", qualifiedByName = "mapStringYN")
	CropTraitObservationInfo mapInfo(CropTraitObservation cto);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	CropTraitObservation mapInfo(CropTraitObservationInfo dto);

	@Mapping(target = "id", source = "observation.id")
	@Mapping(target = "inventory", source = "observation.inventory")
	@Mapping(target = "method", source = "observation.method")
	@Mapping(target = "cropTrait", source = "translatedCropTrait")
	@Mapping(target = "cropTraitCode", source = "translatedCropTraitCode")
	@Mapping(target = "isArchived", source = "observation.isArchived")
	@Mapping(target = "rank", source = "observation.rank")
	@Mapping(target = "frequency", source = "observation.frequency")
	@Mapping(target = "dataQualityCode", source = "observation.dataQualityCode")
	@Mapping(target = "originalValue", source = "observation.originalValue")
	@Mapping(target = "stringValue", source = "observation.stringValue")
	@Mapping(target = "numericValue", source = "observation.numericValue")
	@Mapping(target = "sampleSize", source = "observation.sampleSize")
	@Mapping(target = "minimumValue", source = "observation.minimumValue")
	@Mapping(target = "maximumValue", source = "observation.maximumValue")
	@Mapping(target = "meanValue", source = "observation.meanValue")
	@Mapping(target = "standardDeviation", source = "observation.standardDeviation")
	@Mapping(target = "note", source = "observation.note")
	@Mapping(target = "ownedBy", source = "observation.ownedBy")
	@Mapping(target = "ownedDate", source = "observation.ownedDate")
	@Mapping(target = "createdBy", source = "observation.createdBy")
	@Mapping(target = "createdDate", source = "observation.createdDate")
	@Mapping(target = "modifiedBy", source = "observation.modifiedBy")
	@Mapping(target = "modifiedDate", source = "observation.modifiedDate")
//	@Mapping(target = "isArchived", source = "observation.isArchived", qualifiedByName = "mapStringYN")
	TranslatedCropTraitObservationDTO map(TranslatedCropTraitObservation tcto);

	TranslatedCropTraitCodeDTO map(CropTraitCodeTranslationService.TranslatedCropTraitCode tsdc);

	@Mapping(target = "id", ignore = true)
	@Mapping(target = "entity.langs", ignore = true)
	CropTraitCodeTranslationService.TranslatedCropTraitCode map(TranslatedCropTraitCodeDTO dto);

	TranslatedCropTraitCodeInfo mapInfo(CropTraitCodeTranslationService.TranslatedCropTraitCode tsdc);

	@Mapping(target = "id", ignore = true)
	CropTraitCodeTranslationService.TranslatedCropTraitCode mapInfo(TranslatedCropTraitCodeInfo info);

	TranslatedCropTraitDTO map(CropTraitTranslationService.TranslatedCropTrait tsd);

	@Mapping(target = "id", ignore = true)
	@Mapping(target = "codes", ignore = true)
	@Mapping(target = "attachments", ignore = true)
	CropTraitTranslationService.TranslatedCropTrait map(TranslatedCropTraitDTO dto);

	TranslatedCropTraitInfo mapInfo(CropTraitTranslationService.TranslatedCropTrait tsd);

	@Mapping(target = "id", ignore = true)
	@Mapping(target = "codes", ignore = true)
	@Mapping(target = "attachments", ignore = true)
	CropTraitTranslationService.TranslatedCropTrait mapInfo(TranslatedCropTraitInfo info);

	@Mapping(target = "isArchived", source = "isArchived", qualifiedByName = "mapStringYN")
	@Mapping(target = "isCoded", source = "isCoded", qualifiedByName = "mapStringYN")
	@Mapping(target = "isPeerReviewed", source = "isPeerReviewed", qualifiedByName = "mapStringYN")
	CropTraitDTO map(CropTrait lang);

	@Mapping(target = "codes", ignore = true)
	@Mapping(target = "attachments", ignore = true)
	@Mapping(target = "langs", ignore = true)
	@Mapping(target = "isArchived", source = "isArchived", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isCoded", source = "isCoded", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isPeerReviewed", source = "isPeerReviewed", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "cropTraitObservations", ignore = true) // ALWAYS IGNORE
	CropTrait map(CropTraitDTO dto);

	CropTraitLangDTO map(CropTraitLang lang);

	CropTraitLang map(CropTraitLangDTO dto);

	CropTraitCodeDTO map(CropTraitCode lang);

	@Mapping(target = "langs", ignore = true)
	CropTraitCode map(CropTraitCodeDTO dto);

	CropTraitCodeLangDTO map(CropTraitCodeLang lang);

	CropTraitCodeLang map(CropTraitCodeLangDTO dto);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	CropTraitCodeAttach map(CropTraitCodeAttachDTO source);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	CropTraitCodeAttachDTO map(CropTraitCodeAttach attach);

	CropTraitCodeAttachmentService.CropTraitCodeAttachmentRequest map(CropTraitCodeAttachmentApiService.CropTraitCodeAttachmentRequestDTO metadata);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	CropTraitAttachDTO map(CropTraitAttach attach);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	CropTraitAttach map(CropTraitAttachDTO dto);

	CropTraitAttachmentService.CropTraitAttachmentRequest map(CropTraitAttachmentApiService.CropTraitAttachmentRequestDTO metadata);

	CropTraitObservationService.EnsureObservationsRequest map(CropTraitObservationApiService.EnsureObservationsRequest request);

	CropTraitObservationApiService.FilteredObservations map(CropTraitObservationService.FilteredObservations filteredObservations);

	CropTraitObservationData map(CropTraitObservationDataDTO source);

	CropTraitObservationDataDTO map(CropTraitObservationData source);

	CropTraitObservationDataApiService.FilteredObservationsData map(CropTraitObservationDataService.FilteredObservationsData data);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	@Mapping(target = "name", source = "name")
	Crop mapInfo(CropInfo info);

	CropInfo mapInfo(Crop info);

	CropAttachmentApiService.CropAttachmentRequestDTO map(CropAttachmentService.CropAttachmentRequest request);

	CropAttachmentService.CropAttachmentRequest map(CropAttachmentApiService.CropAttachmentRequestDTO dto);

	CropApiService.CropDetails map(CropService.CropDetails cropDetails);

	@Mapping(source = "isActive", target = "isActive", qualifiedByName = "mapStringYN")
	WorkflowDTO map(Workflow workflow);

	@Mapping(source = "isActive", target = "isActive", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "steps", ignore = true)
	@Mapping(target = "transitions", ignore = true) // Handled in service facade
	Workflow map(WorkflowDTO workflow);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	Workflow mapInfo(WorkflowInfo workflowInfo);

	@Mapping(source = "isActive", target = "isActive", qualifiedByName = "mapStringYN")
	WorkflowInfo mapInfo(Workflow workflow);

	default WorkflowStepDTO map(WorkflowStep model) {
		model = (WorkflowStep) Hibernate.unproxy(model);
		if (model instanceof WorkflowStartStep) {
			return map((WorkflowStartStep) model);
		} else if (model instanceof WorkflowEndStep) {
			return map((WorkflowEndStep) model);
		} else if (model instanceof WorkflowActionStep) {
			return map((WorkflowActionStep) model);
		} else if (model == null) {
			return null; // Handle null
		} else {
			throw new RuntimeException("Invalid workflow step!");
		}
	}

	default WorkflowStep map(WorkflowStepDTO model) {
		if (model instanceof WorkflowStepDTO.WorkflowStartStepDTO) {
			return map((WorkflowStepDTO.WorkflowStartStepDTO) model);
		} else if (model instanceof WorkflowStepDTO.WorkflowEndStepDTO) {
			return map((WorkflowStepDTO.WorkflowEndStepDTO) model);
		} else if (model instanceof WorkflowStepDTO.WorkflowActionStepDTO) {
			return map((WorkflowStepDTO.WorkflowActionStepDTO) model);
		} else if (model == null) {
			return null; // Handle null
		} else {
			throw new RuntimeException("Invalid workflow step DTO type!");
		}
	}

	WorkflowStepDTO.WorkflowStartStepDTO map(WorkflowStartStep workflowStartStep);
	@Mapping(target = "asOrigin", ignore = true)
	@Mapping(target = "asTarget", ignore = true)
	WorkflowStartStep map(WorkflowStepDTO.WorkflowStartStepDTO dto);

	WorkflowStepDTO.WorkflowEndStepDTO map(WorkflowEndStep workflowStartStep);
	@Mapping(target = "asOrigin", ignore = true)
	@Mapping(target = "asTarget", ignore = true)
	WorkflowEndStep map(WorkflowStepDTO.WorkflowEndStepDTO dto);

	WorkflowStepDTO.WorkflowActionStepDTO map(WorkflowActionStep workflowStartStep);
	@Mapping(target = "asOrigin", ignore = true)
	@Mapping(target = "asTarget", ignore = true)
	WorkflowActionStep map(WorkflowStepDTO.WorkflowActionStepDTO dto);

	default WorkflowStepInfo mapInfo(WorkflowStep model) {
		model = (WorkflowStep) Hibernate.unproxy(model);
		if (model instanceof WorkflowStartStep) {
			return mapInfo((WorkflowStartStep) model);
		} else if (model instanceof WorkflowEndStep) {
			return mapInfo((WorkflowEndStep) model);
		} else if (model instanceof WorkflowActionStep) {
			return mapInfo((WorkflowActionStep) model);
		} else if (model == null) {
			return null; // Handle null
		} else {
			throw new RuntimeException("Invalid workflow step!");
		}
	}

	default WorkflowStep mapInfo(WorkflowStepInfo model) {
		if (model instanceof WorkflowStepInfo.WorkflowStartStepInfo) {
			return mapInfo((WorkflowStepInfo.WorkflowStartStepInfo) model);
		} else if (model instanceof WorkflowStepInfo.WorkflowEndStepInfo) {
			return mapInfo((WorkflowStepInfo.WorkflowEndStepInfo) model);
		} else if (model instanceof WorkflowStepInfo.WorkflowActionStepInfo) {
			return mapInfo((WorkflowStepInfo.WorkflowActionStepInfo) model);
		} else if (model == null) {
			return null; // Handle null
		} else {
			throw new RuntimeException("Invalid workflow step info type!");
		}
	}

	@Mapping(target = "workflowId", source = "workflow.id")
	WorkflowStepInfo.WorkflowStartStepInfo mapInfo(WorkflowStartStep workflowStartStep);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	WorkflowStartStep mapInfo(WorkflowStepInfo.WorkflowStartStepInfo info);

	@Mapping(target = "workflowId", source = "workflow.id")
	WorkflowStepInfo.WorkflowEndStepInfo mapInfo(WorkflowEndStep workflowStartStep);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	WorkflowEndStep mapInfo(WorkflowStepInfo.WorkflowEndStepInfo info);

	@Mapping(target = "workflowId", source = "workflow.id")
	WorkflowStepInfo.WorkflowActionStepInfo mapInfo(WorkflowActionStep workflowStartStep);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	WorkflowActionStep mapInfo(WorkflowStepInfo.WorkflowActionStepInfo info);

	@Mapping(target = "originStepId", source = "origin.id")
	@Mapping(target = "targetStepId", source = "target.id")
	WorkflowTransitionDTO map(WorkflowTransition workflow);

	@Mapping(target = "isAutoincrement", source = "isAutoincrement", qualifiedByName = "mapStringYN")
	@Mapping(target = "isForeignKey", source = "isForeignKey", qualifiedByName = "mapStringYN")
	@Mapping(target = "foreignKeyTableFieldId", expression = "java(field.getForeignKeyTableField() == null ? null : field.getForeignKeyTableField().getId())")
	@Mapping(target = "isNullable", source = "isNullable", qualifiedByName = "mapStringYN")
	@Mapping(target = "isPrimaryKey", source = "isPrimaryKey", qualifiedByName = "mapStringYN")
	@Mapping(target = "isReadonly", source = "isReadonly", qualifiedByName = "mapStringYN")
	@Mapping(target = "tableId", expression = "java(field.getTable() == null ? null : field.getTable().getId())")
	@Mapping(target = "tableName", expression = "java(field.getTable() == null ? null : field.getTable().getTableName())")
	SysTableFieldInfo mapInfo(SysTableField field);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	SysTableField mapInfo(SysTableFieldInfo info);

	@Mapping(target = "isEnabled", source = "isEnabled", qualifiedByName = "mapStringYN")
	@Mapping(target = "isReadonly", source = "isReadonly", qualifiedByName = "mapStringYN")
	@Mapping(target = "isTransform", source = "isTransform", qualifiedByName = "mapStringYN")
	SysDataviewDTO map(SysDataview sysDataview);

	@Mapping(target = "isEnabled", source = "isEnabled", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isReadonly", source = "isReadonly", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isTransform", source = "isTransform", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "langs", ignore = true)
	@Mapping(target = "fields", ignore = true)
	@Mapping(target = "parameters", ignore = true)
	@Mapping(target = "sqls", ignore = true)
	SysDataview map(SysDataviewDTO sysDataviewDTO);

	SysDataviewLangDTO map(SysDataviewLang lang);

	SysDataviewLang map(SysDataviewLangDTO dto);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	SysDataview mapInfo(SysDataviewInfo info);

	@Mapping(target = "isEnabled", source = "isEnabled", qualifiedByName = "mapStringYN")
	SysDataviewInfo mapInfo(SysDataview sysDataview);

	@Mapping(target = "isPrimaryKey", source = "isPrimaryKey", qualifiedByName = "mapStringYN")
	@Mapping(target = "isReadonly", source = "isReadonly", qualifiedByName = "mapStringYN")
	@Mapping(target = "isTransform", source = "isTransform", qualifiedByName = "mapStringYN")
	@Mapping(target = "isVisible", source = "isVisible", qualifiedByName = "mapStringYN")
	SysDataviewFieldDTO map(SysDataviewField sysDataviewField);

	@Mapping(target = "isPrimaryKey", source = "isPrimaryKey", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isReadonly", source = "isReadonly", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isTransform", source = "isTransform", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isVisible", source = "isVisible", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "langs", ignore = true)
	SysDataviewField map(SysDataviewFieldDTO sysDataviewFieldDTO);

	SysDataviewFieldLangDTO map(SysDataviewFieldLang lang);

	SysDataviewFieldLang map(SysDataviewFieldLangDTO dto);

	SysDataviewParamDTO map(SysDataviewParam sysDataviewParam);

	SysDataviewParam map(SysDataviewParamDTO dto);

	SysDataviewSqlDTO map(SysDataviewSql sysDataviewSql);

	SysDataviewSql map(SysDataviewSqlDTO dto);

	TranslatedSysDataviewDTO map(SysDataviewTranslationService.TranslatedSysDataview translatedSysDataview);

	@Mapping(target = "id", ignore = true) // There is a setter that needs to be ignored
	SysDataviewTranslationService.TranslatedSysDataview map(TranslatedSysDataviewDTO dto);

	TranslatedSysDataviewFieldDTO map(SysDataviewFieldTranslationService.TranslatedSysDataviewField translatedSysDataviewField);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	SysTableFieldTranslationService.TranslatedSysTableField map(SysTableFieldInfo info);

	@Mapping(target = "id", ignore = true) // There is a setter that needs to be ignored
	@Mapping(target = "sysTableField", source = "sysTableField")
	@Mapping(target = "sysTableField.id", ignore = true) // There is a setter that needs to be ignored
	SysDataviewFieldTranslationService.TranslatedSysDataviewField map(TranslatedSysDataviewFieldDTO dto);

	@Mapping(source = "isPrimaryKey", target = "isPrimaryKey", qualifiedByName = "mapStringYN")
	@Mapping(source = "isReadonly", target = "isReadonly", qualifiedByName = "mapStringYN")
	@Mapping(source = "isTransform", target = "isTransform", qualifiedByName = "mapStringYN")
	@Mapping(source = "isVisible", target = "isVisible", qualifiedByName = "mapStringYN")
	SysDataviewFieldInfo mapInfo(SysDataviewField field);

	SysDataviewParamInfo mapInfo(SysDataviewParam sysDataviewParam);

	SysDataviewSqlInfo mapInfo(SysDataviewSql sysDataviewSql);

	@Mapping(target = "title", source = "title")
	@Mapping(target = "description", source = "description")
	@Mapping(target = "fields", source = "fields")
	@Mapping(target = "parameters", source = "entity.parameters")
	@Mapping(target = "sqls", source = "entity.sqls")
	SysDataviewDetailsDTO mapDetails(SysDataviewDetails details);

	ExplorationDTO map(Exploration exploration);

	@Mapping(target = "accessions", ignore = true)
	Exploration map(ExplorationDTO dto);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	Exploration mapInfo(ExplorationInfo info);

	ExplorationInfo mapInfo(Exploration workflow);

	AppSettingDTO map(AppSetting setting);
	AppSetting map(AppSettingDTO setting);

	AuthorizationDTO map(Authorization setting);
	Authorization map(AuthorizationDTO setting);

	CodeValueDTO map(CodeValue codeValue);

	@Mapping(target = "langs", ignore = true)
	CodeValue map(CodeValueDTO codeValue);

	CodeValueLangDTO map(CodeValueLang lang);

	CodeValueLang map(CodeValueLangDTO codeValue);

	TranslatedCodeValueDTO map(CodeValueTranslationService.TranslatedCodeValue codeValue);

	@Mapping(target = "id", ignore = true)
	CodeValueTranslationService.TranslatedCodeValue map(TranslatedCodeValueDTO dto);

	AppResourceDTO map(AppResource appResource);
	AppResource map(AppResourceDTO setting);

	AppUserGuiSettingDTO map(AppUserGuiSetting setting);
	AppUserGuiSetting map(AppUserGuiSettingDTO setting);

	CropDTO map(Crop crop);

	Crop map(CropDTO dto);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	CropAttachDTO map(CropAttach attach);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	CropAttach map(CropAttachDTO dto);

	@Mapping(source = "isGraftstockGenepool", target = "isGraftstockGenepool", qualifiedByName = "mapStringYN")
	@Mapping(source = "isPrimaryGenepool", target = "isPrimaryGenepool", qualifiedByName = "mapStringYN")
	@Mapping(source = "isQuaternaryGenepool", target = "isQuaternaryGenepool", qualifiedByName = "mapStringYN")
	@Mapping(source = "isSecondaryGenepool", target = "isSecondaryGenepool", qualifiedByName = "mapStringYN")
	@Mapping(source = "isTertiaryGenepool", target = "isTertiaryGenepool", qualifiedByName = "mapStringYN")
	TaxonomyCropMapDTO map(TaxonomyCropMap taxonomyCropMap);

	@Mapping(target = "isGraftstockGenepool", source = "isGraftstockGenepool", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isPrimaryGenepool", source = "isPrimaryGenepool", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isQuaternaryGenepool", source = "isQuaternaryGenepool", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isSecondaryGenepool", source = "isSecondaryGenepool", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isTertiaryGenepool", source = "isTertiaryGenepool", qualifiedByName = "mapBooleanYN")
	TaxonomyCropMap map(TaxonomyCropMapDTO dto);
	
	InvitroInventoryService.IVMultiplicationRequest map(InvitroInventoryApiService.IVMultiplicationRequestDTO request);

	@Mapping(target = "isAutoDeducted", source = "autoDeducted", qualifiedByName = "mapBooleanYN")
	InvitroInventoryService.IVMultiplicationItem map(InvitroInventoryApiService.IVMultiplicationItemDTO item);

	InventoryAttachmentApiService.InventoryAttachmentRequestDTO map(InventoryAttachmentService.InventoryAttachmentRequest request);
	InventoryAttachmentService.InventoryAttachmentRequest map(InventoryAttachmentApiService.InventoryAttachmentRequestDTO dto);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	InventoryViabilityAttach map(InventoryViabilityAttachDTO source);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	InventoryViabilityAttachDTO map(InventoryViabilityAttach source);

	InventoryViabilityAttachmentApiService.InventoryViabilityAttachmentRequestDTO map(InventoryViabilityAttachmentService.InventoryViabilityAttachmentRequest metadata);
	InventoryViabilityAttachmentService.InventoryViabilityAttachmentRequest map(InventoryViabilityAttachmentApiService.InventoryViabilityAttachmentRequestDTO metadata);

	KPIParameter map(KPIParameterDTO parameter);
	KPIParameterDTO map(KPIParameter parameter);

	@Mapping(target = "_class", source = ".", qualifiedByName = "getClassName")
	@SubclassMapping(target = BooleanDimensionDTO.class, source = BooleanDimension.class)
	@SubclassMapping(target = JpaDimensionDTO.class, source = JpaDimension.class)
	@SubclassMapping(target = StringListDimensionDTO.class, source = StringListDimension.class)
	@SubclassMapping(target = NumericListDimensionDTO.class, source = NumericListDimension.class)
	DimensionDTO map(Dimension<?> dimension);

	default Dimension<?> map(DimensionDTO dimension) {
		if (dimension instanceof BooleanDimensionDTO) {
			return map((BooleanDimensionDTO)dimension);
		} else if (dimension instanceof JpaDimensionDTO) {
			return map((JpaDimensionDTO)dimension);
		} else if (dimension instanceof NumericListDimensionDTO) {
			return map((NumericListDimensionDTO)dimension);
		} else if (dimension instanceof StringListDimensionDTO) {
			return map((StringListDimensionDTO)dimension);
		} else if (dimension == null) {
			return null; // Handle null
		} else {
			throw new RuntimeException("Invalid dimension type!");
		}
	}

	@Mapping(target = "_class", source = ".", qualifiedByName = "getClassName")
	BooleanDimensionDTO map(BooleanDimension dimension);

	BooleanDimension map(BooleanDimensionDTO dimension);

	@Mapping(target = "_class", source = ".", qualifiedByName = "getClassName")
	JpaDimensionDTO map(JpaDimension dimension);

	JpaDimension map(JpaDimensionDTO dimension);

	@Mapping(target = "_class", source = ".", qualifiedByName = "getClassName")
	StringListDimensionDTO map(StringListDimension dimension);

	StringListDimension map(StringListDimensionDTO dimension);

	@Mapping(target = "_class", source = ".", qualifiedByName = "getClassName")
	NumericListDimensionDTO map(NumericListDimension dimension);

	@Mapping(target = "javaType", ignore = true)
	NumericListDimension map(NumericListDimensionDTO dimension);

	DimensionKeyInfo mapInfo(DimensionKey key);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	DimensionKey mapInfo(DimensionKeyInfo key);

	ExecutionDimensionDTO map(ExecutionDimension key);

	ExecutionDimension map(ExecutionDimensionDTO dto);

	@Mapping(source = "isActive", target = "isActive", qualifiedByName = "mapBooleanYN")
	Execution map(ExecutionDTO dto);

	@Mapping(target = "_permissions", source = ".", qualifiedByName = "getPermissions")
	@Mapping(source = "isActive", target = "isActive", qualifiedByName = "mapStringYN")
	ExecutionDTO map(Execution execution);

	ExecutionGroupInfo mapInfo(ExecutionGroup group);

	ExecutionGroup mapInfo(ExecutionGroupInfo info);

	ExecutionRun map(ExecutionRunDTO dto);
	ExecutionRunDTO map(ExecutionRun run);
	ExecutionRunInfo mapInfo(ExecutionRun run);

	Observation map(ObservationDTO dto);
	ObservationDTO map(Observation observation);

	KPIService.ExecutionRunsRequest map(KPIApiService.ExecutionRunsRequestDTO runsRequest);

	List<KPIApiService.GroupedRunObservationsDTO> map(List<KPIService.GroupedRunObservations> observationsGroupedByDimension);

	InventoryHarvestDTO map(InventoryHarvest inventoryHarvest);

	@Mapping(target = "inventory", source = "inventory")
	@Mapping(target = "path", source = "inventory.accession", qualifiedByName = "mapGenesysImagePath")
	@Mapping(target = "uuid", source = "repositoryFile.uuid")
	@Mapping(target = "attachVersion", source = "repositoryFile.version")
	@Mapping(target = "contentType", source = "repositoryFile.contentType")
	@Mapping(target = "extension", source = "repositoryFile.extension")
	@Mapping(target = "originalFilename", source = "repositoryFile.originalFilename")
	@Mapping(target = "title", source = "repositoryFile.title")
	@Mapping(target = "subject", source = "repositoryFile.subject")
	@Mapping(target = "description", source = "repositoryFile.description")
	@Mapping(target = "creator", source = "repositoryFile.creator")
	@Mapping(target = "created", source = "repositoryFile.created")
	@Mapping(target = "rightsHolder", source = "repositoryFile.rightsHolder")
	@Mapping(target = "accessRights", source = "repositoryFile.accessRights")
	@Mapping(target = "license", source = "repositoryFile.license")
	@Mapping(target = "bibliographicCitation", source = "repositoryFile.bibliographicCitation")
	@Mapping(target = "md5", source = "repositoryFile.md5Sum")
	GenesysAttachment mapGenesys(AccessionInvAttach attach);

	@Named("mapGenesysImagePath")
	default String mapGenesysImagePath(Accession accession) {
		return getGenesysFolder(accession.getSite().getFaoInstituteNumber(), accession.getAccessionNumber());
	}

	/**
	 * Genesys replaces / in accession number with - to obtain a valid folder name.
	 */
	default String getGenesysFolder(final String instCode, String acceNumb) {
		if (Strings.CS.contains(acceNumb, "/")) {
			acceNumb = acceNumb.replaceAll("/", "-");
		}
		return "/wiews/" + instCode + "/acn/" + acceNumb;
	}

	@SubclassMapping(target = org.gringlobal.api.model.integration.RepositoryImageDTO.class, source = RepositoryImage.class)
	@Mapping(target = "_class", source = ".", qualifiedByName = "getClassName")
	org.gringlobal.api.model.integration.RepositoryFileDTO mapGenesys(RepositoryFile repositoryFile);

	@Mapping(target = "_class", source = ".", qualifiedByName = "getClassName")
	org.gringlobal.api.model.integration.RepositoryImageDTO mapGenesys(RepositoryImage repositoryImage);

	org.gringlobal.api.model.integration.RepositoryFolderInfo mapGenesysInfo(RepositoryFolder repositoryFolder);

	@Mapping(target = "id", ignore = true)
	@Mapping(target = "uuid", ignore = true)
	@Mapping(target = "apply", ignore = true)
	@Mapping(target = "folder", ignore = true)
	@Mapping(target = "thumbnailPath", ignore = true)
	RepositoryImage mapGenesys(org.gringlobal.api.model.integration.RepositoryImageDTO dto);

	@Mapping(target = "version", source = "attachVersion")
	@Mapping(target = "accession", source = "inventory.accession")
	GenesysAttachmentDTO mapGenesys(GenesysAttachment genesysImage);

	@Mapping(target = "attachVersion", source = "version")
	GenesysAttachment mapGenesys(GenesysAttachmentDTO dto);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	CropTraitObservationAttachDTO map(CropTraitObservationAttach attach);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	CropTraitObservationAttach map(CropTraitObservationAttachDTO dto);

	CropTraitObservationAttachmentApiService.CropTraitObservationAttachmentRequestDTO map(CropTraitObservationAttachmentService.CropTraitObservationAttachmentRequest request);
	CropTraitObservationAttachmentService.CropTraitObservationAttachmentRequest map(CropTraitObservationAttachmentApiService.CropTraitObservationAttachmentRequestDTO dto);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	CropTraitObservationDataAttachDTO map(CropTraitObservationDataAttach attach);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	CropTraitObservationDataAttach map(CropTraitObservationDataAttachDTO dto);

	CropTraitObservationDataAttachmentApiService.CropTraitObservationDataAttachmentRequestDTO map(CropTraitObservationDataAttachmentService.CropTraitObservationDataAttachmentRequest request);
	CropTraitObservationDataAttachmentService.CropTraitObservationDataAttachmentRequest map(CropTraitObservationDataAttachmentApiService.CropTraitObservationDataAttachmentRequestDTO dto);



	@Mapping(target = "isExtinct", source = "extinct", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isQuarantine", source = "quarantine", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "attachments", ignore = true)
	@Mapping(target = "symptoms", ignore = true)
	@Mapping(target = "geographies", ignore = true)
	@Mapping(target = "orderRequestItems", ignore = true)
	Pathogen map(PathogenDTO dto);

	@Mapping(target = "quarantine", source = "isQuarantine", qualifiedByName = "mapStringYN")
	@Mapping(target = "extinct", source = "isExtinct", qualifiedByName = "mapStringYN")
	PathogenDTO map(Pathogen pathogen);

	@Mapping(target = "quarantine", source = "isQuarantine", qualifiedByName = "mapStringYN")
	@Mapping(target = "extinct", source = "isExtinct", qualifiedByName = "mapStringYN")
	PathogenInfo mapInfo(Pathogen pathogen);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Pathogen mapInfo(PathogenInfo info);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	PathogenAttachDTO map(PathogenAttach pathogen);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	PathogenAttach map(PathogenAttachDTO dto);

	PathogenAttachmentService.PathogenAttachmentRequest map(PathogenAttachApiService.PathogenAttachRequestDTO request);

	PathogenTaxonomySpeciesMap map(PathogenTaxonomySpeciesMapDTO dto);
	PathogenTaxonomySpeciesMapDTO map(PathogenTaxonomySpeciesMap map);

	PathogenSymptomMapDTO map(PathogenSymptomMap dto);
	PathogenSymptomMap map(PathogenSymptomMapDTO map);

	@Mapping(target = "isQuarantine", source = "quarantine", qualifiedByName = "mapBooleanYN")
	PathogenGeographyMap map(PathogenGeographyMapDTO dto);

	@Mapping(target = "quarantine", source = "isQuarantine", qualifiedByName = "mapStringYN")
	PathogenGeographyMapDTO map(PathogenGeographyMap map);

	OrderRequestItemPathogenMap map(OrderRequestItemPathogenMapDTO dto);
	OrderRequestItemPathogenMapDTO map(OrderRequestItemPathogenMap map);

	@Mapping(target = "attachments", ignore = true)
	@Mapping(target = "pathogens", ignore = true)
	Symptom map(SymptomDTO dto);

	SymptomDTO map(Symptom symptom);
	SymptomInfo mapInfo(Symptom symptom);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Symptom mapInfo(SymptomInfo info);
	
	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	SymptomAttachDTO map(SymptomAttach attach);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	SymptomAttach map(SymptomAttachDTO dto);
	
	InventorySymptomMapDTO map(InventorySymptomMap map);

	@Mapping(target = "attachments", ignore = true)
	InventorySymptomMap map(InventorySymptomMapDTO dto);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "repositoryFile", target = "repositoryFile", qualifiedByName = "mapUnproxiedRepositoryFile")
	InventorySymptomMapAttachDTO map(InventorySymptomMapAttach attach);

	@Mapping(source = "isWebVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	InventorySymptomMapAttach map(InventorySymptomMapAttachDTO dto);

	InventorySymptomMapAttachmentService.InventorySymptomMapAttachmentRequest map(InventorySymptomMapAttachApiService.InventorySymptomMapAttachRequestDTO request);

	@Mapping(target = "isEnabled", source = "enabled", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "data", ignore = true)
	Location map(LocationDTO dto);

	@Mapping(target = "enabled", source = "isEnabled", qualifiedByName = "mapStringYN")
	LocationDTO map(Location location);

	@Mapping(target = "enabled", source = "isEnabled", qualifiedByName = "mapStringYN")
	LocationInfo mapInfo(Location location);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Location mapInfo(LocationInfo info);

	LocationAction map(LocationActionDTO dto);
	LocationActionDTO map(LocationAction action);

	LocationData map(LocationDataDTO dto);
	LocationDataDTO map(LocationData data);

	LocationReservation map(LocationReservationDTO dto);
	LocationReservationDTO map(LocationReservation data);

	LocationActionService.LocationActionRequest map(LocationActionRequestDTO dto);
	LocationActionRequestDTO map(LocationActionService.LocationActionRequest request);

}