- Falcoz Alban (SA)
- Galli Evan (QA)
- Gripari Alexandre (Ops)
- Lassauniere Theo (PO)
- Orders within a group order can only be placed at the same restaurant
- We try to prepare the menu item during the schedule (30 min for example) before the delivery time. If the item can't be prepared because the schedule is too tight, we try to prepare it during the previous schedule, up until 2 hours before. If it can't be prepared during any of the schedules 2 hours before then the item is not available.
- Guest : A person browsing the platform without being authenticated
- Registered User : Any registered member of the campus (students, staff, ...)
- Restaurant Staff : A person employed by the restaurant to prepare meal
- Restaurant Manager : A restaurant staff that can update menus offering and opening hours
- Menu: All the menu items propose by a restaurant
- MenuItem: A item propose by a restaurant (ex : ice-cream, fries, burger, soda, ...)
@startuml
left to right direction
actor "Restaurant Manager" as manager
actor "Restaurant Staff" as restaurant
actor "Registered User" as registered_user
actor "Guest" as guest
actor "Payment system" as payment_system
rectangle {
usecase "Browse restaurants" as UC1
usecase "Browse restaurants by name" as UC1A
usecase "Browse restaurants by type of food" as UC1B
usecase "Browse restaurants by availability" as UC1C
usecase "Browse menu" as UC2
usecase "Update opening hours" as UC10
usecase "Update menus offerings" as UC20
usecase "Set preparation times" as UC30
usecase "Manage orders" as UC40
usecase "Place order" as UC3
usecase "Create group order" as UC4
usecase "Join group order" as UC4b
usecase "Validate group order" as UC5
usecase "Browse historic" as UC6
usecase "Validate payment" as UC7
}
manager --|> restaurant
restaurant --|> registered_user
registered_user --|> guest
guest ------> UC1
UC1 <. UC2 : extends
UC1 <|-- UC1A
UC1 <|-- UC1B
UC1 <|-- UC1C
UC3 --> UC1 : includes
registered_user ---> UC4
registered_user ---> UC4b
registered_user ---> UC6
UC4 --> UC3 : includes
UC4b --> UC3 : includes
UC3 --> UC7 : includes
UC7 <. UC5 : extends
UC7 <--- payment_system
restaurant --> UC40
manager --> UC10
manager --> UC20
UC20 <. UC30 : extends
@enduml
classDiagram
direction BT
namespace order {
class Address {
+ label() String
+ street() String
+ additional_address() String
+ postal_code() String
+ city() String
}
class AddressManager {
AddressManager INSTANCE
}
class GroupOrder {
+ closeOrder() void
+ pay(SingleOrder) boolean
+ getAvailableDeliveryTimes(LocalDateTime, int) List~LocalDateTime~
+ createOrder(String) SingleOrder
List~MenuItem~ availableMenu
List~SingleOrder~ orders
Address address
String restaurantId
LocalDateTime orderTime
double price
List~User~ users
Restaurant restaurant
Duration preparationTime
String groupCode
LocalDateTime deliveryTime
List~MenuItem~ items
Status status
}
class GroupOrderManager {
GroupOrderManager INSTANCE
}
class Order {
<<Interface>>
LocalDateTime orderTime
List~MenuItem~ availableMenu
List~User~ users
Restaurant restaurant
Duration preparationTime
String groupCode
Address address
LocalDateTime deliveryTime
String restaurantId
List~MenuItem~ items
Status status
}
class Saleable {
<<Interface>>
double price
}
class SingleOrder {
+ addMenuItem(MenuItem) void
+ removeMenuItem(MenuItem) void
+ pay() boolean
- updateDiscounts() void
Optional~GroupOrder~ groupOrder
List~MenuItem~ availableMenu
double subPrice
List~Discount~ discounts
Address address
String restaurantId
String id
User user
LocalDateTime orderTime
double price
List~User~ users
Restaurant restaurant
Duration preparationTime
List~Discount~ discountsToApplyNext
String groupCode
LocalDateTime deliveryTime
List~MenuItem~ items
Payment payment
Status status
String userId
}
class SingleOrderManager {
+ getOrdersByUser(String) List~SingleOrder~
+ getOrdersByGroup(String) List~SingleOrder~
SingleOrderManager INSTANCE
}
class Status {
<<enumeration>>
+ values() Status[]
+ valueOf(String) Status
}
}
namespace discounts {
class Discount {
+ isApplicable(SingleOrder) boolean
+ value(double) double
+ freeItems() List~MenuItem~
+ getNewPrice(double) double
+ canBeAppliedDirectly() boolean
boolean stackable
boolean expired
}
class DiscountBuilder {
+ stackable() DiscountBuilder
+ oneTimeOffer() DiscountBuilder
+ neverExpires() DiscountBuilder
+ unstackable() DiscountBuilder
+ appliesAfterOrder() DiscountBuilder
+ expiresAt(LocalDateTime) DiscountBuilder
+ appliesDuringOrder() DiscountBuilder
+ build() Discount
MenuItem[] freeItems
int ordersAmount
Criteria criteria
Role[] userRoles
Discounts discounts
Options options
double orderDiscount
int itemsAmount
int currentOrderItemsAmount
double orderCredit
}
}
namespace restaurant {
class MenuItem {
+ toString() String
+ equals(Object) boolean
+ hashCode() int
String name
Restaurant restaurant
Duration preparationTime
String restaurantName
double price
}
class OpeningTime {
+ toString() String
+ hashCode() int
+ equals(Object) boolean
LocalTime end
LocalTime start
}
class Schedule {
+ overlap(Schedule) boolean
+ compareTo(LocalDateTime) int
+ isBetween(LocalDateTime, LocalDateTime) boolean
+ contains(LocalDateTime) boolean
+ hashCode() int
+ toString() String
+ equals(Object) boolean
+ contains(Order) boolean
+ compareTo(Schedule) int
Duration totalCapacity
int nbPerson
Duration duration
LocalTime end
DayOfWeek dayOfWeek
LocalTime start
}
class Restaurant {
- getMaxCapacityLeft(LocalDateTime) Duration
+ hashCode() int
+ addSchedule(Schedule) void
+ removeDiscount(Discount) void
+ addOrder(Order) void
+ addMenuItem(MenuItem) void
+ discounts() List~Discount~
- capacityLeft(Schedule, LocalDateTime) Duration
+ toString() String
- canAddOrder(LocalDateTime, Duration) boolean
+ getOpeningTimes(DayOfWeek) List~OpeningTime~
+ getAvailableMenu(LocalDateTime) List~MenuItem~
+ equals(Object) boolean
+ addScheduleForPeriod(int, DayOfWeek, LocalTime, DayOfWeek, LocalTime) void
+ availableDiscounts(SingleOrder) List~Discount~
+ canHandle(Order, LocalDateTime) boolean
+ canDeliverAt(LocalDateTime) boolean
+ addDiscount(Discount) void
+ removeMenuItem(MenuItem) void
String name
TypeOfFood typeOfFood
List~Order~ orders
List~MenuItem~ fullMenu
Duration averagePreparationTime
Duration scheduleDuration
}
class RestaurantManager {
+ filterRestaurant(LocalDateTime) List~Restaurant~
+ filterRestaurant(String) List~Restaurant~
+ filterRestaurant(String, TypeOfFood, LocalDateTime) List~Restaurant~
+ filterRestaurant(TypeOfFood) List~Restaurant~
RestaurantManager INSTANCE
}
class TypeOfFood {
<<enumeration>>
+ values() TypeOfFood[]
+ valueOf(String) TypeOfFood
}
}
namespace payment {
class Payment {
+ date() LocalDateTime
+ amount() Double
}
class PaymentSystem {
+ pay(double) Optional~Payment~
}
}
namespace user {
class Role {
<<enumeration>>
+ valueOf(String) Role
+ values() Role[]
}
class User {
+ getOrders(String) List~SingleOrder~
+ getDiscountsToApplyNext(String) List~Discount~
String name
List~SingleOrder~ orders
Role role
List~Payment~ payments
String userId
}
class UserManager {
+ fillForDemo() void
UserManager INSTANCE
}
}
class AbstractManager~T~ {
+ clear() void
+ add(String, T) void
+ contains(String) boolean
+ remove(String) void
+ get(String) T
List~T~ all
}
class NotFoundException
class STEats {
+ getOpeningTimes(Restaurant) Map~DayOfWeek, List~ OpeningTime~~
+ changeDeliveryTime(LocalDateTime) void
+ closeGroupOrder() void
+ getAvailableDeliveryTimes(LocalDateTime, int) List~LocalDateTime~
+ removeMenuItem(MenuItem) void
+ canCloseGroupOrder() boolean
+ payOrder() boolean
+ addMenuItem(MenuItem) void
+ joinGroupOrder(String) void
- updateFullMenu() void
+ createGroupOrder(LocalDateTime, String, String) String
+ createOrder(LocalDateTime, String, String) void
List~MenuItem~ availableMenu
SingleOrder order
List~Restaurant~ allRestaurants
List~MenuItem~ fullMenu
List~MenuItem~ cart
String groupCode
double totalPrice
User user
List~String~ addresses
}
class STEatsController {
+ logging(String) STEats
}
AbstractManager~T~ ..> NotFoundException: «create»
AddressManager --> AbstractManager~T~
DiscountBuilder ..> Discount: «create»
GroupOrder ..> Order
GroupOrder ..> SingleOrder: «create»
GroupOrder "1" *--> "status 1" Status
GroupOrderManager --> AbstractManager~T~
MenuItem ..> Saleable
Order --> Saleable
PaymentSystem ..> Payment: «create»
Restaurant "1" *--> "discounts *" Discount
Restaurant "1" *--> "menu *" MenuItem
Restaurant ..> OpeningTime: «create»
Restaurant "1" *--> "orders *" Order
Restaurant ..> Schedule: «create»
Restaurant "1" *--> "schedules *" Schedule
Restaurant "1" *--> "typeOfFood 1" TypeOfFood
RestaurantManager --> AbstractManager~T~
STEats ..> GroupOrder: «create»
STEats "1" *--> "fullMenu *" MenuItem
STEats "1" *--> "order 1" SingleOrder
STEats ..> SingleOrder: «create»
STEats "1" *--> "user 1" User
STEatsController ..> STEats: «create»
SingleOrder "1" *--> "appliedDiscounts *" Discount
SingleOrder "1" *--> "items *" MenuItem
SingleOrder ..> Order
SingleOrder "1" *--> "payment 1" Payment
SingleOrder "1" *--> "status 1" Status
SingleOrderManager --> AbstractManager~T~
User "1" *--> "role 1" Role
UserManager --> AbstractManager~T~
UserManager ..> User: «create»
sequenceDiagram
actor Client
Client ->> STEats: joinGroupOrder(groupCode)
activate STEats
STEats ->> GroupOrderManager: <<static>><br />getInstance()
activate GroupOrderManager
GroupOrderManager -->> STEats: GroupOrderManager
STEats ->> GroupOrderManager: get(groupCode)
GroupOrderManager -->> STEats: GroupOrder
deactivate GroupOrderManager
STEats ->> GroupOrder: createOrder(userId)
activate GroupOrder
create participant SingleOrder
GroupOrder ->> SingleOrder: <<create>>
activate SingleOrder
SingleOrder ->> Restaurant: canHandle(order, deliveryTime)
activate Restaurant
Restaurant ->> Restaurant: getMaxCapacityLeft(deliveryTime)
loop For each schedule
Restaurant ->> Schedule: isBetween(deliveryTime - 2h, deliveryTime)
activate Schedule
note right of Schedule: Calls inside isBetween<br/>are not shown here for clarity
Schedule -->> Restaurant: boolean
deactivate Schedule
end
note right of Restaurant: Keep only schedules that are at<br/>most 2 hours before the delivery time
loop For each kept schedule
Restaurant ->> Restaurant: capacityLeft(schedule, deliveryDate)
loop For each, at least paid, order of the schedule at the given date
Restaurant ->> Order: getPreparationTime()
activate Order
Order -->> Restaurant: Duration
deactivate Order
note right of Restaurant: Durations are summed up
end
Restaurant ->> Schedule: getTotalCapacity()
activate Schedule
Schedule -->> Restaurant: Duration
deactivate Schedule
note right of Restaurant: Capacity is obtained by<br/>substracting the sum of the preparation<br/>times from the total capacity
end
note right of Restaurant: Keep the max obtained capacity
Restaurant ->> Restaurant: canAddOrder(deliveryTime, maxCapacity)
Restaurant ->> Restaurant: getAveragePreparationTime()
loop For each last 50 orders
Restaurant ->> Order: getPreparationTime()
activate Order
Order -->> Restaurant: Duration
deactivate Order
note right of Restaurant: Averages durations
end
note right of Restaurant: Check if the current number of orders<br/>is not too high compared to the average<br/>preparation time
Restaurant -->> SingleOrder: boolean
deactivate Restaurant
SingleOrder ->> SingleOrderManager: <<static>><br />getInstance()
activate SingleOrderManager
SingleOrderManager -->> SingleOrder: SingleOrderManager
SingleOrder ->> SingleOrderManager: add(singleOrderId, this)
SingleOrderManager -->> SingleOrder: #32;
deactivate SingleOrderManager
SingleOrder -->> GroupOrder: SingleOrder
GroupOrder -->> STEats: SingleOrder
deactivate GroupOrder
STEats ->> STEats: updateFullMenu()
STEats ->> Restaurant: getFullMenu()
activate Restaurant
Restaurant -->> STEats: List<MenuItem>
deactivate Restaurant
Client ->> STEats: getAvailableMenu()
STEats ->> SingleOrder: getAvailableMenu()
SingleOrder ->> Restaurant: getAvailableMenu(deliveryTime)
activate Restaurant
Restaurant ->> Restaurant: getMaxCapacityLeft(deliveryTime)
loop For each schedule
Restaurant ->> Schedule: isBetween(deliveryTime, deliveryTime - 2h)
activate Schedule
note right of Schedule: Calls inside isBetween<br/>are not shown here for clarity
Schedule -->> Restaurant: boolean
deactivate Schedule
end
note right of Restaurant: Only schedules that<br/>are at most 2 hours before<br/>the delivery time are considered
loop For each kept schedule
Restaurant ->> Restaurant: capacityLeft(schedule, deliveryTime)
note right of Restaurant: Calls inside capacityLeft<br/>are not shown here for clarity
loop For each item ordered during the schedule
Restaurant ->> MenuItem: getPreparationTime()
activate MenuItem
MenuItem -->> Restaurant: Duration
deactivate MenuItem
end
end
Restaurant -->> SingleOrder: List<MenuItem>
deactivate Restaurant
SingleOrder -->> STEats: List<MenuItem>
Client ->> STEats: addMenuItem(menuItem)
STEats ->> SingleOrder: addMenuItem(menuItem)
SingleOrder ->> SingleOrder: updateDiscounts()
note right of SingleOrder: updateDiscounts is represented<br/>in another chart for clarity
deactivate SingleOrder
deactivate STEats
sequenceDiagram
activate SingleOrder
SingleOrder ->> SingleOrder: updateDiscounts()
SingleOrder ->> Restaurant: availableDiscounts(order)
activate Restaurant
loop For each discount of the restaurant
Restaurant ->> Discount: isApplicable(order)
activate Discount
Discount ->> SingleOrder: getItems()
SingleOrder -->> Discount: List<MenuItem>
Discount ->> User: getOrders(restaurantId)
activate User
note right of User: Returns orders that<br/>are at least paid<br/>and placed at<br/>the same restaurant
User -->> Discount: List<SingleOrder>
deactivate User
note right of Discount: Check criteria like number of items<br/>in cart and amount of orders
loop For each returned order
Discount ->> SingleOrder: getItems()
SingleOrder -->> Discount: List<MenuItem>
note right of Discount: Used to count the<br/>total number of ordered items
end
Discount ->> User: getRole
activate User
User -->> Discount: Role
deactivate User
note right of Discount: Check the role criteria
Discount -->> Restaurant: boolean
deactivate Discount
end
loop For each applicable discount
Restaurant ->> Discount: isStackable()
activate Discount
Discount -->> Restaurant: boolean
deactivate Discount
note right of Restaurant: every stackable discounts will be applied
end
loop For each un-stackable discount
Restaurant ->> SingleOrder: getSubPrice()
loop For each menu item in the order
SingleOrder ->> MenuItem: getPrice()
activate MenuItem
MenuItem -->> SingleOrder: double
deactivate MenuItem
end
SingleOrder -->> Restaurant: double
Restaurant ->> Discount: value(subPrice)
activate Discount
note right of Discount: Calculates the new price<br/>after applying the discount
Discount -->> Restaurant: double
deactivate Discount
note right of Restaurant: Only the best un-stackable discount is applied
end
Restaurant -->> SingleOrder: List<Discount>
deactivate Restaurant
deactivate SingleOrder