Skip to content
Snippets Groups Projects
Unverified Commit 458f0280 authored by meskio's avatar meskio :tent:
Browse files

Merge branch 'collected_arrived'

parents f7701292 0e2cd267
No related branches found
No related tags found
No related merge requests found
......@@ -67,8 +67,12 @@ func Init(dbPath string, signKey string, mail *Mail, r *mux.Router) error {
r.HandleFunc("/order/{id:[0-9]+}", a.authNum(a.GetOrder)).Methods("GET")
r.HandleFunc("/order/{id:[0-9]+}", a.authNumRole(a.UpdateOrder)).Methods("PUT")
r.HandleFunc("/order/{id:[0-9]+}", a.authNumRole(a.DeleteOrder)).Methods("DELETE")
r.HandleFunc("/order/{id:[0-9]+}/arrive", a.authNumRole(a.ArrivedOrder)).Methods("PUT")
r.HandleFunc("/order/{id:[0-9]+}/collected", a.authNum(a.CollectOrder)).Methods("PUT")
r.HandleFunc("/order/active", a.auth(a.ListActiveOrders)).Methods("GET")
r.HandleFunc("/order/picks", a.authOrderNum(a.ListOrderPicks)).Methods("GET")
r.HandleFunc("/order/unarrived", a.authNum(a.ListOrderUnarrived)).Methods("GET")
r.HandleFunc("/order/collectable", a.authNum(a.ListOrderCollectable)).Methods("GET")
r.HandleFunc("/order/{id:[0-9]+}/purchase", a.authNum(a.AddOrderPurchase)).Methods("POST")
return nil
}
......@@ -9,14 +9,19 @@ import (
"gorm.io/gorm/clause"
)
const (
updateOrderDuration = 5 * 24 * time.Hour
)
type Order struct {
gorm.Model
Name string `json:"name"`
Description string `json:"description"`
MemberNum int `json:"member_num" gorm:"column:member"`
Member *Member `json:"member,omitempty" gorm:"foreignKey:MemberNum;references:Num"`
Deadline time.Time `json:"deadline"`
Active bool `json:"active" gorm:"index"`
Name string `json:"name"`
Description string `json:"description"`
MemberNum int `json:"member_num" gorm:"column:member"`
Member *Member `json:"member,omitempty" gorm:"foreignKey:MemberNum;references:Num"`
Deadline time.Time `json:"deadline"`
Active bool `json:"active" gorm:"index"`
Arrived *time.Time `json:"arrived,omitempty"`
Products []OrderProduct `json:"products"`
Transactions []Transaction `json:"transactions" gorm:"foreignKey:OrderID"`
......@@ -60,6 +65,29 @@ func (d *DB) ListOrderPicks(num int) (orders []Order, err error) {
return
}
func (d *DB) ListOrderUnarrived(memberNum int) (orders []Order, err error) {
err = d.db.Preload(clause.Associations).
Preload("Transactions.OrderPurchase").
Where("member = ?", memberNum).
Where("deadline >= ?", time.Now().Add(-updateOrderDuration)).
Where("active is false").
Where("arrived is null").
Find(&orders).Error
return
}
func (d *DB) ListOrderCollectable(memberNum int) (transactions []Transaction, err error) {
err = d.db.Preload("OrderPurchase.OrderProduct.Product").
Preload("Order").
Joins("left join orders on transactions.order_id = orders.id").
Where("transactions.member = ?", memberNum).
Where("collected is null or collected = false").
Where("orders.Arrived is not null").
Find(&transactions).
Error
return
}
func (d *DB) GetOrder(memberNum int, id int) (order Order, transaction Transaction, err error) {
err = d.db.Preload(clause.Associations).
Preload("Products.Product").
......@@ -96,7 +124,7 @@ func (d *DB) UpdateOrder(memberNum int, id int, order *Order) error {
if memberNum != 0 && dbOrder.MemberNum != memberNum {
return ErrorInvalidRequest
}
if dbOrder.Deadline.Add(5 * 24 * time.Hour).Before(time.Now()) {
if dbOrder.Deadline.Add(updateOrderDuration).Before(time.Now()) {
return ErrorInvalidRequest
}
for _, p := range order.Products {
......@@ -193,6 +221,7 @@ func updateOrderTransaction(tx *gorm.DB, id int, total int, order *Order) error
return err
}
order.Active = true
order.Arrived = nil
order.TransactionID = nil
} else {
totalDiff := total - transaction.Total
......@@ -434,3 +463,35 @@ func (d *DB) DeactivateOrders() []Order {
}
return deactivatedOrders
}
func (d *DB) ArrivedOrder(memberNum int, id int) error {
dbOrder, _, err := d.GetOrder(0, id)
if err != nil {
return err
}
if memberNum != 0 && dbOrder.MemberNum != memberNum {
return ErrorInvalidRequest
}
if dbOrder.Deadline.Add(updateOrderDuration).Before(time.Now()) {
return ErrorInvalidRequest
}
if dbOrder.Active {
return ErrorInvalidRequest
}
return d.db.Model(&Order{}).Where("id = ?", id).Update("arrived", time.Now()).Error
}
func (d *DB) CollectOrder(memberNum int, id int) error {
dbOrder, t, err := d.GetOrder(memberNum, id)
if err != nil {
return err
}
if dbOrder.Active || dbOrder.Arrived == nil {
return ErrorInvalidRequest
}
return d.db.Model(&Transaction{}).Where("id = ?", t.ID).Update("collected", true).Error
}
......@@ -32,6 +32,7 @@ type Transaction struct {
Order *Order `json:"order,omitempty" gorm:"constraint:OnDelete:CASCADE"`
OrderID *uint `json:"-"`
Refund *Order `json:"refund,omitempty" gorm:"foreignKey:TransactionID"`
Collected *bool `json:"collected,omitempty"`
}
type Topup struct {
......
......@@ -73,6 +73,40 @@ func (a *api) ListOrderPicks(num int, w http.ResponseWriter, req *http.Request)
}
}
func (a *api) ListOrderUnarrived(num int, w http.ResponseWriter, req *http.Request) {
orders, err := a.db.ListOrderUnarrived(num)
if err != nil {
log.Printf("Can't list unarrived orders: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
err = json.NewEncoder(w).Encode(orders)
if err != nil {
log.Printf("Can't encode unarrived orders: %v", err)
w.WriteHeader(http.StatusInternalServerError)
}
}
func (a *api) ListOrderCollectable(num int, w http.ResponseWriter, req *http.Request) {
transactions, err := a.db.ListOrderCollectable(num)
if err != nil {
log.Printf("Can't list collectable orders: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
err = json.NewEncoder(w).Encode(transactions)
if err != nil {
log.Printf("Can't encode collectable orders: %v", err)
w.WriteHeader(http.StatusInternalServerError)
}
}
func (a *api) GetOrder(num int, w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
id, _ := strconv.Atoi(vars["id"])
......@@ -230,3 +264,50 @@ func (a *api) AddOrderPurchase(num int, w http.ResponseWriter, req *http.Request
w.WriteHeader(http.StatusInternalServerError)
}
}
func (a *api) ArrivedOrder(num int, role string, w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
id, _ := strconv.Atoi(vars["id"])
if role == "admin" {
num = 0
}
err := a.db.ArrivedOrder(num, id)
if err != nil {
if errors.Is(err, db.ErrorNotFound) {
w.WriteHeader(http.StatusNotFound)
return
}
if errors.Is(err, db.ErrorInvalidRequest) {
w.WriteHeader(http.StatusUnauthorized)
return
}
log.Printf("Can't arrive order %s: %v", vars["id"], err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusAccepted)
}
func (a *api) CollectOrder(num int, w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
id, _ := strconv.Atoi(vars["id"])
err := a.db.CollectOrder(num, id)
if err != nil {
if errors.Is(err, db.ErrorNotFound) {
w.WriteHeader(http.StatusNotFound)
return
}
if errors.Is(err, db.ErrorInvalidRequest) {
w.WriteHeader(http.StatusUnauthorized)
return
}
log.Printf("Can't collect order %s: %v", vars["id"], err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusAccepted)
}
import React, { useState } from "react";
import { Card, Row, Col, Button } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
import Sender from "../Sender";
import Fetcher from "../Fetcher";
function OrderCards() {
const [orders, setOrders] = useState([]);
const order_list = orders.map((o) => (
<Card key={o.ID} as={Col} sm={4}>
const [unarrived, setUnarrived] = useState([]);
const [collectable, setCollectable] = useState([]);
const [active, setActive] = useState([]);
const [refetch, setRefetch] = useState(0);
const unarrived_list = unarrived.map((o) => (
<Card bg="dark" text="white" key={o.ID} as={Col} sm={4}>
<Card.Body>
<Card.Title>{o.name}</Card.Title>
<Card.Text>{o.description}</Card.Text>
<LinkContainer to={"/order/" + o.ID}>
<Button>Realizar pedido</Button>
<div>
<Card.Title>{o.name}</Card.Title>
<Card.Text>{o.description}</Card.Text>
</div>
</LinkContainer>
<Sender
url={"/api/order/" + o.ID + "/arrive"}
method="PUT"
onSuccess={() => setRefetch(refetch + 1)}
>
<Button type="submit" variant="light">
Informar llegada
</Button>
</Sender>
</Card.Body>
</Card>
));
const collectable_list = collectable.map((t) => {
const purchase_list = t.order_purchase.map((o) => {
if (o.amount === 0) {
return "";
}
return (
<div key={o.order_product.code}>
{o.order_product.product.name}: {o.amount}
<br />
</div>
);
});
return (
<Card bg="secondary" text="white" key={"T-" + t.ID} as={Col} sm={4}>
<Card.Body>
<LinkContainer to={"/order/" + t.order.ID}>
<div>
<Card.Title>{t.order.name}</Card.Title>
<Card.Text>{purchase_list}</Card.Text>
</div>
</LinkContainer>
<Sender
url={"/api/order/" + t.order.ID + "/collected"}
method="PUT"
onSuccess={() => setRefetch(refetch + 1)}
>
<Button type="submit" variant="light">
Recogido
</Button>
</Sender>
</Card.Body>
</Card>
);
});
const active_list = active.map((o) => (
<LinkContainer to={"/order/" + o.ID}>
<Card bg="info" text="white" key={o.ID} as={Col} sm={4}>
<Card.Body>
<Card.Title>{o.name}</Card.Title>
<Card.Text>{o.description}</Card.Text>
<Button variant="light">Realizar pedido</Button>
</Card.Body>
</Card>
</LinkContainer>
));
return (
<Fetcher url={"/api/order/active"} onFetch={setOrders}>
<Row>{order_list}</Row>
</Fetcher>
<Row>
<Fetcher
url={"/api/order/unarrived"}
onFetch={setUnarrived}
refetch={refetch}
>
{unarrived_list}
</Fetcher>
<Fetcher
url={"/api/order/collectable"}
onFetch={setCollectable}
refetch={refetch}
>
{collectable_list}
</Fetcher>
<Fetcher url={"/api/order/active"} onFetch={setActive}>
{active_list}
</Fetcher>
</Row>
);
}
......
......@@ -13,6 +13,7 @@ import {
Modal,
} from "react-bootstrap";
import PurchaseOrder from "./PurchaseOrder";
import Sender from "../Sender";
import { printDate } from "../util";
import AuthContext from "../AuthContext";
import { printMoney, url } from "../util";
......@@ -68,6 +69,7 @@ function ShowOrderResults(props) {
return (
<li key={t.member.num}>
{t.member.name} ({t.member.num}):
{t.collected && <Badge variant="success">Recogido</Badge>}
<ul>{list}</ul>
</li>
);
......@@ -173,29 +175,66 @@ class ShowOrder extends React.Component {
const { id } = this.props.match.params;
let update_button;
let collect_button;
if (this.state.isLoading) {
update_button = <Spinner animation="border" />;
} else {
let deadline_week = new Date(order.deadline);
deadline_week.setDate(deadline_week.getDate() + 7);
if (
(order.member_num === parseInt(this.context.num) ||
this.context.role === "admin") &&
deadline_week > Date.now()
) {
update_button = (
<ButtonGroup>
<LinkContainer to={"/order/edit/" + id}>
<Button variant="info">Modificar</Button>
</LinkContainer>
<Button
variant="danger"
onClick={() => this.setState({ showDelete: true })}
if (deadline_week > Date.now()) {
if (order.arrived && !this.state.transaction.collected) {
collect_button = (
<Sender
url={"/api/order/" + id + "/collected"}
method="PUT"
onSuccess={() =>
this.setState({ refetch: this.state.refetch + 1 })
}
>
Cancelar
</Button>
</ButtonGroup>
);
<Button type="submit" variant="secondary">
Recogido
</Button>
</Sender>
);
}
if (
order.member_num === parseInt(this.context.num) ||
this.context.role === "admin"
) {
let arrived_button;
if (!order.active && !order.arrived) {
arrived_button = (
<Sender
url={"/api/order/" + id + "/arrive"}
method="PUT"
onSuccess={() =>
this.setState({ refetch: this.state.refetch + 1 })
}
>
<Button type="submit" variant="dark">
Informar llegada
</Button>
</Sender>
);
}
update_button = (
<div>
<ButtonGroup>
<LinkContainer to={"/order/edit/" + id}>
<Button variant="info">Modificar</Button>
</LinkContainer>
<Button
variant="danger"
onClick={() => this.setState({ showDelete: true })}
>
Cancelar
</Button>
</ButtonGroup>
<br />
{arrived_button}
</div>
);
}
}
}
......@@ -223,6 +262,7 @@ class ShowOrder extends React.Component {
{expired}
</p>
{update_button}
{collect_button}
</Col>
</Row>
<p>{this.state.order.description}</p>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment