Skip to content

Commit

Permalink
Add parent products to share stock
Browse files Browse the repository at this point in the history
  • Loading branch information
JoepdeJong committed Sep 20, 2024
1 parent 0bb248b commit a843b90
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ abstract class DashboardController {
/** Object key product. */
static final String OBJ_PRODUCT = "product";

/** Object key parentProducts. */
static final String OBJ_PARENT_PRODUCTS = "parentProducts";

/** Object key tasks. */
static final String OBJ_TASKS = "tasks";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,19 @@ public String create(RedirectAttributes redirect, @ModelAttribute Product produc
*
* @return thymeleaf template path
*/
@GetMapping("/edit/{key}")
@GetMapping({"/edit/{key}","/edit/{key}/"})
public String edit(Model model, RedirectAttributes redirect, @PathVariable String key) {
try {
if (!model.containsAttribute(OBJ_PRODUCT)) {
model.addAttribute(OBJ_PRODUCT, productService.getByKey(key));
}
if (!model.containsAttribute(OBJ_PARENT_PRODUCTS)) {
model.addAttribute(OBJ_PARENT_PRODUCTS,
productService.getPossibleParentProductsByProduct(
(Product) model.getAttribute(OBJ_PRODUCT)
)
);
}

return "admin/products/product";
} catch (ProductNotFoundException e) {
Expand All @@ -164,7 +171,7 @@ public String edit(Model model, RedirectAttributes redirect, @PathVariable Strin
*
* @return String
*/
@PostMapping("/edit/{key}")
@PostMapping({"/edit/{key}","/edit/{key}/"})
public String update(RedirectAttributes redirect, @ModelAttribute Product product, @PathVariable String key) {
try {
product.setKey(key);
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/ch/wisv/events/admin/utils/ProductTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public enum ProductTemplate {

/** Product maxSoldperCustomer. */
@Getter
private final int maxSolPerCustomer;
private final int maxSoldPerCustomer;

/** Product chOnly. */
@Getter
Expand All @@ -53,23 +53,23 @@ public enum ProductTemplate {
* @param title of type String
* @param cost of type double
* @param vatRate of type VatRate
* @param maxSolPerCustomer of type int
* @param maxSoldPerCustomer of type int
* @param chOnly of type boolean
* @param reservable of type boolean
*/
ProductTemplate(
String templateName, String title,
double cost,
VatRate vatRate,
int maxSolPerCustomer,
int maxSoldPerCustomer,
boolean chOnly,
boolean reservable
) {
this.templateName = templateName;
this.title = title;
this.cost = cost;
this.vatRate = vatRate;
this.maxSolPerCustomer = maxSolPerCustomer;
this.maxSoldPerCustomer = maxSoldPerCustomer;
this.chOnly = chOnly;
this.reservable = reservable;
}
Expand All @@ -85,7 +85,7 @@ public String toJson() {
object.put("title", this.getTitle());
object.put("cost", this.getCost());
object.put("vatRate", this.getVatRate());
object.put("maxSoldPerCustomer", this.getMaxSolPerCustomer());
object.put("maxSoldPerCustomer", this.getMaxSoldPerCustomer());
object.put("chOnly", this.isChOnly());
object.put("reservable", this.isReservable());

Expand Down
71 changes: 67 additions & 4 deletions src/main/java/ch/wisv/events/core/model/product/Product.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import jakarta.persistence.*;

import ch.wisv.events.core.model.event.Event;
Expand Down Expand Up @@ -102,12 +106,28 @@ public class Product {

@ManyToOne
@JoinTable(name = "event_products", joinColumns = @JoinColumn(name = "products_id"))
@JsonIgnore
public Event event;

/**
* Parent Product. Shares the product availability.
*/
@ManyToOne
@JoinColumn(name = "parent_product_id")
public Product parentProduct;

/**
* Field childProducts.
*/
@OneToMany(mappedBy = "parentProduct", cascade = CascadeType.MERGE, targetEntity = Product.class, fetch = FetchType.LAZY)
@JsonIgnore
public List<Product> childProducts;

/**
* Field productList.
*/
@OneToMany(cascade = CascadeType.MERGE, targetEntity = Product.class, fetch = FetchType.EAGER)
@JsonIgnore
public List<Product> products;

/**
Expand Down Expand Up @@ -196,13 +216,13 @@ public Product(
* @return progress of event
*/
public double calcProgress() {
if (this.maxSold == null) {
if (this.getMaxSold() == null) {
return 100.d;
} else if (this.sold == 0) {
} else if (this.getTotalSold() == 0) {
return 0.d;
}

return Math.round((((double) this.sold / (double) this.maxSold) * 100.d) * 100.d) / 100.d;
return Math.round((((double) this.getTotalSold() / (double) this.getMaxSold()) * 100.d) * 100.d) / 100.d;
}

/**
Expand All @@ -229,7 +249,50 @@ public void increaseReserved(int amount) {
* @return boolean
*/
public boolean isSoldOut() {
return this.maxSold != null && this.sold >= this.maxSold;
return this.getMaxSold() != null && this.getTotalSold() >= this.getMaxSold();
}

public Integer getMaxSold() {
// If the product has a parent product, return the maxSold of the parent product.
if (this.parentProduct != null) {
return this.parentProduct.getMaxSold();
}

return this.maxSold;
}

public Integer getMaxSoldPerCustomer() {
// If the product has a parent product, return the maxSoldPerCustomer of the parent product.
if (this.parentProduct != null) {
return this.parentProduct.getMaxSoldPerCustomer();
}

return this.maxSoldPerCustomer;
}

public Integer getTotalSold() {
// If the product has a parent product, return the total sold of the parent product.
if (this.parentProduct != null) {
return this.parentProduct.getTotalSold();
}

int totalSold = this.sold;
if (this.childProducts != null) {
for (Product childProduct : this.childProducts) {
totalSold += childProduct.getSold();
}
}

return totalSold;
}

// Custom toString
@Override
public String toString() {
return "Product{" +
"id=" + id +
", title='" + title + '\'' +
", description='" + description + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ public interface ProductService {
*/
List<Product> getAvailableProducts();

/**
* Get possible parent products.
*
* @return Collection of Products
*/
List<Product> getPossibleParentProductsByProduct(Product product);

/**
* Get Product by Key.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,25 @@ public List<Product> getAllProducts() {
public List<Product> getAvailableProducts() {
return productRepository.findAllBySellStartBefore(LocalDateTime.now()).stream()
.filter(product -> (product.getSellEnd() == null || !product.getSellEnd()
.isBefore(LocalDateTime.now())) && (product.getMaxSold() == null || product.getSold() < product.getMaxSold()))
.isBefore(LocalDateTime.now())) && (product.getMaxSold() == null || product.getTotalSold() < product.getMaxSold()))
.collect(Collectors.toCollection(ArrayList::new));
}

/**
* Get possible parent products.
*
* @return Collection of Products
*/
@Override
public List<Product> getPossibleParentProductsByProduct(Product product) {
if (product.getEvent() == null) {
return new ArrayList<>();
}
return product.getEvent().getProducts().stream()
.filter(p -> p.getParentProduct() == null && !p.getKey().equals(product.getKey()))
.collect(Collectors.toList());
}

/**
* Get Product by Key.
*
Expand Down Expand Up @@ -125,6 +140,7 @@ public void update(Product product) throws ProductNotFoundException, ProductInva
model.setMaxSoldPerCustomer(product.getMaxSoldPerCustomer());
model.setChOnly(product.isChOnly());
model.setReservable(product.isReservable());
model.setParentProduct(product.getParentProduct());

if (product.getSold() != 0) {
model.setSold(product.getSold());
Expand Down Expand Up @@ -197,7 +213,7 @@ private void assertIsValidProduct(Product product) throws ProductInvalidExceptio
throw new ProductInvalidException("It is not possible to add the same product twice or more!");
}

if (product.getMaxSoldPerCustomer() == null || product.getMaxSoldPerCustomer() < 1 || product.getMaxSoldPerCustomer() > 25) {
if (product.getParentProduct() == null && (product.getMaxSoldPerCustomer() == null || product.getMaxSoldPerCustomer() < 1 || product.getMaxSoldPerCustomer() > 25)) {
throw new ProductInvalidException("Max sold per customer should be between 1 and 25!");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,25 @@ public EventTestDataRunner(EventRepository eventRepository, ProductRepository pr
}

/**
* Method addProduct.
* Method addProducts.
*
* @param event of type Event
* @param jsonObject of type JSONObject
*
* @return Event
*/
private Event addProduct(Event event, JSONObject jsonObject) {
if (jsonObject.get("productNumber") != null) {
int productNumber = ((Long) jsonObject.get("productNumber")).intValue();
Optional<Product> optional = this.productRepository.findById(productNumber);
private Event addProducts(Event event, JSONObject jsonObject) {
if (jsonObject.get("productNumbers") != null) {
for (Object productNumber : (Iterable<?>) jsonObject.get("productNumbers")) {
Optional<Product> optional = this.productRepository.findById(((Long) productNumber).intValue());

optional.ifPresent(product -> {
product.setLinked(true);
this.productRepository.saveAndFlush(product);
optional.ifPresent(product -> {
product.setLinked(true);
this.productRepository.saveAndFlush(product);

event.addProduct(product);
});
event.addProduct(product);
});
}
}

return event;
Expand Down Expand Up @@ -101,7 +102,7 @@ private Event createEvent(JSONObject jsonObject) {
@Override
protected void loop(JSONObject jsonObject) {
Event event = this.createEvent(jsonObject);
event = this.addProduct(event, jsonObject);
event = this.addProducts(event, jsonObject);

this.eventRepository.save(event);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import ch.wisv.events.core.repository.ProductRepository;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Optional;

import ch.wisv.events.core.util.VatRate;
import org.json.simple.JSONObject;
Expand Down Expand Up @@ -51,6 +52,11 @@ private Product createProduct(JSONObject jsonObject) {
);
product.setKey((String) jsonObject.get("key"));
product.setMaxSoldPerCustomer(((Long) jsonObject.get("maxSoldPerCustomer")).intValue());
if (jsonObject.get("parentProductNumber") != null) {
Optional<Product> optional = this.productRepository.findById(((Long) jsonObject.get("parentProductNumber")).intValue());

optional.ifPresent(product::setParentProduct);
}

return product;
}
Expand Down
12 changes: 6 additions & 6 deletions src/main/resources/dev/data/events.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"target": 50,
"maxSold": 100,
"imageUrl": "https://ch.tudelft.nl/wp-content/uploads/2017/01/pt-boards.jpg",
"productNumber": 1
"productNumbers": [1, 6]
},
{
"title": "Symposium",
Expand All @@ -17,7 +17,7 @@
"target": 50,
"maxSold": 100,
"imageUrl": "https://ch.tudelft.nl/wp-content/uploads/2017/01/pt-boards.jpg",
"productNumber": 2
"productNumbers": [2]
},
{
"title": "MatCH CH vision Music Festival",
Expand All @@ -27,7 +27,7 @@
"target": 50,
"maxSold": 100,
"imageUrl": "https://ch.tudelft.nl/wp-content/uploads/2017/01/pt-boards.jpg",
"productNumber": 4
"productNumbers": [4]
},
{
"title": "WiFi Rally",
Expand All @@ -37,7 +37,7 @@
"target": 50,
"maxSold": 100,
"imageUrl": "https://ch.tudelft.nl/wp-content/uploads/2017/01/pt-boards.jpg",
"productNumber": 3
"productNumbers": [3]
},
{
"title": "Annu drinks",
Expand All @@ -47,6 +47,6 @@
"target": 50,
"maxSold": 100,
"imageUrl": "https://ch.tudelft.nl/wp-content/uploads/2017/01/pt-boards.jpg",
"productNumber": 5
"productNumbers": [5]
}
]
]
12 changes: 11 additions & 1 deletion src/main/resources/dev/data/products.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,15 @@
"maxSold": 250,
"maxSoldPerCustomer": 25,
"key": "7a5b9158-a9e4-40f5-ad7f-586092274e63"
},
{
"title": "T.U.E.S.Day lecture ticket zonder eten",
"description": "Ticket for the T.U.E.S.Day lecture. Zonder eten.",
"cost": 0.00,
"vatRate": "VAT_HIGH",
"maxSold": 100,
"maxSoldPerCustomer": 4,
"parentProductNumber": 1,
"key": "dd729c96-dacf-4eab-a2e8-19d2a1ad917d"
}
]
]
Loading

0 comments on commit a843b90

Please sign in to comment.