Skip to content

Commit cd34db8

Browse files
authored
Merge pull request #419 from cap-java/moveAndCopyBugFix
Move and copy bug fix
2 parents 6aad92c + 86eb33d commit cd34db8

2 files changed

Lines changed: 120 additions & 18 deletions

File tree

sdm/src/main/java/com/sap/cds/sdm/persistence/DBQuery.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,11 @@ public Object[] getValidSecondaryPropertiesWithEntity(AttachmentMoveEventContext
296296
CdsEntity entity = attachmentEntity.get();
297297

298298
// Get secondary properties annotations
299+
// Filter out associations - only include actual database columns
299300
Map<String, String> secondaryProperties = new HashMap<>();
300301
entity
301302
.elements()
303+
.filter(element -> !element.getType().isAssociation())
302304
.forEach(
303305
element -> {
304306
Optional<com.sap.cds.reflect.CdsAnnotation<Object>> annotation =

sdm/src/main/java/com/sap/cds/sdm/service/handler/SDMCustomServiceHandler.java

Lines changed: 118 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ public void copyAttachments(AttachmentCopyEventContext context) throws IOExcepti
161161
.customPropertyValues(null)
162162
.build();
163163

164-
createDraftEntries(draftRequest, customPropertyDefinitions);
164+
// Pass the entity for type conversion
165+
CdsEntity targetEntity = entity.isPresent() ? entity.get() : null;
166+
createDraftEntries(draftRequest, customPropertyDefinitions, targetEntity);
165167

166168
context.setCompleted();
167169
}
@@ -1297,6 +1299,17 @@ private void processSecondaryProperty(
12971299
return;
12981300
}
12991301

1302+
// Check if this is an association field (e.g., customProperty1)
1303+
// For associations, we should only populate the foreign key field (e.g., customProperty1_code)
1304+
// Skip the association field itself as CDS will resolve it from the _code field
1305+
CdsElement element = targetEntity.getElement(dbPropertyName);
1306+
if (element != null && element.getType().isAssociation()) {
1307+
logger.info(
1308+
"Skipping association field '{}' - association will be resolved from corresponding _code field",
1309+
dbPropertyName);
1310+
return;
1311+
}
1312+
13001313
Object convertedValue = convertValueIfNeeded(value, dbPropertyName, targetEntity);
13011314
filteredProperties.put(dbPropertyName, convertedValue);
13021315
logger.info("Added to DB map: '{}' = '{}'", dbPropertyName, convertedValue);
@@ -1312,28 +1325,36 @@ private void processSecondaryProperty(
13121325
* @return converted value or original value if no conversion needed
13131326
*/
13141327
private Object convertValueIfNeeded(Object value, String dbPropertyName, CdsEntity targetEntity) {
1315-
if (!(value instanceof Long)) {
1316-
return value;
1317-
}
1328+
// Handle Long values - convert to Instant for DateTime fields
1329+
if (value instanceof Long) {
1330+
CdsElement element = targetEntity.getElement(dbPropertyName);
1331+
if (isDateTimeField(element)) {
1332+
Object converted = Instant.ofEpochMilli((Long) value);
1333+
logger.info(
1334+
"Converted Long timestamp {} to Instant {} for DateTime field '{}'",
1335+
value,
1336+
converted,
1337+
dbPropertyName);
1338+
return converted;
1339+
}
13181340

1319-
CdsElement element = targetEntity.getElement(dbPropertyName);
1320-
if (isDateTimeField(element)) {
1321-
Object converted = Instant.ofEpochMilli((Long) value);
13221341
logger.info(
1323-
"Converted Long timestamp {} to Instant {} for DateTime field '{}'",
1342+
"Keeping Long value {} as-is for non-DateTime field '{}' (type: {})",
13241343
value,
1325-
converted,
1326-
dbPropertyName);
1327-
return converted;
1344+
dbPropertyName,
1345+
element != null && element.getType() != null
1346+
? element.getType().getQualifiedName()
1347+
: "unknown");
1348+
return value;
13281349
}
13291350

1351+
// For all other types (String, Integer, Boolean, etc.), return as-is
1352+
// This ensures codelist/dropdown String values are properly handled
13301353
logger.info(
1331-
"Keeping Long value {} as-is for non-DateTime field '{}' (type: {})",
1354+
"Keeping value {} (type: {}) as-is for field '{}'",
13321355
value,
1333-
dbPropertyName,
1334-
element != null && element.getType() != null
1335-
? element.getType().getQualifiedName()
1336-
: "unknown");
1356+
value != null ? value.getClass().getSimpleName() : "null",
1357+
dbPropertyName);
13371358
return value;
13381359
}
13391360

@@ -1349,6 +1370,81 @@ private boolean isDateTimeField(CdsElement element) {
13491370
&& "cds.DateTime".equals(element.getType().getQualifiedName());
13501371
}
13511372

1373+
/**
1374+
* Converts custom property value from String to appropriate type based on CDS field definition.
1375+
* Used for copy operations where values come as String from SDM response.
1376+
*
1377+
* @param value the String value from SDM
1378+
* @param dbPropertyName the DB field name
1379+
* @param targetEntity the target entity for type checking
1380+
* @return converted value or original value if no conversion needed
1381+
*/
1382+
private Object convertCustomPropertyValue(
1383+
String value, String dbPropertyName, CdsEntity targetEntity) {
1384+
if (value == null || value.isEmpty() || targetEntity == null) {
1385+
return value;
1386+
}
1387+
1388+
CdsElement element = targetEntity.getElement(dbPropertyName);
1389+
if (element == null || element.getType() == null) {
1390+
return value;
1391+
}
1392+
1393+
String fieldType = element.getType().getQualifiedName();
1394+
1395+
try {
1396+
// Handle DateTime fields - convert Long timestamp to Instant
1397+
if ("cds.DateTime".equals(fieldType)) {
1398+
long timestamp = Long.parseLong(value);
1399+
Object converted = Instant.ofEpochMilli(timestamp);
1400+
logger.info(
1401+
"Converted String timestamp '{}' to Instant {} for DateTime field '{}'",
1402+
value,
1403+
converted,
1404+
dbPropertyName);
1405+
return converted;
1406+
}
1407+
1408+
// Handle Integer fields
1409+
if ("cds.Integer".equals(fieldType)) {
1410+
int convertedInt = Integer.parseInt(value);
1411+
logger.info(
1412+
"Converted String '{}' to Integer {} for field '{}'",
1413+
value,
1414+
convertedInt,
1415+
dbPropertyName);
1416+
return convertedInt;
1417+
}
1418+
1419+
// Handle Boolean fields
1420+
if ("cds.Boolean".equals(fieldType)) {
1421+
boolean convertedBool = Boolean.parseBoolean(value);
1422+
logger.info(
1423+
"Converted String '{}' to Boolean {} for field '{}'",
1424+
value,
1425+
convertedBool,
1426+
dbPropertyName);
1427+
return convertedBool;
1428+
}
1429+
1430+
} catch (NumberFormatException e) {
1431+
logger.warn(
1432+
"Failed to convert value '{}' for field '{}' of type '{}', keeping as String: {}",
1433+
value,
1434+
dbPropertyName,
1435+
fieldType,
1436+
e.getMessage());
1437+
}
1438+
1439+
// For String fields (including codelist/dropdown) and other types, return as-is
1440+
logger.info(
1441+
"Keeping String value '{}' as-is for field '{}' (type: {})",
1442+
value,
1443+
dbPropertyName,
1444+
fieldType);
1445+
return value;
1446+
}
1447+
13521448
// Rollback a single attachment to source folder
13531449
private void rollbackSingleAttachment(
13541450
String objectId,
@@ -1521,7 +1617,9 @@ private void createDraftEntriesForMove(DraftEntryMoveData data) {
15211617
* copy operations where custom properties come from the SDM copyAttachment response.
15221618
*/
15231619
private void createDraftEntries(
1524-
CreateDraftEntriesRequest request, Map<String, String> customPropertyDefinitions) {
1620+
CreateDraftEntriesRequest request,
1621+
Map<String, String> customPropertyDefinitions,
1622+
CdsEntity targetEntity) {
15251623

15261624
for (int i = 0; i < request.getAttachmentsMetadata().size(); i++) {
15271625
Map<String, String> attachmentMetadata = request.getAttachmentsMetadata().get(i);
@@ -1565,7 +1663,9 @@ private void createDraftEntries(
15651663

15661664
if (attachmentMetadata.containsKey(sdmPropertyName)) {
15671665
String value = attachmentMetadata.get(sdmPropertyName);
1568-
updatedFields.put(columnName, value);
1666+
// Convert value based on CDS field type
1667+
Object convertedValue = convertCustomPropertyValue(value, columnName, targetEntity);
1668+
updatedFields.put(columnName, convertedValue);
15691669
}
15701670
}
15711671
}

0 commit comments

Comments
 (0)