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())) {