diff --git a/samples/bookshop/app/supplier-invoices/fiori-service.cds b/samples/bookshop/app/supplier-invoices/fiori-service.cds
index fa54eea..cad65f0 100644
--- a/samples/bookshop/app/supplier-invoices/fiori-service.cds
+++ b/samples/bookshop/app/supplier-invoices/fiori-service.cds
@@ -67,6 +67,12 @@ annotate service.SupplierInvoices with @(
]
);
+annotate service.SupplierInvoices with {
+ invoiceNumber @title: '{i18n>InvoiceNumber}';
+ supplier @title: '{i18n>Supplier}';
+ status @title: '{i18n>Status}';
+}
+
annotate service.SupplierInvoices with {
supplier @Common: {
Text : supplier.name,
diff --git a/samples/bookshop/db/data/sap.capire.bookshop-Suppliers.csv b/samples/bookshop/db/data/sap.capire.bookshop-Suppliers.csv
index 4baf974..804a9d4 100644
--- a/samples/bookshop/db/data/sap.capire.bookshop-Suppliers.csv
+++ b/samples/bookshop/db/data/sap.capire.bookshop-Suppliers.csv
@@ -3,3 +3,4 @@ b1e2c3d4-5f6a-7b8c-9d0e-1f2a3b4c5d6e;Springer Nature;DE;orders@springernature.co
c2f3d4e5-6a7b-8c9d-0e1f-2a3b4c5d6e7f;O'Reilly Media;US;invoices@oreilly.com
d3a4b5c6-7d8e-9f0a-1b2c-3d4e5f6a7b8c;Elsevier;NL;ap@elsevier.com
e4b5c6d7-8e9f-0a1b-2c3d-4e5f6a7b8c9d;Pearson Education;GB;finance@pearson.com
+e4b5c6d7-8e9f-0a1b-2c3d-4e5f6a7b8c9f;Studio Salford;US;@pearson.com
diff --git a/samples/bookshop/db/schema.cds b/samples/bookshop/db/schema.cds
index c13c016..afd21c6 100644
--- a/samples/bookshop/db/schema.cds
+++ b/samples/bookshop/db/schema.cds
@@ -62,3 +62,7 @@ entity SupplierInvoices : managed, cuid {
status : Association to InvoiceStatus;
documentAiJobId : String;
}
+
+annotate SupplierInvoices with {
+ status @UI.RecommendationState: 0;
+}
diff --git a/samples/bookshop/pom.xml b/samples/bookshop/pom.xml
index 32a66ff..aae91b9 100644
--- a/samples/bookshop/pom.xml
+++ b/samples/bookshop/pom.xml
@@ -22,7 +22,7 @@
5.0.0
4.1.0
0.0.1-alpha
- 0.0.1-alpha
+ 0.0.2-alpha
1.5.0
diff --git a/samples/bookshop/srv/src/main/java/customer/bookshop/handlers/DocumentExtractionResultHandler.java b/samples/bookshop/srv/src/main/java/customer/bookshop/handlers/DocumentExtractionResultHandler.java
index 3a87cf2..0b4da83 100644
--- a/samples/bookshop/srv/src/main/java/customer/bookshop/handlers/DocumentExtractionResultHandler.java
+++ b/samples/bookshop/srv/src/main/java/customer/bookshop/handlers/DocumentExtractionResultHandler.java
@@ -5,10 +5,13 @@
import cds.gen.sap.capire.bookshop.SupplierInvoices;
import cds.gen.sap.capire.bookshop.SupplierInvoices_;
+import cds.gen.sap.capire.bookshop.Suppliers;
+import cds.gen.sap.capire.bookshop.Suppliers_;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sap.cds.feature.documentai.generated.cds4j.sap.document.ai.documentaiservice.DocumentExtractionResult;
import com.sap.cds.feature.documentai.generated.cds4j.sap.document.ai.documentaiservice.DocumentExtractionResultContext;
+import com.sap.cds.ql.Select;
import com.sap.cds.ql.Update;
import com.sap.cds.services.cds.ApplicationService;
import com.sap.cds.services.handler.EventHandler;
@@ -17,7 +20,9 @@
import com.sap.cds.services.persistence.PersistenceService;
import java.math.BigDecimal;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,7 +31,7 @@
/**
* Handles extraction results from the SAP Document AI plugin. Parses the extraction JSON returned
* by the DIE service and populates the corresponding SupplierInvoice entity with the extracted
- * header fields (invoice number, date, total amount, currency).
+ * header fields (invoice number, date, total amount, currency, and matched supplier).
*/
@Component
@ServiceName(value = "*", type = ApplicationService.class)
@@ -50,18 +55,15 @@ public void onExtractionCompleted(DocumentExtractionResultContext context) {
JsonNode root = objectMapper.readTree(data.getExtractionResult());
JsonNode headerFields = root.path("extraction").path("headerFields");
- // Extract relevant header fields from the DIE response
String documentNumber = getHeaderFieldValue(headerFields, "documentNumber");
String documentDate = getHeaderFieldValue(headerFields, "documentDate");
String currencyCode = getHeaderFieldValue(headerFields, "currencyCode");
BigDecimal grossAmount = getHeaderFieldNumber(headerFields, "grossAmount");
+ String senderName = getHeaderFieldValue(headerFields, "senderName");
logger.info(
- "[bookshop] Extracted: number={}, date={}, amount={} {}",
- documentNumber,
- documentDate,
- grossAmount,
- currencyCode);
+ "[bookshop] Extracted: number={}, date={}, amount={} {}, sender={}",
+ documentNumber, documentDate, grossAmount, currencyCode, senderName);
// Update the invoice that is currently in EXTRACTING status.
// In a production app you would correlate by a stored jobId; for this sample
@@ -73,6 +75,18 @@ public void onExtractionCompleted(DocumentExtractionResultContext context) {
if (grossAmount != null) updateData.put(SupplierInvoices.TOTAL_AMOUNT, grossAmount);
if (currencyCode != null) updateData.put(SupplierInvoices.CURRENCY_CODE, currencyCode);
+ // Match extracted sender name to a Supplier record (case-insensitive)
+ if (senderName != null) {
+ findSupplierByName(senderName)
+ .ifPresentOrElse(
+ supplier -> {
+ updateData.put(SupplierInvoices.SUPPLIER_ID, supplier.getId());
+ logger.info(
+ "[bookshop] Matched supplier: {} ({})", supplier.getName(), supplier.getId());
+ },
+ () -> logger.warn("[bookshop] No supplier found for sender name: {}", senderName));
+ }
+
long updated =
db.run(
Update.entity(SupplierInvoices_.CDS_NAME)
@@ -93,6 +107,15 @@ public void onExtractionCompleted(DocumentExtractionResultContext context) {
context.setCompleted();
}
+ private Optional findSupplierByName(String name) {
+ String normalized = name.toLowerCase(Locale.ROOT);
+ return db.run(Select.from(Suppliers_.class))
+ .listOf(Suppliers.class)
+ .stream()
+ .filter(s -> s.getName() != null && normalized.equals(s.getName().toLowerCase(Locale.ROOT)))
+ .findFirst();
+ }
+
private String getHeaderFieldValue(JsonNode headerFields, String fieldName) {
for (JsonNode field : headerFields) {
if (fieldName.equals(field.path("name").asText())) {