Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions srv/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@
<basePackage>cds.gen</basePackage>
<strictSetters>true</strictSetters>
<interfacesForAspects>true</interfacesForAspects>
<linkedInterfaces>true</linkedInterfaces>
</configuration>
</execution>
</executions>
Expand Down
19 changes: 8 additions & 11 deletions srv/src/main/java/my/bookshop/RatingCalculator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@

import static cds.gen.my.bookshop.Bookshop_.BOOKS;

import cds.gen.my.bookshop.Books;
import cds.gen.my.bookshop.Reviews;
import com.sap.cds.Result;
import com.sap.cds.ql.Select;
import com.sap.cds.ql.Update;
import com.sap.cds.services.persistence.PersistenceService;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.OptionalDouble;
import java.util.stream.Stream;

import org.springframework.stereotype.Component;

import com.sap.cds.Result;
import com.sap.cds.ql.Select;
import com.sap.cds.ql.Update;
import com.sap.cds.services.persistence.PersistenceService;

import cds.gen.my.bookshop.Books;
import cds.gen.my.bookshop.Reviews;

/**
* Takes care of calculating the average rating of a book based on its review
* ratings.
Expand All @@ -34,8 +31,8 @@ public class RatingCalculator {
* Initializes the ratings for all existing books based on their reviews.
*/
public void initBookRatings() {
Result result = db.run(Select.from(BOOKS).columns(b -> b.ID()));
for (Books book : result.listOf(Books.class)) {
var result = db.run(Select.from(BOOKS).columns(b -> b.ID()));
for (Books book : result) {
setBookRating(book.getId());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@

import static cds.gen.adminservice.AdminService_.ADDRESSES;

import java.time.Duration;
import java.util.Optional;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import com.sap.cds.Result;
import cds.gen.adminservice.Addresses;
import cds.gen.adminservice.Addresses_;
import cds.gen.adminservice.AdminService_;
import cds.gen.adminservice.Orders;
import cds.gen.api_business_partner.ApiBusinessPartner;
import cds.gen.api_business_partner.ApiBusinessPartner_;
import cds.gen.api_business_partner.BusinessPartnerChangedContext;
import com.sap.cds.ql.CQL;
import com.sap.cds.ql.Insert;
import com.sap.cds.ql.Predicate;
Expand All @@ -33,15 +30,14 @@
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceConfiguration;
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceConfiguration.TimeLimiterConfiguration;
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceDecorator;

import cds.gen.adminservice.Addresses;
import cds.gen.adminservice.Addresses_;
import cds.gen.adminservice.AdminService_;
import cds.gen.adminservice.Orders;
import cds.gen.api_business_partner.ApiBusinessPartner;
import cds.gen.api_business_partner.ApiBusinessPartner_;
import cds.gen.api_business_partner.BusinessPartnerChangedContext;
import java.time.Duration;
import java.util.Optional;
import java.util.stream.Stream;
import my.bookshop.MessageKeys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

/**
* Custom handler for the Admin Service Addresses, which come from a remote S/4 System
Expand Down Expand Up @@ -107,13 +103,13 @@ public void patchAddressId(EventContext context, Stream<Orders> orders) {

orders.filter(o -> o.getShippingAddressId() != null).forEach(order -> {
String addressId = order.getShippingAddressId();
Result replica = db.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner).and(a.ID().eq(addressId))));
var replica = db.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner).and(a.ID().eq(addressId))));
// check if the address was not yet replicated
if(replica.rowCount() < 1) {
logger.info("Replicating Address '{}' from S/4 service", addressId);
Addresses remoteAddress = bupa.run(Select.from(ADDRESSES)
.where(a -> a.businessPartner().eq(businessPartner).and(a.ID().eq(addressId))))
.single(Addresses.class);
.single();

remoteAddress.setTombstone(false);
db.run(Insert.into(ADDRESSES).entry(remoteAddress));
Expand All @@ -128,15 +124,15 @@ public void updateBusinessPartnerAddresses(BusinessPartnerChangedContext context
String businessPartner = context.getData().getBusinessPartner();

// fetch affected entries from local replicas
Result replicas = db.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner)));
var replicas = db.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner)));
if(replicas.rowCount() > 0) {
logger.info("Updating Addresses for BusinessPartner '{}'", businessPartner);
// fetch changed data from S/4 -> might be less than local due to deletes
Result remoteAddresses = bupa.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner)));
var remoteAddresses = bupa.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner)));
// update replicas or add tombstone if external address was deleted
replicas.streamOf(Addresses.class).forEach(rep -> {
replicas.stream().forEach(rep -> {
Optional<Addresses> matching = remoteAddresses
.streamOf(Addresses.class)
.stream()
.filter(ext -> ext.getId().equals(rep.getId()))
.findFirst();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@

import static cds.gen.adminservice.AdminService_.ORDERS;

import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import cds.gen.adminservice.AdminService_;
import cds.gen.adminservice.Orders;
import cds.gen.adminservice.Orders_;
import com.sap.cds.ql.Select;
import com.sap.cds.ql.cqn.CqnDelete;
import com.sap.cds.services.EventContext;
Expand All @@ -24,10 +20,11 @@
import com.sap.cds.services.handler.annotations.Before;
import com.sap.cds.services.handler.annotations.ServiceName;
import com.sap.cds.services.persistence.PersistenceService;

import cds.gen.adminservice.AdminService_;
import cds.gen.adminservice.Orders;
import cds.gen.adminservice.Orders_;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

/**
* A custom handler that creates AuditLog messages.
Expand Down Expand Up @@ -74,7 +71,7 @@ public void beforeUpdateOrUpsertOrder(EventContext context, Stream<Orders> order
});
}

@Before(event = { CqnService.EVENT_DELETE }, entity = { Orders_.CDS_NAME })
@Before(entity = { Orders_.CDS_NAME })
public void beforeDelete(CdsDeleteEventContext context) {
// prepare a select statement to read old currency code
Select<?> ordersSelect = toSelect(context.getCqn());
Expand All @@ -95,11 +92,11 @@ private void auditCfgChange(final Action action, final ConfigChange cfgChange, E

private Optional<Orders> readOldOrders(String ordersId) {
// prepare a select statement to read old order number
Select<Orders_> ordersSelect = Select.from(ORDERS).columns(Orders_::OrderNo)
var ordersSelect = Select.from(ORDERS).columns(Orders_::OrderNo)
.where(o -> o.ID().eq(ordersId).and(o.IsActiveEntity().eq(true)));

// read old orders from DB
return this.db.run(ordersSelect).first(Orders.class);
return this.db.run(ordersSelect).first();
}

private static ConfigChange createConfigChange(Orders orders, Orders oldOrders) {
Expand Down
111 changes: 47 additions & 64 deletions srv/src/main/java/my/bookshop/handlers/AdminServiceHandler.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package my.bookshop.handlers;

import static cds.gen.adminservice.AdminService_.ORDERS;
import static cds.gen.adminservice.AdminService_.ORDER_ITEMS;
import static cds.gen.my.bookshop.Bookshop_.BOOKS;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.springframework.stereotype.Component;
import com.sap.cds.Result;
import cds.gen.adminservice.AdminService;
import cds.gen.adminservice.AdminService_;
import cds.gen.adminservice.Books;
import cds.gen.adminservice.BooksAddToOrderContext;
import cds.gen.adminservice.BooksCovers;
import cds.gen.adminservice.Books_;
import cds.gen.adminservice.OrderItems;
import cds.gen.adminservice.OrderItems_;
import cds.gen.adminservice.Orders;
import cds.gen.adminservice.Upload;
import cds.gen.adminservice.Upload_;
import cds.gen.my.bookshop.Bookshop_;
import com.sap.cds.ql.Select;
import com.sap.cds.ql.Update;
import com.sap.cds.ql.Upsert;
Expand All @@ -37,20 +35,18 @@
import com.sap.cds.services.handler.annotations.ServiceName;
import com.sap.cds.services.messages.Messages;
import com.sap.cds.services.persistence.PersistenceService;

import cds.gen.adminservice.AdminService;
import cds.gen.adminservice.AdminService_;
import cds.gen.adminservice.Books;
import cds.gen.adminservice.BooksAddToOrderContext;
import cds.gen.adminservice.BooksCovers;
import cds.gen.adminservice.Books_;
import cds.gen.adminservice.OrderItems;
import cds.gen.adminservice.OrderItems_;
import cds.gen.adminservice.Orders;
import cds.gen.adminservice.Upload;
import cds.gen.adminservice.Upload_;
import cds.gen.my.bookshop.Bookshop_;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
import my.bookshop.MessageKeys;
import org.springframework.stereotype.Component;

/**
* Custom business logic for the "Admin Service" (see admin-service.cds)
Expand Down Expand Up @@ -100,11 +96,11 @@ public void beforeCreateOrder(Stream<Orders> orders, EventContext context) {
// calculate the actual quantity difference
// FIXME this should handle book changes, currently only quantity changes are handled
int diffQuantity = quantity - db.run(Select.from(Bookshop_.ORDER_ITEMS).columns(i -> i.quantity()).byId(orderItem.getId()))
.first(OrderItems.class).map(i -> i.getQuantity()).orElse(0);
.first().map(i -> i.getQuantity()).orElse(0);

// check if enough books are available
Result result = db.run(Select.from(BOOKS).columns(b -> b.ID(), b -> b.stock(), b -> b.price()).byId(bookId));
result.first(Books.class).ifPresent(book -> {
var result = db.run(Select.from(BOOKS).columns(b -> b.ID(), b -> b.stock(), b -> b.price()).byId(bookId));
result.first().ifPresent(book -> {
if (book.getStock() < diffQuantity) {
// Tip: you can have localized messages and use parameters in your messages
messages.error(MessageKeys.BOOK_REQUIRE_STOCK, book.getStock())
Expand All @@ -128,51 +124,43 @@ public void beforeCreateOrder(Stream<Orders> orders, EventContext context) {
});
}

/**
/*
* Calculate the total order value preview when editing an order item
*
* @param context
* @param orderItem
*/
@Before(event = DraftService.EVENT_DRAFT_PATCH)
public void patchOrderItems(DraftPatchEventContext context, OrderItems orderItem) {
@Before
public void patchOrderItems(DraftPatchEventContext context, OrderItems_ ref, OrderItems orderItem) {
// check if quantity or book was updated
Integer quantity = orderItem.getQuantity();
String bookId = orderItem.getBookId();
String orderItemId = orderItem.getId();
BigDecimal amount = calculateAmountInDraft(orderItemId, quantity, bookId);
BigDecimal amount = calculateAmountInDraft(ref, quantity, bookId);
if (amount != null) {
orderItem.setAmount(amount);
}
}

/**
/*
* Calculate the total order value preview when deleting an order item from the order
*
* @param context
*/
@Before(event = DraftService.EVENT_DRAFT_CANCEL, entity = OrderItems_.CDS_NAME)
public void cancelOrderItems(DraftCancelEventContext context) {
String orderItemId = (String) analyzer.analyze(context.getCqn()).targetKeys().get(OrderItems.ID);
if(orderItemId != null) {
calculateAmountInDraft(orderItemId, 0, null);
@Before
public void cancelOrderItems(DraftCancelEventContext context, OrderItems_ ref) {
if(ref.asRef().targetSegment().filter().isPresent()) {
calculateAmountInDraft(ref, 0, null);
}
}

private BigDecimal calculateAmountInDraft(String orderItemId, Integer newQuantity, String newBookId) {
private BigDecimal calculateAmountInDraft(OrderItems_ ref, Integer newQuantity, String newBookId) {
Integer quantity = newQuantity;
String bookId = newBookId;
if (quantity == null && bookId == null) {
return null; // nothing changed
}

// get the order item that was updated (to get access to the book price, quantity and order total)
Result result = adminService.run(Select.from(ORDER_ITEMS)
var result = adminService.run(Select.from(ref)
.columns(o -> o.quantity(), o -> o.amount(),
o -> o.book().expand(b -> b.ID(), b -> b.price()),
o -> o.parent().expand(p -> p.ID(), p -> p.total()))
.where(o -> o.ID().eq(orderItemId).and(o.IsActiveEntity().eq(false))));
OrderItems itemToPatch = result.first(OrderItems.class).orElseThrow(notFound(MessageKeys.ORDERITEM_MISSING));
o -> o.parent().expand(p -> p.ID(), p -> p.total())));
OrderItems itemToPatch = result.single();
BigDecimal bookPrice = null;

// fallback to existing values
Expand All @@ -191,9 +179,8 @@ private BigDecimal calculateAmountInDraft(String orderItemId, Integer newQuantit

// get the price of the updated book ID
if(bookPrice == null) {
result = db.run(Select.from(BOOKS).byId(bookId).columns(b -> b.price()));
Books book = result.first(Books.class).orElseThrow(notFound(MessageKeys.BOOK_MISSING));
bookPrice = book.getPrice();
var bookResult = db.run(Select.from(BOOKS).byId(bookId).columns(b -> b.price()));
bookPrice = bookResult.single().getPrice();
}

// update the amount of the order item
Expand All @@ -215,9 +202,9 @@ private BigDecimal calculateAmountInDraft(String orderItemId, Integer newQuantit
* @param context
*/
@On(entity = Books_.CDS_NAME)
public void addBookToOrder(BooksAddToOrderContext context) {
public Orders addBookToOrder(BooksAddToOrderContext context) {
String orderId = context.getOrderId();
List<Orders> orders = adminService.run(Select.from(ORDERS).columns(o -> o._all(), o -> o.Items().expand()).where(o -> o.ID().eq(orderId))).listOf(Orders.class);
List<Orders> orders = adminService.run(Select.from(ORDERS).columns(o -> o._all(), o -> o.Items().expand()).where(o -> o.ID().eq(orderId))).list();
Orders order = orders.stream().filter(p -> p.getIsActiveEntity()).findFirst().orElse(null);

// check that the order with given ID exists and is not in draft-mode
Expand All @@ -241,9 +228,9 @@ public void addBookToOrder(BooksAddToOrderContext context) {
newItem.setQuantity(context.getQuantity());
order.getItems().add(newItem);

Orders updatedOrder = adminService.run(Update.entity(ORDERS).data(order)).single(Orders.class);
Orders updatedOrder = adminService.run(Update.entity(ORDERS).data(order)).single();
messages.success(MessageKeys.BOOK_ADDED_ORDER);
context.setResult(updatedOrder);
return updatedOrder;
}

/**
Expand All @@ -260,7 +247,7 @@ public Upload getUploadSingleton() {
* @param csv
*/
@On
public void addBooksViaCsv(CdsUpdateEventContext context, Upload upload) {
public List<Upload> addBooksViaCsv(CdsUpdateEventContext context, Upload upload) {
InputStream is = upload.getCsv();
if (is != null) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
Expand All @@ -287,7 +274,7 @@ public void addBooksViaCsv(CdsUpdateEventContext context, Upload upload) {
throw new ServiceException(ErrorStatuses.SERVER_ERROR, MessageKeys.BOOK_IMPORT_INVALID_CSV, e);
}
}
context.setResult(Arrays.asList(upload));
return Arrays.asList(upload);
}

@Before(event = {CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE, DraftService.EVENT_DRAFT_NEW, DraftService.EVENT_DRAFT_PATCH})
Expand All @@ -296,10 +283,6 @@ public void restoreCoversUpId(CqnStructuredTypeRef ref, BooksCovers cover) {
cover.setUpId((String) analyzer.analyze(ref).rootKeys().get(Books.ID));
}

private Supplier<ServiceException> notFound(String message) {
return () -> new ServiceException(ErrorStatuses.NOT_FOUND, message);
}

private BigDecimal defaultZero(BigDecimal decimal) {
return decimal == null ? BigDecimal.valueOf(0) : decimal;
}
Expand Down
Loading