diff --git a/src/App.js b/src/App.js
index 679b601437272008e38a21662097852f2c155325..8bb058954d85b71d178b57fc57eecf56b308908f 100644
--- a/src/App.js
+++ b/src/App.js
@@ -6,6 +6,8 @@ import Dashboard from './Dashboard';
 import Purchase from './purchase/Purchase';
 import Topup from './Topup';
 import ShowTransaction from './ShowTransaction';
+import ShowOrder from './order/ShowOrder';
+import CreateOrder from './order/CreateOrder';
 import AuthContext from './AuthContext';
 import SignIn from './SignIn';
 import Head from './Head';
@@ -31,6 +33,10 @@ function Panel(props) {
                 <Route path="/topup">
                     <Topup />
                 </Route>
+                <Route path="/order/create">
+                    <CreateOrder />
+                </Route>
+                <Route path="/order/:id" component={ShowOrder} />
                 <Route path="/">
                     <Dashboard />
                 </Route>
diff --git a/src/Dashboard.js b/src/Dashboard.js
index 6198dbb555ea057a837f028613b26c6ed7989b5d..42377cb220df8aeab17f353865968fbe4262dd7a 100644
--- a/src/Dashboard.js
+++ b/src/Dashboard.js
@@ -2,6 +2,7 @@ import React from 'react';
 import { Container, Row, Col, Button } from 'react-bootstrap';
 import AuthContext from './AuthContext';
 import Fetcher from './Fetcher';
+import OrderList from './order/OrderList';
 import TransactionList from './TransactionList';
 import { printMoney } from './util';
 
@@ -18,10 +19,10 @@ class Dashboard extends React.Component {
 
     render() {
         return (
-            <Fetcher
-                    url={"/api/member/me"}
-                    onFetch={member => this.setState({ balance: member.balance, name: member.name})} >
-                <Container>
+            <Container>
+                <Fetcher
+                        url={"/api/member/me"}
+                        onFetch={member => this.setState({ balance: member.balance, name: member.name})} >
                     <Row>
                         <Col xs>
                             <div className="text-right">
@@ -34,9 +35,10 @@ class Dashboard extends React.Component {
                             <Button variant="success" href="/purchase">Comprar</Button>
                         </Col>
                     </Row>
+                    <OrderList />
                     <TransactionList />
-                </Container>
-            </Fetcher>
+                </Fetcher>
+            </Container>
         );
     }
 }
diff --git a/src/Head.js b/src/Head.js
index 5241de98a056d85f34a60233a74e6c7d61b3ff21..b47ff38ff425020db86440c2ca5d8cbcd843105f 100644
--- a/src/Head.js
+++ b/src/Head.js
@@ -27,6 +27,7 @@ function Head(props) {
                 <Nav className="mr-auto" activeKey={location.pathname}>
                     <Nav.Link href="/products">Productos</Nav.Link>
                     <Nav.Link href="/purchase">Comprar</Nav.Link>
+                    <Nav.Link href="/order/create">Abrir pedido</Nav.Link>
                 </Nav>
                 {adminNav}
                 <Form inline>
diff --git a/src/Topup.js b/src/Topup.js
index 0139ab3d83bdcd90704676396ecae518d934a40b..b427600515157efaa505282b743b1a93c8ca93ce 100644
--- a/src/Topup.js
+++ b/src/Topup.js
@@ -140,7 +140,7 @@ class Topup extends React.Component {
                     </Form.Group>
                     <Form.Group as={Row}>
                         <Col sm={{ span: 10, offset: 2 }}>
-                            <Button type="submit">Envia</Button>
+                            <Button type="submit">Recarga</Button>
                         </Col>
                     </Form.Group>
                 </Form>
diff --git a/src/TransactionList.js b/src/TransactionList.js
index 2c5f4251fe2583b0a78dd5227eb7982ac2cd3f99..74d1002a0ef45d848d19a5fe083dccf0d505f5ae 100644
--- a/src/TransactionList.js
+++ b/src/TransactionList.js
@@ -3,6 +3,7 @@ import { Redirect } from 'react-router-dom';
 import BootstrapTable from 'react-bootstrap-table-next';
 import { FaShoppingBasket, FaMoneyBillAlt } from 'react-icons/fa';
 import { GiPayMoney, GiReceiveMoney } from 'react-icons/gi';
+import { HiClipboardCopy, HiClipboardList } from 'react-icons/hi';
 import Fetcher from './Fetcher';
 import { printMoney, printDate } from './util';
 
@@ -17,6 +18,10 @@ const columns = [
                         return <GiReceiveMoney />;
                     }
                     return <GiPayMoney />;
+                case "order":
+                    return <HiClipboardList />;
+                case "refund":
+                    return <HiClipboardCopy />;
                 default:
                     return <FaMoneyBillAlt />;
             };
diff --git a/src/order/CreateOrder.js b/src/order/CreateOrder.js
new file mode 100644
index 0000000000000000000000000000000000000000..740021e62b4dc1a6383a359fdabb8720965d4c77
--- /dev/null
+++ b/src/order/CreateOrder.js
@@ -0,0 +1,126 @@
+import React from 'react';
+import { Redirect } from 'react-router-dom';
+import { Container, Form, Row, Col, Button, Alert, Spinner } from 'react-bootstrap';
+import ProductPicker from '../ProductPicker';
+import AuthContext from '../AuthContext';
+
+function date2string(date) {
+    return date.toISOString().split("T")[0];
+}
+
+function daysAfterNow(days) {
+    let date = new Date();
+    date.setDate(date.getDate() + days);
+    return date;
+}
+
+class CreateOrder extends React.Component {
+    static contextType = AuthContext;
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            products: [],
+            name: "",
+            description: "",
+            deadline: daysAfterNow(3),
+            orderId: null,
+            isLoading: false,
+            error: null
+        };
+    }
+
+    submit(e) {
+        e.preventDefault();
+
+        this.setState({isLoading: true, error: null, noMoney: false});
+        const products = this.state.products.map(p => {
+            return { code: p.code };
+        });
+        const body = JSON.stringify({
+            name: this.state.name,
+            description: this.state.description,
+            deadline: this.state.deadline,
+            products
+        });
+        fetch("/api/order", {headers: {'x-authentication': this.context.token}, method: "POST", body})
+            .then(response => {
+                if (!response.ok) {
+                    throw new Error(response.status.toString()+' '+response.statusText);
+                }
+                return response.json();
+            })
+            .then(o => this.setState({orderId: o.ID, isLoading: false}))
+            .catch(error => this.setState({isLoading: false, error: error.message}));
+    }
+
+    render() {
+        if (this.state.isLoading) {
+            return <Spinner animation="border" />;
+        }
+
+        let alert;
+        if (this.state.error != null) {
+            alert = (
+                <Alert variant="danger">
+                   Ha ocurrido un error creando el pedido: {this.state.error}
+                </Alert>
+            );
+        }
+        if (this.state.orderId !== null) {
+            return <Redirect to={"/order/"+this.state.orderId} />;
+        }
+
+        return (
+            <Container>
+                {alert}
+                <Form onSubmit={e => this.submit(e)}>
+                    <Form.Group as={Row}>
+                        <Form.Label as="legend" column sm={3}>Nombre</Form.Label>
+                        <Col sm={9}>
+                            <Form.Control
+                                placeholder="nombre del pedido"
+                                value={this.state.name}
+                                onChange={e => this.setState({name: e.target.value})}
+                            />
+                        </Col>
+                    </Form.Group>
+                    <Form.Group as={Row}>
+                        <Form.Label as="legend" column sm={3}>Descripción</Form.Label>
+                        <Col sm={9}>
+                            <Form.Control
+                                as='textarea'
+                                value={this.state.description}
+                                onChange={e => this.setState({description: e.target.value})}
+                            />
+                        </Col>
+                    </Form.Group>
+                    <Form.Group as={Row}>
+                        <Form.Label as="legend" column sm={3}>Fecha límite</Form.Label>
+                        <Col sm={9}>
+                            <Form.Control
+                                type='date'
+                                value={date2string(this.state.deadline)}
+                                onChange={e => this.setState({deadline: Date(e.target.value)})}
+                                min={date2string(daysAfterNow(1))}
+                            />
+                        </Col>
+                    </Form.Group>
+                    <ProductPicker
+                        picks={this.state.products}
+                        setPicks={products => this.setState({ products })}
+                    />
+                    <Form.Group as={Row}>
+                        <Col>
+                            <div className="text-right">
+                                <Button type="submit">Abrir pedido</Button>
+                            </div>
+                        </Col>
+                    </Form.Group>
+                </Form>
+            </Container>
+        );
+    }
+}
+
+export default CreateOrder;
diff --git a/src/order/OrderList.js b/src/order/OrderList.js
new file mode 100644
index 0000000000000000000000000000000000000000..b6eaadee5a85d5a9bf2379c68b397bf5b104d425
--- /dev/null
+++ b/src/order/OrderList.js
@@ -0,0 +1,27 @@
+import React, { useState } from 'react';
+import Fetcher from '../Fetcher';
+import { Container, Card, Row, Col, Button } from 'react-bootstrap';
+
+function OrderList() {
+    const [ orders, setOrders ] = useState([]);
+    const order_list = orders.map(o =>
+        <Card as={Col} sm={4}>
+            <Card.Body>
+                <Card.Title>{o.name}</Card.Title>
+                <Card.Text>{o.description}</Card.Text>
+                <Button href={"/order/"+o.ID}>Realizar pedido</Button>
+            </Card.Body>
+        </Card>
+    );
+    return (
+        <Container>
+            <Fetcher url={"/api/order/active"} onFetch={setOrders} >
+                <Row>
+                    {order_list}
+                </Row>
+            </Fetcher>
+        </Container>
+    );
+}
+
+export default OrderList;
diff --git a/src/order/PurchaseOrder.js b/src/order/PurchaseOrder.js
new file mode 100644
index 0000000000000000000000000000000000000000..ea3ef20a5158781332c6fc83303500b46e144a30
--- /dev/null
+++ b/src/order/PurchaseOrder.js
@@ -0,0 +1,129 @@
+import React from 'react';
+import { Alert, Spinner, Form, InputGroup, Button, Col, Row } from 'react-bootstrap';
+import AuthContext from '../AuthContext';
+import { printMoney } from '../util';
+
+class PurchaseOrder extends React.Component {
+    static contextType = AuthContext;
+
+    constructor(props) {
+        super(props);
+        let order = props.order.products;
+        order = order.map(p => {
+            p.amount = 0;
+            p.purchased = 0;
+            return p;
+        });
+        props.order.transactions.forEach(t => {
+            t.order_purchase.forEach(purchase => {
+                const i = order.findIndex(p => p.code === purchase.product_code);
+                if (i) {
+                    order[i].purchased += purchase.amount;
+                }
+            });
+        });
+        this.state = {
+            order: order,
+            total: 0,
+            isLoading: false,
+            noMoney: false,
+            error: null
+        };
+    }
+
+    send(e) {
+        e.preventDefault();
+
+        this.setState({isLoading: true, error: null, noMoney: false});
+        const purchase = this.state.order.map(p => {
+            return {
+                product_code: p.code,
+                amount: parseInt(p.amount)
+            };
+        });
+        const body = JSON.stringify({
+            order: this.props.order.ID,
+            purchase
+        });
+        console.log(body);
+        fetch("/api/order/purchase", {headers: {'x-authentication': this.context.token}, method: "POST", body})
+            .then(response => {
+                if (response.status === 400) {
+                    this.setState({isLoading: false, noMoney: true});
+                } else if (!response.ok) {
+                    this.setState({isLoading: false, error: response.status.toString()+' '+response.statusText})
+                } else {
+                    this.props.onSend(this.state.order, this.state.total);
+                }
+            })
+    }
+
+    setAmount(index, amount) {
+        let order = this.state.order;
+        order[index].amount = amount;
+
+        const add = (acc, p) => acc + (p.price*p.amount);
+        const total = order.reduce(add, 0);
+
+        this.setState({ order, total });
+    }
+
+    render() {
+        if (this.state.isLoading) {
+            return <Spinner animation="border" />;
+        }
+
+        let alert;
+        if (this.state.noMoney) {
+            alert = (
+                <Alert variant="warning">
+                   No tienes suficiente dinero para realizar este pedido.
+                </Alert>
+            );
+        } else if (this.state.error != null) {
+            alert = (
+                <Alert variant="danger">
+                   Ha ocurrido un error enviando el pedido: {this.state.error}
+                </Alert>
+            );
+        }
+
+        const formEntries = this.state.order.map((p, i) =>
+            <Form.Group key={p.code} as={Row}>
+                <Form.Label column className="text-right">
+                    {p.name} ({p.code}):
+                </Form.Label>
+                <Col>
+                    <InputGroup>
+                        <Form.Control
+                            type="number" min="0"
+                            placeholder="cantidad"
+                            value={p.amount}
+                            onChange={e => this.setAmount(i, e.target.value)}
+                        />
+                        <InputGroup.Append>
+                            <InputGroup.Text>{printMoney(p.price)}€</InputGroup.Text>
+                        </InputGroup.Append>
+                    </InputGroup>
+                </Col>
+            </Form.Group>
+        );
+
+        return (
+            <Form onSubmit={e => this.send(e)}>
+                {alert}
+                {formEntries}
+                <Form.Group as={Row}>
+                    <Col className="text-right">
+                        <Button type="submit">Realizar pedido</Button>
+                    </Col>
+                    <Col>
+                        <h3>Total: {printMoney(this.state.total)}€</h3>
+                    </Col>
+                </Form.Group>
+            </Form>
+        );
+    }
+}
+
+export default PurchaseOrder;
diff --git a/src/order/ShowOrder.js b/src/order/ShowOrder.js
new file mode 100644
index 0000000000000000000000000000000000000000..723c3bb83c98b1b259ee02ab226c92b8dcb1794a
--- /dev/null
+++ b/src/order/ShowOrder.js
@@ -0,0 +1,130 @@
+import React from 'react';
+import Fetcher from '../Fetcher';
+import { Container, Row, Col, Badge } from 'react-bootstrap';
+import PurchaseOrder from './PurchaseOrder';
+import { printDate } from '../util';
+import AuthContext from '../AuthContext';
+import { printMoney } from '../util';
+
+function ShowOrderTransaction(props) {
+    const list = props.transaction.order_purchase.map(o =>
+        <li key={o.product.code}>
+            {o.product.name} ({o.product.code}): {o.amount}
+        </li>
+    );
+    return (
+        <div>
+            <h3>Mi pedido</h3>
+            <ul>
+                {list}
+            </ul>
+            <p>Total: {printMoney(-props.transaction.total)} €</p>
+        </div>
+    );
+}
+
+function ShowOrderResults(props) {
+    let products = props.order.products.map(p => {
+        p.amount = 0;
+        return p;
+    });
+    const transactions = props.order.transactions.map(t => {
+        const list = t.order_purchase.map(purchase => {
+            const i = products.findIndex(p => p.code === purchase.product_code);
+            if (!i) {
+                return null;
+            }
+            products[i].amount += purchase.amount;
+            if (purchase.amount) {
+                const key = t.member.num.toString() + "-" + purchase.product_code.toString();
+                return <li key={key}>{products[i].name} {purchase.amount}</li>;
+            }
+            return null;
+        });
+        return (
+            <li key={t.member.num}>
+                {t.member.name} ({t.member.num}):
+                <ul>
+                    {list}
+                </ul>
+            </li>
+        );
+    });
+
+    const product_list = products.map(p => <li key={p.code}>{p.name}: {p.amount}</li>);
+    return (
+        <div>
+            <h3>Productos pedidos</h3>
+            <ul>{product_list}</ul>
+
+            <h3>Pedidos</h3>
+            <ul>{transactions}</ul>
+        </div>
+    );
+}
+
+class ShowOrder extends React.Component {
+    static contextType = AuthContext;
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            order: {
+                products: [],
+                transactions: []
+            },
+            transaction: null
+        };
+    }
+
+    onSend(purchase, total) {
+        const order_purchase = purchase.map(p => {
+            p.product = p;
+            return p;
+        });
+        const transaction = { order_purchase, total: -total };
+        this.setState({ transaction });
+    }
+
+    showTransaction() {
+        if (this.state.transaction) {
+            return <ShowOrderTransaction order={this.state.order} transaction={this.state.transaction} />;
+        }
+        if (this.state.order.active) {
+            return <PurchaseOrder order={this.state.order} onSend={(p, t) => this.onSend(p, t)}/>
+        }
+    }
+
+    setData(data) {
+        this.setState({ order: data.order, transaction: data.transaction });
+    }
+
+    render() {
+        let expired;
+        if (!this.state.order.active) {
+            expired = <Badge variant="info">finalizado</Badge>
+        }
+
+        const { id } = this.props.match.params;
+        return (
+            <Container>
+            <Fetcher url={"/api/order/"+id} onFetch={data => this.setData(data)} >
+                <Row>
+                    <Col>
+                        <h1>{this.state.order.name}</h1>
+                    </Col>
+                    <Col>
+                        <p className="text-right">Fecha limite: {printDate(this.state.order.deadline)}{expired}</p>
+                    </Col>
+                </Row>
+                <p>{this.state.order.description}</p>
+
+                {this.showTransaction()}
+                <ShowOrderResults order={this.state.order} />
+            </Fetcher>
+            </Container>
+        );
+    }
+}
+
+export default ShowOrder;