[Task]: add comments + comment quality saving
This commit is contained in:
parent
d38839c4f9
commit
99d62fcb47
16
README.md
16
README.md
|
@ -1,5 +1,5 @@
|
|||
# Stock-Microservice
|
||||
This is a microservice cutted out of a [Monolith](https://gitlab.com/matthiasstock/monolith).
|
||||
This microservice is cut out of a [Monolith](https://gitlab.com/matthiasstock/monolith).
|
||||
|
||||
[![Build Status](https://travis-ci.org/genofire/hs_master-kss-monolith.svg?branch=master)](https://travis-ci.org/genofire/hs_master-kss-monolith) [![CircleCI](https://circleci.com/gh/genofire/hs_master-kss-monolith/tree/master.svg?style=svg)](https://circleci.com/gh/genofire/hs_master-kss-monolith/tree/master) [![Coverage Status](https://coveralls.io/repos/github/genofire/hs_master-kss-monolith/badge.svg?branch=master)](https://coveralls.io/github/genofire/hs_master-kss-monolith?branch=master) [![GoDoc](https://godoc.org/github.com/genofire/hs_master-kss-monolith?status.svg)](https://godoc.org/github.com/genofire/hs_master-kss-monolith)
|
||||
|
||||
|
@ -9,14 +9,14 @@ This is a microservice cutted out of a [Monolith](https://gitlab.com/matthiassto
|
|||
* [Easy dummy Shop-Cart in browser-cache](https://stock.pub.warehost.de/dummy_cart/)
|
||||
|
||||
## Features of this stock mircoservice
|
||||
* The main functionality of the microservice is to store the goods with their storage location and the date, when they are too old to be sell.
|
||||
* The main functionality of the microservice is to store goods with their storage location and the date, when they are too old to sell.
|
||||
* Functionality of the admin frontend
|
||||
* Add new goods to the stock
|
||||
* Manually remove a single goods from the stock, for example when they are rancid
|
||||
* Remove single goods from the stock, when they are send to a costumer
|
||||
* Block goods from the stock, when a costumer adds them to his cart
|
||||
* Manually remove a single good from the stock, for example when it is fouled
|
||||
* Remove a single good from the stock, when it is send to a costumer
|
||||
* Block goods from the stock, when a costumer adds them to his shop-cart
|
||||
* Functionality of the costumer frontend
|
||||
* Show the store with a traffic light food labelling
|
||||
* Show the stock of a product with a traffic light food labelling system
|
||||
* Optional Features
|
||||
* Admin frontend: display of a statistic on the amount and average of goods in the stock
|
||||
* Admin frontend: display a traffic light food labelling for each good, which indicates whether the good is too old
|
||||
* Admin frontend: display of a statistic of the current and average amount of goods in the stock
|
||||
* Admin frontend: display a traffic light food labelling system for each good, which indicates whether the good is too old
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Package that containts the cmd binary of the microservice to run it
|
||||
// Package that contains the cmd binary of the microservice to run it
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -50,13 +50,15 @@ func main() {
|
|||
}
|
||||
grw := runtime.NewGoodReleaseWorker(config.GoodRelease)
|
||||
cw := runtime.NewCacheWorker()
|
||||
fw := worker.NewWorker(config.FouledDeleter.Duration, func() { runtime.GoodFouled() })
|
||||
fw := worker.NewWorker(config.FouledDeleter.Duration, func() {
|
||||
runtime.GoodFouled()
|
||||
})
|
||||
go grw.Start()
|
||||
go cw.Start()
|
||||
if config.FouledDeleter.Duration != time.Duration(0) {
|
||||
go fw.Start()
|
||||
}
|
||||
// Startwebsrver
|
||||
// Start webserver
|
||||
router := goji.NewMux()
|
||||
web.BindAPI(router)
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
# bind the webserver to a dynamic ports
|
||||
webserver_bind = ":65000"
|
||||
webroot = "webroot"
|
||||
|
||||
good_availablity_template = "contrib/good_availablity.svg"
|
||||
|
||||
[database]
|
||||
type = "sqlite3"
|
||||
# logging = true
|
||||
connection = "file::memory:?mode=memory&cache=shared"
|
||||
# For Master-Slave cluster
|
||||
# read_connection = ""
|
||||
|
||||
[good_release]
|
||||
every = "5m"
|
||||
after = "30m"
|
||||
|
||||
[cache_clean]
|
||||
every = "5m"
|
||||
after = "30m"
|
||||
|
||||
[microservice_dependencies]
|
||||
product = "http://localhost:65000/api-test/product/%d/"
|
||||
permission = "http://localhost:65000/api-test/session/%s/%d/"
|
|
@ -1,8 +1,7 @@
|
|||
<!-- SVG to show the current stock with a traffic light food labeling system -->
|
||||
<!-- Used functions -->
|
||||
<!-- process_radius ANZAHL MAXANZAHL RADIUS -->
|
||||
<!-- procent ANZAHL MAXANZAHL -->
|
||||
<!-- ex. {procent .Count 10}%-->
|
||||
<!-- process_radius ANZAHL MAXANZAHL RADIUS -->
|
||||
<!-- procent ANZAHL MAXANZAHL ex. {procent .Count 10}%-->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<g class="arcs">
|
||||
{{if eq .Count 0}}
|
||||
|
|
Before Width: | Height: | Size: 854 B After Width: | Height: | Size: 840 B |
|
@ -58,8 +58,9 @@ Die Packages und Go-Files des Application Layers umfassen die Logik des Microser
|
|||
\paragraph{http:} Go-Files, die die Anwendungslogik (Funktionen) und die API-Routen beinhalten
|
||||
\begin{itemize}
|
||||
\item \texttt{bindapi.go}: Funktionen, die für das Binden der URL-Pfade notwendig sind
|
||||
\item \texttt{good.go}: Funktionen für die Verwaltung der Waren im Warenbestand
|
||||
\item \texttt{good\_show.go}: Funktionen für die Auflistung und Zählung der vorhandenen Waren sowie die Feststellung ihrer Verfügbarkeit
|
||||
\item \texttt{good.go}: Funktionen für das Hinzufügen von Waren zum Warenbestand und die Anzeige, ob eine Ware abgelaufen ist
|
||||
\item \texttt{good\_lock.go}: Funktionen für das Blockieren von Waren, die sich im Warenkorb befinden
|
||||
\item \texttt{good\_show.go}: Funktionen für die Auflistung und Zählung der vorhandenen Waren sowie die Feststellung ihrer Verfügbarkeit
|
||||
\item \texttt{good\_temp.go}: Hilfsfunktionen, die für die Darstellung des Warenbestandes als Ampel im Kunden-Frontend benötigt werden
|
||||
\item \texttt{status.go}: Funktion, die den Status des Microservice abfragt
|
||||
\end{itemize}
|
||||
|
@ -77,9 +78,9 @@ Die Packages und Go-Files des Application Layers umfassen die Logik des Microser
|
|||
\begin{itemize}
|
||||
\item \texttt{auth.go}: Hilfsfunktionen zur Prüfung, ob eine Berechtigung für den Zugriff vorliegt
|
||||
\item \texttt{cache\_worker.go}: Hilfsfunktionen für das Löschen und Anlegen von Cache-Workers
|
||||
\item \texttt{good\_fouled.go}: Hilfsfunktion, um abgelaufene Waren automatisch aus dem Warenbestand zu entfernen
|
||||
\item \texttt{good\_release.go}: Hilfsfunktionen zum Blockieren und Entsperren von Waren
|
||||
\item \texttt{productcache.go}: Hilfsfunktionen zum Anlegen eines Caches für Produkte
|
||||
\item \texttt{runtime.go}: Übergreifende Hintergrundfunktionalitäten
|
||||
\end{itemize}
|
||||
|
||||
|
||||
|
@ -122,7 +123,7 @@ connection = "file::memory:?mode=memory&cache=shared"
|
|||
\newpage
|
||||
\subsection{Integrierte Tests}
|
||||
\label{subsec: Integrierte Test}
|
||||
Neben den bisherigen Packages, die bereits Whitebox-Tests umfassen, ist in dem Package \textbf{\texttt{test}} ein weiteres Go-File (\texttt{testRest.go}) enthalten. Dieses setzt einen Test des Webservers um, bei dem auf Testdaten eines Produktkataloges zurückgegriffen wird. Mit Hilfe der integrierten Tests wird in der hier beschriebenen Version eine Code-Coverage von 100\% erreicht, das heißt jedes Stück Code wird mindestens einmal zur Ausführung gebracht.
|
||||
Neben den bisherigen Packages, die bereits Whitebox-Tests umfassen, ist in dem Package \textbf{\texttt{test}} ein weiteres Go-File (\texttt{testrest.go}) enthalten. Dieses setzt einen Test des Webservers um, bei dem auf Testdaten eines Produktkataloges zurückgegriffen wird. Mit Hilfe der integrierten Tests wird in der hier beschriebenen Version eine Code-Coverage von 100\% erreicht, das heißt jedes Stück Code wird mindestens einmal zur Ausführung gebracht.
|
||||
|
||||
\subsection{Anpassung des Monolithen}
|
||||
\label{subsec: Anpassung des Monolithen}
|
||||
|
|
Binary file not shown.
18
http/good.go
18
http/good.go
|
@ -28,8 +28,8 @@ func addGood(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
id, err := strconv.ParseInt(pat.Param(r, "productid"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warn("false productid format")
|
||||
http.Error(w, "the product id is false", http.StatusNotAcceptable)
|
||||
log.Warn("false product id format")
|
||||
http.Error(w, "the product id has a false format", http.StatusNotAcceptable)
|
||||
return
|
||||
}
|
||||
log = log.WithField("productid", id)
|
||||
|
@ -40,7 +40,7 @@ func addGood(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
if !ok {
|
||||
log.Warn("false product, product not found")
|
||||
log.Warn("product not found")
|
||||
http.Error(w, "the product was not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func addGood(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if db.Error != nil {
|
||||
log.Error("database not able to write", db.Error)
|
||||
log.Error("database unable to write", db.Error)
|
||||
http.Error(w, "the product could not be written into the database", http.StatusInternalServerError)
|
||||
}
|
||||
lib.Write(w, &obj)
|
||||
|
@ -79,7 +79,7 @@ func delGood(w http.ResponseWriter, r *http.Request) {
|
|||
log := logger.HTTP(r)
|
||||
id, err := strconv.ParseInt(pat.Param(r, "goodid"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warn("wrong goodid format")
|
||||
log.Warn("wrong good id format")
|
||||
http.Error(w, "the good id has a false format", http.StatusNotAcceptable)
|
||||
return
|
||||
}
|
||||
|
@ -90,8 +90,8 @@ func delGood(w http.ResponseWriter, r *http.Request) {
|
|||
good.ID = id
|
||||
db := good.FilterAvailable(database.Read).First(&good)
|
||||
if db.RecordNotFound() {
|
||||
log.Warnf("good could not found: %s", db.Error)
|
||||
http.Error(w, "the good could not found", http.StatusNotFound)
|
||||
log.Warnf("could not find good: %s", db.Error)
|
||||
http.Error(w, "the good was not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
good.ManuelleDelete = true
|
||||
|
@ -99,8 +99,8 @@ func delGood(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
db = database.Write.Save(&good)
|
||||
if db.Error != nil {
|
||||
log.Warnf("good could not delete: %s", db.Error)
|
||||
http.Error(w, "the good could not delete", http.StatusInternalServerError)
|
||||
log.Warnf("could not delete good: %s", db.Error)
|
||||
http.Error(w, "the good could not be deleted", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Info("done")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package that contains all api routes of this microservice
|
||||
package http
|
||||
|
||||
import (
|
||||
|
@ -14,6 +15,7 @@ type LockGood struct {
|
|||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
// Function to lock goods
|
||||
func lockGoods(w http.ResponseWriter, r *http.Request) {
|
||||
log := logger.HTTP(r)
|
||||
secret := r.Header.Get("secret")
|
||||
|
@ -37,8 +39,8 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if len(goods) <= 0 {
|
||||
log.Warn("try to log nothing")
|
||||
http.Error(w, "try to log nothing", http.StatusBadRequest)
|
||||
log.Warn("tried to log nothing")
|
||||
http.Error(w, "tried to log nothing", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -47,9 +49,9 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
for _, good := range goods {
|
||||
if good.ProductID <= 0 {
|
||||
log.Warn("try to log nothing")
|
||||
log.Warn("tried to log nothing")
|
||||
tx.Rollback()
|
||||
http.Error(w, "try to log nothing", http.StatusBadRequest)
|
||||
http.Error(w, "tried to log nothing", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
for i := 0; i < good.Count; i++ {
|
||||
|
@ -68,7 +70,7 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
|
|||
if db.Error != nil || db.RowsAffected != 1 {
|
||||
http.Error(w, "the good was not found in database", http.StatusInternalServerError)
|
||||
tx.Rollback()
|
||||
log.Panic("more then one good locked: ", db.Error)
|
||||
log.Panic("there is more than one good locked: ", db.Error)
|
||||
return
|
||||
}
|
||||
count += 1
|
||||
|
@ -81,6 +83,7 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
}
|
||||
|
||||
// Function to release locked goods
|
||||
func releaseGoods(w http.ResponseWriter, r *http.Request) {
|
||||
log := logger.HTTP(r)
|
||||
secret := r.Header.Get("secret")
|
||||
|
@ -91,14 +94,14 @@ func releaseGoods(w http.ResponseWriter, r *http.Request) {
|
|||
result := db.RowsAffected
|
||||
|
||||
if err != nil {
|
||||
log.Warn("database error during release goods: ", err)
|
||||
http.Error(w, "secret could not validate", http.StatusInternalServerError)
|
||||
log.Warn("database error during the release of goods: ", err)
|
||||
http.Error(w, "the secret could not be validated", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if result <= 0 {
|
||||
log.Warn("no goods found")
|
||||
http.Error(w, "no goods found to release", http.StatusNotFound)
|
||||
http.Error(w, "there are no goods to release", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/genofire/hs_master-kss-monolith/test"
|
||||
)
|
||||
|
||||
// Function to test lockGoods()
|
||||
func TestLockGoods(t *testing.T) {
|
||||
assertion, router := test.Init(t)
|
||||
good := &models.Good{
|
||||
|
@ -58,6 +59,7 @@ func TestLockGoods(t *testing.T) {
|
|||
test.Close()
|
||||
}
|
||||
|
||||
// Function to test releaseGoods()
|
||||
func TestReleaseGoods(t *testing.T) {
|
||||
now := time.Now()
|
||||
assertion, router := test.Init(t)
|
||||
|
|
|
@ -38,7 +38,7 @@ func TestListGood(t *testing.T) {
|
|||
test.Close()
|
||||
}
|
||||
|
||||
// Function to getGoodAvailability() and getGoodAvailabilityCount()
|
||||
// Function to test getGoodAvailability() and getGoodAvailabilityCount()
|
||||
func TestGetGoodAvailable(t *testing.T) {
|
||||
now := time.Now()
|
||||
assertion, router := test.Init(t)
|
||||
|
@ -99,7 +99,7 @@ func TestGetGoodAvailable(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
// Function to getGoodFreshness()
|
||||
// Function to test getGoodFreshness()
|
||||
func TestGetGoodFreshness(t *testing.T) {
|
||||
now := time.Now().Add(36 * time.Hour)
|
||||
assertion, router := test.Init(t)
|
||||
|
|
|
@ -9,19 +9,19 @@ import (
|
|||
"text/template"
|
||||
)
|
||||
|
||||
// Path to the svg image template, that shows the availablity or freshness of a given good
|
||||
// Path to the svg image template, that shows the availability or freshness of a given good
|
||||
// with a traffic light food labeling system
|
||||
var GoodAvailabilityTemplate string
|
||||
var GoodFreshnessTemplate string
|
||||
|
||||
// Function to calculate a percent value from a given value and an maximum value
|
||||
// Function to calculate a percent value from a given value and a maximum value
|
||||
func tempPercent(value, max int) int {
|
||||
return value * 100 / max
|
||||
}
|
||||
|
||||
// Function to calculate a partial radius, depending on a percentage value
|
||||
func tempProcessRadius(value, max, radius int) float64 {
|
||||
return (1 - float64(value)/float64(max)) * float64(radius) * 2 * 3.14
|
||||
return (1 - float64(value) / float64(max)) * float64(radius) * 2 * 3.14
|
||||
}
|
||||
|
||||
// Function to get the SVG, that shows the availability with a traffic light food labeling system for a given good
|
||||
|
|
|
@ -73,14 +73,14 @@ func TestAddGood(t *testing.T) {
|
|||
runtime.HasPermission("testsessionkey", runtime.PermissionCreateGood)
|
||||
runtime.CleanCache()
|
||||
|
||||
// Test gatewaytimeout on product exists
|
||||
// Test the gatewaytimeout on product exists
|
||||
_, w = session.JSONRequest("POST", "/api/good/1", good)
|
||||
assertion.Equal(http.StatusGatewayTimeout, w.StatusCode)
|
||||
|
||||
time.Sleep(time.Duration(10) * time.Millisecond)
|
||||
runtime.CleanCache()
|
||||
|
||||
// Test gatewaytimeout on permission exists
|
||||
// Test the gatewaytimeout on permission exists
|
||||
_, w = session.JSONRequest("POST", "/api/good/1", good)
|
||||
assertion.Equal(http.StatusGatewayTimeout, w.StatusCode)
|
||||
|
||||
|
|
|
@ -23,14 +23,14 @@ var (
|
|||
|
||||
// Configuration of the database connection
|
||||
type Config struct {
|
||||
// type of the database, currently supports sqlite and postgres
|
||||
Type string
|
||||
// connection configuration
|
||||
Connection string
|
||||
// create another connection for reading only
|
||||
// Type of the database (currently supports sqlite and postgres)
|
||||
Type string
|
||||
// Connection configuration
|
||||
Connection string
|
||||
// Create another connection for reading only
|
||||
ReadConnection string
|
||||
// enable logging of the generated sql string
|
||||
Logging bool
|
||||
// Enable logging of the generated sql string
|
||||
Logging bool
|
||||
}
|
||||
|
||||
// Function to open a database and set the given configuration
|
||||
|
|
|
@ -22,7 +22,7 @@ func Read(r *http.Request, to interface{}) (err error) {
|
|||
func Write(w http.ResponseWriter, data interface{}) {
|
||||
js, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to encode response: "+err.Error(), http.StatusInternalServerError)
|
||||
http.Error(w, "failed to encode response: " + err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Package with a lib for cronjobs to run in background
|
||||
// Package with a lib for cronjobs to run in the background
|
||||
package worker
|
||||
|
||||
import "time"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Package with a lib for cronjobs to run in background
|
||||
// Package with a lib for cronjobs to run in the background
|
||||
package worker
|
||||
|
||||
import (
|
||||
|
@ -14,7 +14,7 @@ func TestWorker(t *testing.T) {
|
|||
|
||||
runtime := 0
|
||||
|
||||
w := NewWorker(time.Duration(5)*time.Millisecond, func() {
|
||||
w := NewWorker(time.Duration(5) * time.Millisecond, func() {
|
||||
runtime = runtime + 1
|
||||
})
|
||||
go w.Start()
|
||||
|
|
|
@ -13,27 +13,27 @@ import (
|
|||
// Config file for this daemon (more information at the config_example.conf in this git repository)
|
||||
type Config struct {
|
||||
// address under which the api and static content of the webserver runs
|
||||
WebserverBind string `toml:"webserver_bind"`
|
||||
WebserverBind string `toml:"webserver_bind"`
|
||||
|
||||
// path to deliver static content
|
||||
Webroot string `toml:"webroot"`
|
||||
Webroot string `toml:"webroot"`
|
||||
|
||||
Database database.Config `toml:"database"`
|
||||
GoodRelease GoodReleaseConfig `toml:"good_release"`
|
||||
CacheClean CacheWorkerConfig `toml:"cache_clean"`
|
||||
Database database.Config `toml:"database"`
|
||||
GoodRelease GoodReleaseConfig `toml:"good_release"`
|
||||
CacheClean CacheWorkerConfig `toml:"cache_clean"`
|
||||
|
||||
// path to the svg image templates to show the availablity and freshness
|
||||
// path to the SVG image templates to show the availability and freshness
|
||||
// of a given good with a traffic light food labeling system
|
||||
GoodAvailabilityTemplate string `toml:"good_availablity_template"`
|
||||
GoodFreshnessTemplate string `toml:"good_freshness_template"`
|
||||
|
||||
FouledDeleter Duration `toml:"fouled_deleted"`
|
||||
FouledDeleter Duration `toml:"fouled_deleted"`
|
||||
|
||||
// URLs to other microservices that this services uses
|
||||
// URLs to other microservices, which this service uses
|
||||
MicroserviceDependencies struct {
|
||||
Product string `toml:"product"`
|
||||
Permission string `toml:"permission"`
|
||||
} `toml:"microservice_dependencies"`
|
||||
Product string `toml:"product"`
|
||||
Permission string `toml:"permission"`
|
||||
} `toml:"microservice_dependencies"`
|
||||
}
|
||||
|
||||
// Configuration of the Worker to clean the cache from values of other microservice
|
||||
|
@ -48,7 +48,7 @@ type CacheWorkerConfig struct {
|
|||
type GoodReleaseConfig struct {
|
||||
// Run worker every Duration
|
||||
Every Duration `toml:"every"`
|
||||
// Unlock those which are not used since Duration
|
||||
// Unlock those, which are not used since Duration
|
||||
After Duration `toml:"after"`
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// Duration is a TOML datatype
|
||||
// A duration string is a possibly signed sequence of decimal numbers and a unit suffix,
|
||||
// A duration string is a possibly signed sequence of decimal numbers and a unit suffix,
|
||||
// such as "300s", "1.5h" or "5d". Valid time units are "s", "m", "h", "d", "w".
|
||||
type Duration struct {
|
||||
time.Duration
|
||||
|
@ -28,8 +28,8 @@ func (d *Duration) UnmarshalTOML(dataInterface interface{}) error {
|
|||
return fmt.Errorf("invalid duration: \"%s\"", data)
|
||||
}
|
||||
|
||||
unit := data[len(data)-1]
|
||||
value, err := strconv.Atoi(string(data[:len(data)-1]))
|
||||
unit := data[len(data) - 1]
|
||||
value, err := strconv.Atoi(string(data[:len(data) - 1]))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse duration %s: %s", data, err)
|
||||
}
|
||||
|
|
|
@ -10,18 +10,18 @@ import (
|
|||
"github.com/genofire/hs_master-kss-monolith/lib/database"
|
||||
)
|
||||
|
||||
// Goods managed in this stock microservice
|
||||
// Type of goods managed in this stock microservice
|
||||
type Good struct {
|
||||
ID int64 `json:"id"`
|
||||
ProductID int64 `json:"product_id"`
|
||||
Position string `json:"position"`
|
||||
Comment string `json:"comment"`
|
||||
FouledAt *time.Time `json:"fouled_at"`
|
||||
ID int64 `json:"id"`
|
||||
ProductID int64 `json:"product_id"`
|
||||
Position string `json:"position"`
|
||||
Comment string `json:"comment"`
|
||||
FouledAt *time.Time `json:"fouled_at"`
|
||||
|
||||
RecievedAt *time.Time `sql:"default:current_timestamp" json:"recieved_at"`
|
||||
RecievedAt *time.Time `sql:"default:current_timestamp" json:"recieved_at"`
|
||||
// Make it temporary unusable
|
||||
LockedAt *time.Time `json:"-"`
|
||||
LockedSecret string `json:"-"`
|
||||
LockedAt *time.Time `json:"-"`
|
||||
LockedSecret string `json:"-"`
|
||||
// Make it unusable
|
||||
DeletedAt *time.Time `json:"-"`
|
||||
ManuelleDelete bool `json:"-"`
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
// Package with the mostly static content (models) of this microservice
|
||||
package models
|
||||
|
||||
// Store all the structs
|
|
@ -16,108 +16,109 @@ import de.mstock.monolith.web.ReviewDTO;
|
|||
public class DataTransferObjectFactory {
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Data Transfer Object (DTO).
|
||||
*
|
||||
* @param category database entity
|
||||
* @param locale the requested locale
|
||||
* @return DTO
|
||||
*/
|
||||
public CategoryDTO createCategoryDTO(Category category, Locale locale) {
|
||||
CategoryI18n i18n = category.getI18n().get(locale.getLanguage());
|
||||
CategoryDTO categoryDTO = new CategoryDTO();
|
||||
categoryDTO.setName(i18n.getName());
|
||||
categoryDTO.setPrettyUrlFragment(i18n.getPrettyUrlFragment());
|
||||
return categoryDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Data Transfer Object (DTO).
|
||||
*
|
||||
* @param product database entity
|
||||
* @param locale the requested locale
|
||||
* @return DTO
|
||||
*/
|
||||
public ProductDTO createProductDTO(Product product, Locale locale) {
|
||||
return createProductDTO(product, locale, NumberFormat.getCurrencyInstance(locale));
|
||||
}
|
||||
|
||||
private ProductDTO createProductDTO(Product product, Locale locale, NumberFormat numberFormat) {
|
||||
ProductDTO productDTO = createProductWithoutReviewsDTO(product, locale, numberFormat);
|
||||
ProductI18n i18n = product.getI18n().get(locale.getLanguage());
|
||||
productDTO.setReviews(createReviewDTOs(i18n.getReviews()));
|
||||
return productDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Data Transfer Objects (DTOs).
|
||||
*
|
||||
* @param products database entities
|
||||
* @param locale the requested locale
|
||||
* @return DTOs
|
||||
*/
|
||||
public List<ProductDTO> createProductDTOs(List<Product> products, Locale locale) {
|
||||
List<ProductDTO> productDTOs = new ArrayList<>(products.size());
|
||||
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
|
||||
for (Product product : products) {
|
||||
productDTOs.add(createProductDTO(product, locale, numberFormat));
|
||||
/**
|
||||
* Creates a Data Transfer Object (DTO).
|
||||
*
|
||||
* @param category database entity
|
||||
* @param locale the requested locale
|
||||
* @return DTO
|
||||
*/
|
||||
public CategoryDTO createCategoryDTO(Category category, Locale locale) {
|
||||
CategoryI18n i18n = category.getI18n().get(locale.getLanguage());
|
||||
CategoryDTO categoryDTO = new CategoryDTO();
|
||||
categoryDTO.setName(i18n.getName());
|
||||
categoryDTO.setPrettyUrlFragment(i18n.getPrettyUrlFragment());
|
||||
return categoryDTO;
|
||||
}
|
||||
return productDTOs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Data Transfer Objects (DTOs) without loading their reviews.
|
||||
*
|
||||
* @param products database entities
|
||||
* @param locale the requested locale
|
||||
* @return DTOs
|
||||
*/
|
||||
public List<ProductDTO> createProductWithoutReviewsDTOs(List<Product> products, Locale locale) {
|
||||
List<ProductDTO> productDTOs = new ArrayList<>(products.size());
|
||||
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
|
||||
for (Product product : products) {
|
||||
productDTOs.add(createProductWithoutReviewsDTO(product, locale, numberFormat));
|
||||
/**
|
||||
* Creates a Data Transfer Object (DTO).
|
||||
*
|
||||
* @param product database entity
|
||||
* @param locale the requested locale
|
||||
* @return DTO
|
||||
*/
|
||||
public ProductDTO createProductDTO(Product product, Locale locale) {
|
||||
return createProductDTO(product, locale, NumberFormat.getCurrencyInstance(locale));
|
||||
}
|
||||
return productDTOs;
|
||||
}
|
||||
|
||||
private ProductDTO createProductWithoutReviewsDTO(Product product, Locale locale,
|
||||
NumberFormat numberFormat) {
|
||||
ProductI18n i18n = product.getI18n().get(locale.getLanguage());
|
||||
String price = numberFormat.format(i18n.getPrice());
|
||||
ProductDTO productDTO = new ProductDTO();
|
||||
productDTO.setId(product.getId());
|
||||
productDTO.setItemNumber(product.getItemNumber());
|
||||
productDTO.setUnit(product.getUnit());
|
||||
productDTO.setName(i18n.getName());
|
||||
productDTO.setPrettyUrlFragment(i18n.getPrettyUrlFragment());
|
||||
productDTO.setPrice(price);
|
||||
productDTO.setDescription(i18n.getDescription());
|
||||
return productDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Data Transfer Object (DTO).
|
||||
*
|
||||
* @param review database entity
|
||||
* @return DTO
|
||||
*/
|
||||
public ReviewDTO createReviewDTO(Review review) {
|
||||
ReviewDTO dto = new ReviewDTO();
|
||||
dto.setLanguage(review.getLocaleLanguage());
|
||||
dto.setRatingStars(review.getRatingStars());
|
||||
dto.setFirstName(review.getFirstName());
|
||||
dto.setLastName(review.getLastName());
|
||||
dto.setText(review.getText());
|
||||
return dto;
|
||||
}
|
||||
|
||||
private List<ReviewDTO> createReviewDTOs(List<Review> reviews) {
|
||||
List<ReviewDTO> ratingDTOs = new ArrayList<>(reviews.size());
|
||||
for (Review review : reviews) {
|
||||
ratingDTOs.add(createReviewDTO(review));
|
||||
private ProductDTO createProductDTO(Product product, Locale locale, NumberFormat numberFormat) {
|
||||
ProductDTO productDTO = createProductWithoutReviewsDTO(product, locale, numberFormat);
|
||||
ProductI18n i18n = product.getI18n().get(locale.getLanguage());
|
||||
productDTO.setReviews(createReviewDTOs(i18n.getReviews()));
|
||||
return productDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Data Transfer Objects (DTOs).
|
||||
*
|
||||
* @param products database entities
|
||||
* @param locale the requested locale
|
||||
* @return DTOs
|
||||
*/
|
||||
public List<ProductDTO> createProductDTOs(List<Product> products, Locale locale) {
|
||||
List<ProductDTO> productDTOs = new ArrayList<>(products.size());
|
||||
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
|
||||
for (Product product : products) {
|
||||
productDTOs.add(createProductDTO(product, locale, numberFormat));
|
||||
}
|
||||
return productDTOs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Data Transfer Objects (DTOs) without loading their reviews.
|
||||
*
|
||||
* @param products database entities
|
||||
* @param locale the requested locale
|
||||
* @return DTOs
|
||||
*/
|
||||
public List<ProductDTO> createProductWithoutReviewsDTOs(List<Product> products, Locale locale) {
|
||||
List<ProductDTO> productDTOs = new ArrayList<>(products.size());
|
||||
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
|
||||
for (Product product : products) {
|
||||
productDTOs.add(createProductWithoutReviewsDTO(product, locale, numberFormat));
|
||||
}
|
||||
return productDTOs;
|
||||
}
|
||||
|
||||
private ProductDTO createProductWithoutReviewsDTO(Product product, Locale locale,
|
||||
NumberFormat numberFormat) {
|
||||
ProductI18n i18n = product.getI18n().get(locale.getLanguage());
|
||||
String price = numberFormat.format(i18n.getPrice());
|
||||
ProductDTO productDTO = new ProductDTO();
|
||||
// Addition: productDTO.setID()
|
||||
productDTO.setId(product.getId());
|
||||
productDTO.setItemNumber(product.getItemNumber());
|
||||
productDTO.setUnit(product.getUnit());
|
||||
productDTO.setName(i18n.getName());
|
||||
productDTO.setPrettyUrlFragment(i18n.getPrettyUrlFragment());
|
||||
productDTO.setPrice(price);
|
||||
productDTO.setDescription(i18n.getDescription());
|
||||
return productDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Data Transfer Object (DTO).
|
||||
*
|
||||
* @param review database entity
|
||||
* @return DTO
|
||||
*/
|
||||
public ReviewDTO createReviewDTO(Review review) {
|
||||
ReviewDTO dto = new ReviewDTO();
|
||||
dto.setLanguage(review.getLocaleLanguage());
|
||||
dto.setRatingStars(review.getRatingStars());
|
||||
dto.setFirstName(review.getFirstName());
|
||||
dto.setLastName(review.getLastName());
|
||||
dto.setText(review.getText());
|
||||
return dto;
|
||||
}
|
||||
|
||||
private List<ReviewDTO> createReviewDTOs(List<Review> reviews) {
|
||||
List<ReviewDTO> ratingDTOs = new ArrayList<>(reviews.size());
|
||||
for (Review review : reviews) {
|
||||
ratingDTOs.add(createReviewDTO(review));
|
||||
}
|
||||
return Collections.unmodifiableList(ratingDTOs);
|
||||
}
|
||||
return Collections.unmodifiableList(ratingDTOs);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,16 +17,18 @@ public class HomepageController {
|
|||
|
||||
@Autowired
|
||||
private ShopService shopService;
|
||||
// Addition: contant with the address of the stock microservice adminfrontend
|
||||
private final String STOCKADMINFRONTENDTEMPLATE = "https://stock.pub.warehost.de/index.html";
|
||||
|
||||
/**
|
||||
* Redirect
|
||||
* Redirect to stock admin frontend
|
||||
*
|
||||
* @param model Template model
|
||||
* @param model Template model
|
||||
* @return The constant template name for the stock admin frontend.
|
||||
*/
|
||||
@RequestMapping(value = "/stockadmin", method = RequestMethod.GET)
|
||||
public String redirect(Model model) {return "redirect:"+ this.STOCKADMINFRONTENDTEMPLATE;
|
||||
public String redirect(Model model) {
|
||||
return "redirect:" + this.STOCKADMINFRONTENDTEMPLATE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,7 @@ public class ProductDTO {
|
|||
private String description;
|
||||
private List<ReviewDTO> reviews;
|
||||
|
||||
// Addition: int id, getId() and setID()
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,10 @@
|
|||
</div>
|
||||
<div class="col-md-8">
|
||||
<h2 th:text="${product.name}">Product Name</h2>
|
||||
|
||||
<!-- Addition: traffic light food labeling system of the stock microservice -->
|
||||
<img width="10%" class="icon" th:src="${'https://stock.pub.warehost.de/api/good/availablity/'+product.id}"/>
|
||||
|
||||
<p class="text-info text-uppercase" th:text="${product.price}">0,00 Euro</p>
|
||||
<p class="lead" th:text="${product.description}">Description.</p>
|
||||
<div th:replace="fragments/reviews :: reviews"></div>
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head th:replace="fragments/skeleton :: head">
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="refresh" content="5; URL=http://localhost:65000/"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="../static/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="../static/css/mosh.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div th:replace="fragments/skeleton :: navigation">
|
||||
<div class="container">
|
||||
<nav>Navigation</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container product">
|
||||
|
||||
<div class="row info">
|
||||
<p align="center"><a href="http://localhost:65000/">If the automatic redirection to the stock management admin
|
||||
front end does not work, click here.</a></p>
|
||||
</div>
|
||||
<footer th:replace="fragments/skeleton :: footer">
|
||||
<p>© 2017</p>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="../static/js/jquery-3.1.1.min.js"
|
||||
th:src="@{/js/jquery-3.1.1.min.js}"></script>
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
<script src="../static/js/bootstrap.min.js"
|
||||
th:src="@{/js/bootstrap.min.js}"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -10,20 +10,20 @@ import (
|
|||
"sync"
|
||||
)
|
||||
|
||||
// URL to the microservice which manages permissions
|
||||
// URL to the microservice, which manages permissions
|
||||
var PermissionURL string
|
||||
|
||||
// Type of permission
|
||||
type Permission int
|
||||
|
||||
// Some permissions (the real permissions need to come from the permission microservice)
|
||||
// Some permissions (the real permissions need to come from a permission microservice)
|
||||
const (
|
||||
// permission to add goods to the stock
|
||||
// e.g. if a good is received and now available to sell
|
||||
// e.g. if a good is received and now available for selling
|
||||
PermissionCreateGood = 1
|
||||
|
||||
// permission to delete goods from the stock
|
||||
// e.g. if a good becomes fouled and has to be removed
|
||||
// e.g. if a good becomes fouled and has to be removed manually
|
||||
PermissionDeleteGood = 2
|
||||
)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/genofire/hs_master-kss-monolith/models"
|
||||
)
|
||||
|
||||
// Function to test the cache Worker
|
||||
// Function to test the cacheWorker
|
||||
func TestCacheWorker(t *testing.T) {
|
||||
|
||||
productExistCache[2] = boolMicroServiceCache{LastCheck: time.Now(), Value: true}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/genofire/hs_master-kss-monolith/models"
|
||||
)
|
||||
|
||||
// Function to remove automaticle goods after the are fouled
|
||||
// Function to automatically remove goods, if they are fouled
|
||||
func GoodFouled() int {
|
||||
var goods []*models.Good
|
||||
var g models.Good
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/genofire/hs_master-kss-monolith/models"
|
||||
)
|
||||
|
||||
// Function to test the unlocking of goods
|
||||
// Function to test fouledDelete()
|
||||
func TestFouledDelete(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
database.Open(database.Config{
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/genofire/hs_master-kss-monolith/models"
|
||||
)
|
||||
|
||||
// Function to test the unlocking of goods
|
||||
// Function to test goodRelease()
|
||||
func TestGoodRelease(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
database.Open(database.Config{
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Function to test, if and which products exist (get information from the products catalogue)
|
||||
// Function to test, if and which products exist (get information from the product catalogue)
|
||||
func TestProductExists(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
// Package with supporting functionality to run the microservice
|
||||
package runtime
|
||||
|
||||
// some mingled functionality to handle in background
|
||||
|
|
@ -25,6 +25,7 @@ type MockTransport struct {
|
|||
running bool
|
||||
}
|
||||
|
||||
// Function to use the http handler
|
||||
func (t *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if !t.running {
|
||||
return nil, errors.New("mock a error")
|
||||
|
@ -33,9 +34,13 @@ func (t *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||
t.Handler.ServeHTTP(w, req)
|
||||
return w.Result(), nil
|
||||
}
|
||||
|
||||
// Function to start the http handler
|
||||
func (t *MockTransport) Start() {
|
||||
t.running = true
|
||||
}
|
||||
|
||||
// Function to close/stop the http handler
|
||||
func (t *MockTransport) Close() {
|
||||
t.running = false
|
||||
}
|
||||
|
@ -73,7 +78,7 @@ func Close() {
|
|||
mock.Close()
|
||||
}
|
||||
|
||||
// Handle a test session with cookies
|
||||
// Struct to dandle a test session with cookies
|
||||
type Request struct {
|
||||
req *http.Request
|
||||
cookies []*http.Cookie
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"id": 1,
|
||||
"title": "Kiwi"
|
||||
"id": 1,
|
||||
"title": "Kiwi"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"id": 2,
|
||||
"title": "Blueberries"
|
||||
"id": 2,
|
||||
"title": "Blueberries"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"id": 3,
|
||||
"title": "Cherries"
|
||||
"id": 3,
|
||||
"title": "Cherries"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"id": 4,
|
||||
"title": "Potatoes"
|
||||
"id": 4,
|
||||
"title": "Potatoes"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"id": 5,
|
||||
"title": "Tomatoes"
|
||||
"id": 5,
|
||||
"title": "Tomatoes"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"id": 6,
|
||||
"title": "Rhubarb"
|
||||
"id": 6,
|
||||
"title": "Rhubarb"
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
[
|
||||
{"id":1, "title": "Kiwi"},
|
||||
{"id":2, "title": "Blueberries"},
|
||||
{"id":3, "title": "Cherries"},
|
||||
{"id":4, "title": "Potatoes"},
|
||||
{"id":5, "title": "Tomatoes"},
|
||||
{"id":6, "title": "Rhubarb"}
|
||||
{"id":1, "title": "Kiwi"},
|
||||
{"id":2, "title": "Blueberries"},
|
||||
{"id":3, "title": "Cherries"},
|
||||
{"id":4, "title": "Potatoes"},
|
||||
{"id":5, "title": "Tomatoes"},
|
||||
{"id":6, "title": "Rhubarb"}
|
||||
]
|
||||
|
|
|
@ -1,79 +1,81 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="/node_modules/semantic-ui-css/semantic.min.css" rel="stylesheet" />
|
||||
<link href="/static/css/main.css" rel="stylesheet" />
|
||||
<title>microStock Dummy Cart</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link href="/node_modules/semantic-ui-css/semantic.min.css" rel="stylesheet"/>
|
||||
<link href="/static/css/main.css" rel="stylesheet"/>
|
||||
<title>microStock Dummy Cart</title>
|
||||
</head>
|
||||
<body ng-app="microStockDummieCare" ng-controller="MainCtrl">
|
||||
<nav class="ui stackable inverted menu">
|
||||
<div class="ui container">
|
||||
<div class="header item">Dummy Cart</div>
|
||||
<div class="right menu">
|
||||
<a class="ui item" ng-click="reset()">
|
||||
<i class="undo icon"></i>
|
||||
Reset
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="ui container">
|
||||
<form class="ui form" ng-submit="add()">
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<div class="ui fluid search selection dropdown">
|
||||
<input name="country" type="hidden">
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">Select Product</div>
|
||||
<div class="menu">
|
||||
<div class="item" ng-repeat="item in products" data-value="{{item.id}}"><img class="icon" ng-src="{{'/api/good/availablity/'+item.id| reloadSrc}}"/>{{item.title}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<input placeholder="Count" type="number" min="1" max="50" ng-model="goods.count">
|
||||
</div>
|
||||
<div class="field">
|
||||
<button type="submit" class="ui button" tabindex="0">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<table class="ui table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Count</th>
|
||||
<th>Product</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="item in cart">
|
||||
<td>{{item.count}}</td>
|
||||
<td>{{getProduct(item.product_id).title}}</td>
|
||||
<td>
|
||||
<div class="ui button icon" ng-click="del(item)"><i class="icon trash"></i></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<footer class="ui vertical footer segment">
|
||||
<div class="ui center aligned container">
|
||||
<p>© 2017 MM / Go - Team</p>
|
||||
<nav class="ui stackable inverted menu">
|
||||
<div class="ui container">
|
||||
<div class="header item">Dummy Cart</div>
|
||||
<div class="right menu">
|
||||
<a class="ui item" ng-click="reset()">
|
||||
<i class="undo icon"></i>
|
||||
Reset
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="/node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="/node_modules/semantic-ui-css/semantic.min.js"></script>
|
||||
<script src="/node_modules/angular/angular.min.js"></script>
|
||||
<script src="/node_modules/angular-animate/angular-animate.min.js"></script>
|
||||
<script src="/node_modules/angular-ui-router/release/angular-ui-router.min.js"></script>
|
||||
<script src="/node_modules/angular-loading-bar/build/loading-bar.min.js"></script>
|
||||
<script>
|
||||
</nav>
|
||||
|
||||
<div class="ui container">
|
||||
<form class="ui form" ng-submit="add()">
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<div class="ui fluid search selection dropdown">
|
||||
<input name="country" type="hidden">
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">Select Product</div>
|
||||
<div class="menu">
|
||||
<div class="item" ng-repeat="item in products" data-value="{{item.id}}"><img class="icon"
|
||||
ng-src="{{'/api/good/availablity/'+item.id| reloadSrc}}"/>{{item.title}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<input placeholder="Count" type="number" min="1" max="50" ng-model="goods.count">
|
||||
</div>
|
||||
<div class="field">
|
||||
<button type="submit" class="ui button" tabindex="0">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<table class="ui table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Count</th>
|
||||
<th>Product</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="item in cart">
|
||||
<td>{{item.count}}</td>
|
||||
<td>{{getProduct(item.product_id).title}}</td>
|
||||
<td>
|
||||
<div class="ui button icon" ng-click="del(item)"><i class="icon trash"></i></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<footer class="ui vertical footer segment">
|
||||
<div class="ui center aligned container">
|
||||
<p>© 2017 MM / Go - Team</p>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="/node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="/node_modules/semantic-ui-css/semantic.min.js"></script>
|
||||
<script src="/node_modules/angular/angular.min.js"></script>
|
||||
<script src="/node_modules/angular-animate/angular-animate.min.js"></script>
|
||||
<script src="/node_modules/angular-ui-router/release/angular-ui-router.min.js"></script>
|
||||
<script src="/node_modules/angular-loading-bar/build/loading-bar.min.js"></script>
|
||||
<script>
|
||||
var config = {
|
||||
'microservice_dependencies': {
|
||||
'products': '/api-test/product/',
|
||||
|
@ -192,6 +194,7 @@
|
|||
};
|
||||
|
||||
}]);
|
||||
</script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,48 +1,49 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="/node_modules/semantic-ui-css/semantic.min.css" rel="stylesheet" />
|
||||
<link href="/static/css/main.css" rel="stylesheet" />
|
||||
<title>Stock Admin</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link href="/node_modules/semantic-ui-css/semantic.min.css" rel="stylesheet"/>
|
||||
<link href="/static/css/main.css" rel="stylesheet"/>
|
||||
<title>Stock Admin</title>
|
||||
</head>
|
||||
<body ng-app="microStock">
|
||||
<nav class="ui stackable inverted menu" ng-controller="GlobalCtrl">
|
||||
<nav class="ui stackable inverted menu" ng-controller="GlobalCtrl">
|
||||
<div class="ui container">
|
||||
<div class="header item">Stock Admin</div>
|
||||
<a class="item" ui-sref="list" ui-active="active">List</a>
|
||||
<a class="item" ui-sref="statistics" ui-active="active">Statistics</a><
|
||||
<div class="right menu">
|
||||
<a class="ui item" ng-click="login()">
|
||||
<i class="icon" ng-class="{'unlock':!loggedIn,'lock':loggedIn}"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="header item">Stock Admin</div>
|
||||
<a class="item" ui-sref="list" ui-active="active">List</a>
|
||||
<a class="item" ui-sref="statistics" ui-active="active">Statistics</a><
|
||||
<div class="right menu">
|
||||
<a class="ui item" ng-click="login()">
|
||||
<i class="icon" ng-class="{'unlock':!loggedIn,'lock':loggedIn}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
|
||||
<div class="ui container" ui-view=""></div>
|
||||
<div class="ui container" ui-view=""></div>
|
||||
|
||||
<footer class="ui vertical footer segment">
|
||||
<footer class="ui vertical footer segment">
|
||||
<div class="ui center aligned container">
|
||||
<p>© 2017 MM / Go - Team</p>
|
||||
<p>© 2017 MM / Go - Team</p>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="//cdn.jsdelivr.net/webshim/1.14.5/polyfiller.js"></script>
|
||||
<script>
|
||||
</footer>
|
||||
<script src="//cdn.jsdelivr.net/webshim/1.14.5/polyfiller.js"></script>
|
||||
<script>
|
||||
webshims.setOptions('forms-ext', {types: 'date'});
|
||||
webshims.polyfill('forms forms-ext');
|
||||
</script>
|
||||
<script src="/node_modules/angular/angular.min.js"></script>
|
||||
<script src="/node_modules/angular-animate/angular-animate.min.js"></script>
|
||||
<script src="/node_modules/angular-ui-router/release/angular-ui-router.min.js"></script>
|
||||
<script src="/node_modules/angular-loading-bar/build/loading-bar.min.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="/static/js/item.controller.js"></script>
|
||||
<script src="/static/js/item-add.controller.js"></script>
|
||||
<script src="/static/js/global.js"></script>
|
||||
<script src="/static/js/list.controller.js"></script>
|
||||
<script src="/static/js/statistics.controller.js"></script>
|
||||
|
||||
</script>
|
||||
<script src="/node_modules/angular/angular.min.js"></script>
|
||||
<script src="/node_modules/angular-animate/angular-animate.min.js"></script>
|
||||
<script src="/node_modules/angular-ui-router/release/angular-ui-router.min.js"></script>
|
||||
<script src="/node_modules/angular-loading-bar/build/loading-bar.min.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="/static/js/item.controller.js"></script>
|
||||
<script src="/static/js/item-add.controller.js"></script>
|
||||
<script src="/static/js/global.js"></script>
|
||||
<script src="/static/js/list.controller.js"></script>
|
||||
<script src="/static/js/statistics.controller.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 451 KiB |
|
@ -1,31 +1,31 @@
|
|||
<h1>
|
||||
{{product.title}}
|
||||
<a ui-sref="item({productid:product.id})">
|
||||
<i class="icon linkify"></i>
|
||||
</a>
|
||||
{{product.title}}
|
||||
<a ui-sref="item({productid:product.id})">
|
||||
<i class="icon linkify"></i>
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
<form class="ui form segment" ng-submit="submit()" ng-class="{'top attached':msg.type}">
|
||||
<div class="field">
|
||||
<label>Expiration Date</label>
|
||||
<input type="date" name="fouled_at" placeholder="Fouled at date (e.g. 2017-06-30)" ng-model="obj.fouled_at"
|
||||
required="" pattern="((?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|
|
||||
<div class="field">
|
||||
<label>Expiration Date</label>
|
||||
<input type="date" name="fouled_at" placeholder="Fouled at date (e.g. 2017-06-30)" ng-model="obj.fouled_at"
|
||||
required="" pattern="((?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|
|
||||
1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31)))?">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Position</label>
|
||||
<input type="text" name="position" placeholder="Location in Store", ng-model="obj.position">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Comment</label>
|
||||
<input type="text" name="comment" placeholder="Comment for this good", ng-model="obj.comment">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Count</label>
|
||||
<input type="number" name="count" ng-model="count" min="1">
|
||||
</div>
|
||||
<button class="ui button" type="submit">Submit</button>
|
||||
<div class="field">
|
||||
<label>Position</label>
|
||||
<input type="text" name="position" placeholder="Location in Store" , ng-model="obj.position">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Comment</label>
|
||||
<input type="text" name="comment" placeholder="Comment for this good" , ng-model="obj.comment">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Count</label>
|
||||
<input type="number" name="count" ng-model="count" min="1">
|
||||
</div>
|
||||
<button class="ui button" type="submit">Submit</button>
|
||||
</form>
|
||||
<div class="ui bottom attached message {{msg.type}}" ng-show="msg.type">
|
||||
{{msg.text}}
|
||||
{{msg.text}}
|
||||
</div>
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
<h1>
|
||||
{{obj.title}}
|
||||
<img class="icon" src="/api/good/availablity/{{obj.id}}"/>
|
||||
<a class="ui icon button mini right floated" ui-sref="item-add({productid: obj.id})"><i class="icon plus"></i></a>
|
||||
{{obj.title}}
|
||||
<img class="icon" src="/api/good/availablity/{{obj.id}}"/>
|
||||
<a class="ui icon button mini right floated" ui-sref="item-add({productid: obj.id})"><i class="icon plus"></i></a>
|
||||
</h1>
|
||||
<table class="ui table very basic">
|
||||
<tr>
|
||||
<td>Product ID</td>
|
||||
<td>{{obj.id}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Count</td>
|
||||
<td>{{list.length}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Product ID</td>
|
||||
<td>{{obj.id}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Count</td>
|
||||
<td>{{list.length}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2>Goods of this product</h2>
|
||||
<div class="ui warning message" ng-if="list.length <= 0">
|
||||
<p>There are no goods for this product.</p>
|
||||
<div class="ui warning message" ng-if="list.length <= 0">
|
||||
<p>There are no goods for this product.</p>
|
||||
</div>
|
||||
<table class="ui table list" ng-if="list.length > 0">
|
||||
<thead>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status of Freshness</th>
|
||||
<th>Location</th>
|
||||
<th>Comment</th>
|
||||
<th></th>
|
||||
<th>Status of Freshness</th>
|
||||
<th>Location</th>
|
||||
<th>Comment</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="item in list">
|
||||
<td><img class="icon" ng-src="{{'/api/good/freshness/'+item.id| reloadSrc}}"/></td>
|
||||
<td>{{item.position}}</td>
|
||||
<td>{{item.comment}}</td>
|
||||
<td><a class="ui icon button mini" ng-click="delete(item.id)"><i class="trash icon"></i></a></td>
|
||||
<td><img class="icon" ng-src="{{'/api/good/freshness/'+item.id| reloadSrc}}"/></td>
|
||||
<td>{{item.position}}</td>
|
||||
<td>{{item.comment}}</td>
|
||||
<td><a class="ui icon button mini" ng-click="delete(item.id)"><i class="trash icon"></i></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
<h1>List of Products</h1>
|
||||
<table class="ui very compact table">
|
||||
<thead>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="two wide">#</th>
|
||||
<th class="twelve wide">Productname</th>
|
||||
<th class="one wide">Amount</th>
|
||||
<th class="one wide"></th>
|
||||
<th class="two wide">#</th>
|
||||
<th class="twelve wide">Productname</th>
|
||||
<th class="one wide">Amount</th>
|
||||
<th class="one wide"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="item in list">
|
||||
<td>{{item.id}}</td>
|
||||
<td><a ui-sref="item({productid: item.id})">{{item.title}}</a></td>
|
||||
<td>
|
||||
<img class="icon" ng-src="{{'/api/good/availablity/'+item.id| reloadSrc}}"/>
|
||||
</td>
|
||||
<td>
|
||||
<a class="ui icon button" ui-sref="item-add({productid: item.id})"><i class="icon plus"></i></a>
|
||||
</td>
|
||||
<td>{{item.id}}</td>
|
||||
<td><a ui-sref="item({productid: item.id})">{{item.title}}</a></td>
|
||||
<td>
|
||||
<img class="icon" ng-src="{{'/api/good/availablity/'+item.id| reloadSrc}}"/>
|
||||
</td>
|
||||
<td>
|
||||
<a class="ui icon button" ui-sref="item-add({productid: item.id})"><i class="icon plus"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
<h1>Statistics</h1>
|
||||
<div class="ui statistics two">
|
||||
<div class="statistic">
|
||||
<div class="value">
|
||||
<i class="cube icon"></i>
|
||||
{{obj.good.count}}
|
||||
<div class="statistic">
|
||||
<div class="value">
|
||||
<i class="cube icon"></i>
|
||||
{{obj.good.count}}
|
||||
</div>
|
||||
<div class="label">
|
||||
Total Count of Goods
|
||||
</div>
|
||||
</div>
|
||||
<div class="label">
|
||||
Total Count of Goods
|
||||
<div class="statistic">
|
||||
<div class="value">
|
||||
<i class="cubes icon"></i>
|
||||
{{obj.good.avg}}
|
||||
</div>
|
||||
<div class="label">
|
||||
Avg Goods per Product
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<div class="value">
|
||||
<i class="cubes icon"></i>
|
||||
{{obj.good.avg}}
|
||||
</div>
|
||||
<div class="label">
|
||||
Avg Goods per Product
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Reference in New Issue