Exemple #1
0
async def create_repository(request):
    if request.method == "POST":
        form = RepositoryForm(await request.post(), meta=await generate_csrf_meta(request))
        if form.validate():
            data = remove_special_data(form.data)
            data = days_to_array(data)
            async with request.app["db-pool"].acquire() as conn:
                q = "INSERT INTO repository ({}) VALUES ({})".format(
                    field_list(data), place_holders(data)
                )
                try:
                    await conn.execute(q, *data.values())
                except IntegrityConstraintViolationError:
                    flash(request, ("warning", _("Le point de livraison ne peut pas être créé")))
                    return {"form": form}
            flash(request, ("success", _("Le point de livraison a été créé")))
            return HTTPFound(request.app.router["list_repository"].url_for())
        else:
            flash(request, ("danger", _("Le formulaire contient des erreurs.")))
            return {"form": form}
    elif request.method == "GET":
        form = RepositoryForm(meta=await generate_csrf_meta(request))
        return {"form": form}
    else:
        raise HTTPMethodNotAllowed()
Exemple #2
0
    def __call__(self):
        method = self.request.method.lower()

        if method in self.methods:
            return (yield from getattr(self, method)())
        else:
            raise HTTPMethodNotAllowed(method, self.methods)
async def handler(request):
    if request.method == "POST":
        form = EmailForm(await request.post(),
                         meta=await generate_csrf_meta(request))
        if form.validate():
            data = dict(form.data.items())
            email_address = data["email_address"]
            async with request.app["db-pool"].acquire() as conn:
                q = "SELECT id, email_address, login FROM client WHERE email_address = $1"
                client = await conn.fetchrow(q, email_address)
            if client is None:
                flash(request,
                      ("danger",
                       _("Il n'y a pas de profil dont l'adresse email est {}").
                       format(email_address)))
            else:
                await send_text_message(
                    request,
                    client["email_address"],
                    _("Rappel de Votre identifiant"),
                    _("Votre identifiant est {}").format(client["login"]),
                )
                flash(request,
                      ("info", _("Un email de confirmation a été envoyé à {}").
                       format(email_address)))
                return HTTPFound(request.app.router["login"].url_for())
        else:
            flash(request,
                  ("danger", _("Le formulaire contient des erreurs.")))
        return {"form": form}
    elif request.method == "GET":
        form = EmailForm(meta=await generate_csrf_meta(request))
        return {"form": form}
    else:
        raise HTTPMethodNotAllowed()
Exemple #4
0
async def login(request):
    if request.method == "POST":
        form = LoginForm(await request.post(),
                         meta=await generate_csrf_meta(request))
        if form.validate():
            response = HTTPFound(request.app.router["home"].url_for())
            login = form.login.data
            password = form.password.data
            db_pool = request.app["db-pool"]
            if await check_credentials(db_pool, login, password):
                async with request.app["db-pool"].acquire() as conn:
                    q = (
                        "UPDATE client SET last_seen = NOW() WHERE login = $1 "
                        "RETURNING first_name, super_user")
                    client = await conn.fetchrow(q, login)
                await remember(request, response, login)
                if not client["super_user"]:
                    flash(request, (
                        "info",
                        _("Bonjour {} ! Ravi de vous revoir à nouveau").format(
                            client["first_name"])))
                return response
        flash(request,
              ("danger",
               _("La combinaison identifiant/mot de passe est invalide")))
        return {"form": form}
    elif request.method == "GET":
        form = LoginForm(meta=await generate_csrf_meta(request))
        return {"form": form}
    else:
        raise HTTPMethodNotAllowed()
Exemple #5
0
async def edit_repository(request):
    async with request.app["db-pool"].acquire() as conn:
        id_ = int(request.match_info["id"])
        data = dict(await conn.fetchrow("SELECT * FROM repository WHERE id = $1", id_))
        data = array_to_days(data)
        if request.method == "POST":
            form = RepositoryForm(
                await request.post(),
                data=data,
                meta=await generate_csrf_meta(request)
            )
            if form.validate():
                data = remove_special_data(form.data)
                data = days_to_array(data)
                q = "UPDATE repository SET {} WHERE id = ${:d}".format(
                    settings(data), len(data) + 1
                )
                try:
                    await conn.execute(q, *data.values(), id_)
                except IntegrityConstraintViolationError:
                    flash(request, ("warning", _("Le point de livraison ne peut pas être modifié")))
                else:
                    flash(request, ("success", _("Le point de livraison a été modifié")))
                    return HTTPFound(request.app.router["list_repository"].url_for())
            else:
                flash(request, ("danger", _("Le formulaire contient des erreurs.")))
            return {"id": str(id_), "form": form}
        elif request.method == "GET":
            form = RepositoryForm(data=data, meta=await generate_csrf_meta(request))
            return {"id": str(id_), "form": form}
        else:
            raise HTTPMethodNotAllowed()
Exemple #6
0
    async def __call__(self):
        method = self.request.method.lower()

        if method in self.methods:
            return await getattr(self, method)()
        else:
            raise HTTPMethodNotAllowed(method, self.methods)
Exemple #7
0
    def start(self, request):
        if request.method != 'GET':
            raise HTTPMethodNotAllowed(request.method, ['GET'])

        resp_impl = super().start(request)
        self._prepare_sse(request.app.loop)
        return resp_impl
Exemple #8
0
    async def dispatch(self, request, view=None, **kwargs):
        """Dispatch request."""
        if view is None and request.method not in self.methods:
            raise HTTPMethodNotAllowed(request.method, self.methods)

        method = getattr(self, view or request.method.lower())
        response = await method(request, **kwargs)
        return await self.make_response(request, response)
Exemple #9
0
    def dispatch(self, request, view=None, **kwargs):
        """ Dispatch request. """
        if request.method not in self.methods:
            raise HTTPMethodNotAllowed(request.method, self.methods)

        method = getattr(self, view or request.method.lower())
        response = yield from method(request, **kwargs)

        return (yield from self.make_response(request, response))
Exemple #10
0
    def __init__(self, request: Request) -> None:
        super().__init__(request)
        self.methods = {}
        self._get_class_methods()

        method_name = self.request.method.upper()
        if not method_name:
            raise HTTPMethodNotAllowed(method_name)

        setattr(self, method_name.lower(),
                self._serializer_wrapper(self.methods[method_name]))
Exemple #11
0
async def list_client(request):
    if request.method == "GET":
        async with request.app["db-pool"].acquire() as conn:
            q = (
                "SELECT id, first_name, last_name, login, confirmed, disabled "
                "FROM client "
                "WHERE NOT super_user "
                "ORDER BY last_name, first_name")
            clients = await conn.fetch(q)
        return {"clients": clients}
    else:
        raise HTTPMethodNotAllowed()
async def mailing(request):
    async with request.app["db-pool"].acquire() as conn:
        rows = await conn.fetch("SELECT id, name FROM repository WHERE opened")
        repository_choices = [(row["id"], row["name"]) for row in rows]

        if request.method == "POST":
            form = MailingForm(await request.post(), meta=await generate_csrf_meta(request))
            form.repository_id.choices = repository_choices
            if form.validate():
                data = remove_special_data(form.data)
                subject = data["subject"]
                message = data["message"]
                if data["all_repositories"]:
                    q = (
                        "SELECT first_name, email_address, login FROM client "
                        "WHERE confirmed AND mailing"
                        )
                    rows = await conn.fetch(q)
                else:
                    repository_id = data.get("repository_id")
                    q = (
                        "SELECT first_name, email_address, login FROM client "
                        "WHERE confirmed AND mailing AND repository_id = $1"
                    )
                    rows = await conn.fetch(q, repository_id)
                if not rows:
                    flash(request, ("warning", _("Il n'y a pas de destinataire.")))
                    return HTTPFound(request.app.router["mailing"].url_for())

                if "<first_name>" in message or "<login>" in message:
                    for r in rows:
                        message_ = message.replace("<first_name>", r["first_name"])
                        message_ = message_.replace("<login>", r["login"])
                        await send_text_message(request, r["email_address"], subject, message_)
                else:
                    email_addresses = [r["email_address"] for r in rows]
                    await send_mailing_message(request, email_addresses, subject, message)
                flash(request, ("info", _("Les messages ont été envoyés.")))
                return HTTPFound(request.app.router["mailing"].url_for())
            else:
                flash(request, ("danger", _("Le formulaire contient des erreurs.")))
                return HTTPFound(request.app.router["mailing"].url_for())

        elif request.method == "GET":
            form = MailingForm(meta=await generate_csrf_meta(request))
            form.repository_id.choices = repository_choices
            return {"form": form}

        else:
            raise HTTPMethodNotAllowed()
Exemple #13
0
    def prepare(self, request):
        """Prepare for streaming and send HTTP headers.

        :param request: regular aiohttp.web.Request.
        """
        if request.method != 'GET':
            raise HTTPMethodNotAllowed(request.method, ['GET'])

        resp_impl = self._start_pre_check(request)
        if resp_impl is not None:
            return resp_impl
        resp_impl = yield from super().prepare(request)
        self._prepare_sse(request.app.loop)
        return resp_impl
async def edit_profile(request):
    async with request.app["db-pool"].acquire() as conn:
        rows = await conn.fetch(
            "SELECT id, name, latitude, longitude FROM repository WHERE opened"
        )
        repository_choices = [(row["id"], row["name"]) for row in rows]

        login = await authorized_userid(request)
        data = dict(await
                    conn.fetchrow("SELECT * FROM client WHERE login = $1",
                                  login))
        del data["password_hash"]
        if request.method == "POST":
            form = ProfileForm(await request.post(),
                               data=data,
                               meta=await generate_csrf_meta(request))
            form.repository_id.choices = repository_choices
            if form.validate():
                data = remove_special_data(form.data)
                del data["password2"]
                password = data.pop("password")
                if password:
                    if len(password) < 6:
                        flash(request,
                              ("warning", _("Le mot de passe est trop court")))
                        return {"form": form}
                    data["password_hash"] = sha256_crypt.hash(password)
                q = "UPDATE client SET {} WHERE login = ${}".format(
                    settings(data),
                    len(data) + 1)
                try:
                    await conn.execute(q, *data.values(), login)
                except UniqueViolationError:
                    flash(request,
                          ("warning", _("Votre profil ne peut être modifié")))
                else:
                    flash(request,
                          ("success", _("Votre profil a été modifié")))
                    return HTTPFound(request.app.router["home"].url_for())
            else:
                flash(request,
                      ("danger", _("Le formulaire contient des erreurs.")))
            return {"form": form, "repositories": rows}
        elif request.method == "GET":
            form = ProfileForm(data=data,
                               meta=await generate_csrf_meta(request))
            form.repository_id.choices = repository_choices
            return {"form": form, "repositories": rows}
        else:
            raise HTTPMethodNotAllowed()
async def confirm(request):
    token = request.match_info["token"]
    try:
        token_data = get_token_data(
            token, request.app["config"]["application"]["secret_key"]
        )
        id_ = token_data["id"]
    except Exception:
        flash(request, ("danger", _("Le lien est invalide ou a expiré")))
        raise HTTPBadRequest()

    if request.method == "POST":
        form = PasswordForm(await request.post(), meta=await generate_csrf_meta(request))
        if form.validate():
            password_hash = sha256_crypt.hash(form.password.data)

            async with request.app["db-pool"].acquire() as conn:
                q = "UPDATE client SET password_hash = $1 WHERE id = $2"
                try:
                    await conn.execute(q, password_hash, id_)
                except Exception:
                    flash(
                        request,
                        (
                            "danger",
                            _("Votre mot de passe ne peut être modifié")
                        )
                    )
                    return {"form": form, "token": token}
                else:
                    flash(
                        request,
                        (
                            "info",
                            _(
                                "Votre mot de passe a été modifié, "
                                "vous pouvez vous connecter"
                            )
                        )
                    )
                    return HTTPFound(request.app.router["login"].url_for())
        else:
            flash(request, ("danger", _("Le formulaire contient des erreurs.")))
        return {"form": form, "token": token}
    elif request.method == "GET":
        form = PasswordForm(meta=await generate_csrf_meta(request))
        return {"form": form, "token": token}
    else:
        raise HTTPMethodNotAllowed()
async def handler(request):
    async with request.app["db-pool"].acquire() as conn:
        rows = await conn.fetch(
            "SELECT id, name, latitude, longitude FROM repository WHERE opened"
        )
        repository_choices = [(row["id"], row["name"]) for row in rows]

        if request.method == "POST":
            form = RegisterForm(await request.post(),
                                meta=await generate_csrf_meta(request))
            form.repository_id.choices = repository_choices
            if form.validate():
                data = remove_special_data(form.data)
                del data["password2"]
                data["password_hash"] = sha256_crypt.hash(data.pop("password"))
                try:
                    async with conn.transaction():
                        q = "INSERT INTO client ({}) VALUES ({}) RETURNING *".format(
                            field_list(data), place_holders(data))
                        try:
                            client = await conn.fetchrow(q, *data.values())
                        except UniqueViolationError:
                            flash(request,
                                  ("warning",
                                   _("Votre profil ne peut être créé, cet "
                                     "identifiant est déjà utilisé")))
                            raise  # rollback the transaction : client not created
                        await send_confirmation(
                            request, client["email_address"],
                            {"id": client["id"]}, "confirm_register",
                            _("Confirmation de votre enregistrement"),
                            "register-confirmation")
                        flash(request,
                              ("info",
                               _("Un email de confirmation a été envoyé à {}").
                               format(client["email_address"])))
                        return HTTPFound(request.app.router["login"].url_for())
                except Exception:
                    return HTTPFound(request.app.router["register"].url_for())
            else:
                flash(request,
                      ("danger", _("Le formulaire contient des erreurs.")))
            return {"form": form, "repositories": rows}
        elif request.method == "GET":
            form = RegisterForm(meta=await generate_csrf_meta(request))
            form.repository_id.choices = repository_choices
            return {"form": form, "repositories": rows}
        else:
            raise HTTPMethodNotAllowed()
Exemple #17
0
    async def prepare(self, request):
        """Prepare for streaming and send HTTP headers.

        :param request: regular aiohttp.web.Request.
        """
        if request.method != 'GET':
            raise HTTPMethodNotAllowed(request.method, ['GET'])

        if not self.prepared:
            writer = await super().prepare(request)
            self._loop = request.app.loop
            self._ping_task = self._loop.create_task(self._ping())
            # explicitly enabling chunked encoding, since content length
            # usually not known beforehand.
            self.enable_chunked_encoding()
            return writer
Exemple #18
0
    async def dispatch(self, request: Request):
        method = self.methods.get(request.method.upper())
        if not method:
            raise HTTPMethodNotAllowed('', DEFAULT_METHODS)

        wanted_args = list(signature(method).parameters.keys())
        available_args = request.match_info.copy()
        available_args.update({'request': request})

        unsatisfied_args = set(wanted_args) - set(available_args.keys())
        if unsatisfied_args:
            # Expected match info that doesn't exist
            raise HTTPBadRequest

        return await method(
            **{arg_name: available_args[arg_name]
               for arg_name in wanted_args})
async def handler(request):
    login = await authorized_userid(request)
    async with request.app["db-pool"].acquire() as conn:
        q = "SELECT id, email_address FROM client WHERE login = $1"
        client = await conn.fetchrow(q, login)

        if request.method == "POST":
            form = EmailForm(await request.post(),
                             meta=await generate_csrf_meta(request))

            if form.validate():
                data = dict(form.data.items())
                email_address = data["email_address"]

                q = "SELECT COUNT(*) FROM client WHERE email_address = $1"
                if await conn.fetchval(q, email_address) != 0:
                    flash(request,
                          ("danger",
                           _("Veuillez choisir une autre adresse email")))
                    return {"form": form, "email": client["email_address"]}

                await send_confirmation(request, email_address, {
                    "id": client["id"],
                    "email_address": email_address
                }, "confirm_email", _("Changement d'adresse email"),
                                        "email-confirmation")
                flash(request,
                      ("info", _("Un email de confirmation a été envoyé à {}").
                       format(email_address)))
                return HTTPFound(request.app.router["home"].url_for())
            else:
                flash(request,
                      ("danger", _("Le formulaire contient des erreurs.")))
            return {"form": form, "email": client["email_address"]}
        elif request.method == "GET":
            form = EmailForm(meta=await generate_csrf_meta(request))
            return {"form": form, "email": client["email_address"]}
        else:
            raise HTTPMethodNotAllowed()
Exemple #20
0
    def start(self, request):
        """Prepare for streaming and send HTTP headers.

        :param request: regular aiohttp.web.Request.
        """
        if request.method != 'GET':
            raise HTTPMethodNotAllowed(request.method, ['GET'])

        self._loop = request.app.loop
        self._finish_fut = asyncio.Future(loop=self._loop)
        self._finish_fut.add_done_callback(self._cancel_ping)

        resp_impl = self._start_pre_check(request)
        if resp_impl is not None:
            return resp_impl
        self._req = request

        self._keep_alive = True
        resp_impl = self._resp_impl = ResponseImpl(request._writer,
                                                   self._status,
                                                   request.version,
                                                   close=not self._keep_alive,
                                                   reason=self._reason)

        self._copy_cookies()

        # explicitly enabling chunked encoding, since content length
        # usually not known beforehand.
        self._chunked = True
        resp_impl.enable_chunked_encoding()

        headers = self.headers.items()
        for key, val in headers:
            resp_impl.add_header(key, val)

        resp_impl.send_headers()
        self._ping_task = asyncio.Task(self._ping(), loop=self._loop)
        return resp_impl
Exemple #21
0
    async def prepare(self, request):
        """Prepare for streaming and send HTTP headers.

        :param request: regular aiohttp.web.Request.
        """
        if request.method != 'GET':
            raise HTTPMethodNotAllowed(request.method, ['GET'])

        if not self.prepared:
            writer = await super().prepare(request)
            self._loop = request.app.loop
            self._ping_task = self._loop.create_task(self._ping())
            # explicitly enabling chunked encoding, since content length
            # usually not known beforehand.
            self.enable_chunked_encoding()
            return writer
        else:
            # hackish way to check if connection alive
            # should be updated once we have proper API in aiohttp
            # https://github.com/aio-libs/aiohttp/issues/3105
            if request.protocol.transport is None:
                # request disconnected
                raise asyncio.CancelledError()
Exemple #22
0
async def edit_batch(request):
    async with request.app["db-pool"].acquire() as conn:
        id_ = int(request.match_info["id"])
        data = dict(await conn.fetchrow("SELECT * FROM batch WHERE id = $1",
                                        id_))

        if request.method == "POST":
            form = BatchForm(await request.post(),
                             data=data,
                             meta=await generate_csrf_meta(request))
            if form.validate():
                data = remove_special_data(form.data)
                # as the date only is chosen by the user, the time part is set to 6:00 am
                data["date"] = datetime.combine(data["date"], time(hour=6))

                q = "UPDATE batch SET {} WHERE id = ${:d}".format(
                    settings(data),
                    len(data) + 1)
                try:
                    await conn.execute(q, *data.values(), id_)
                except IntegrityConstraintViolationError:
                    flash(
                        request,
                        ("warning", _("La fournée ne peut pas être modifiée")))
                else:
                    flash(request, ("success", _("La fournée a été modifiée")))
                    return HTTPFound(
                        request.app.router["list_batch"].url_for())
            else:
                flash(request,
                      ("danger", _("Le formulaire contient des erreurs.")))
            return {"id": str(id_), "form": form}
        elif request.method == "GET":
            form = BatchForm(data=data, meta=await generate_csrf_meta(request))
            return {"id": str(id_), "form": form}
        else:
            raise HTTPMethodNotAllowed()
Exemple #23
0
async def create_batch(request):
    async with request.app["db-pool"].acquire() as conn:
        if request.method == "POST":
            form = BatchForm(await request.post(),
                             meta=await generate_csrf_meta(request))
            data = remove_special_data(form.data)

            # just for csrf !
            if not form.validate():
                flash(request,
                      ("danger", _("Le formulaire contient des erreurs.")))
                return {"form": form}

            # as the date only is chosen by the user, the time part is set to 6:00 am
            data["date"] = datetime.combine(data["date"], time(hour=6))

            try:
                async with conn.transaction():
                    # create the batch
                    q = (
                        "INSERT INTO batch (date, capacity, opened) VALUES ($1, $2, $3)"
                    )
                    await conn.execute(q, data["date"], data["capacity"],
                                       data["opened"])

                flash(request, ("success", _("La fournée a été créée")))
            except Exception:
                flash(request,
                      ("warning", _("La fournée ne peut pas être créée")))
                return {"form": form}
            return HTTPFound(request.app.router["list_batch"].url_for())
        elif request.method == "GET":
            form = BatchForm(meta=await generate_csrf_meta(request))
            return {"form": form}
        else:
            raise HTTPMethodNotAllowed()
async def edit_order(request):
    order_id = int(request.match_info["id"])
    login = await authorized_userid(request)

    async with request.app["db-pool"].acquire() as conn:
        q = ("SELECT c.id, c.disabled, r.days "
             "FROM client AS c "
             "INNER JOIN repository AS r ON c.repository_id = r.id "
             "WHERE c.login = $1 ")
        client = await conn.fetchrow(q, login)
        client_id = client["id"]

        if client["disabled"]:
            flash(
                request,
                ("warning", _("Vous ne pouvez pas modifier votre commande.")))
            return HTTPFound(request.app.router["list_order"].url_for())

        # check that the order belongs to the right client
        q = ("SELECT COUNT(*) FROM order_ " "WHERE id = $1 AND client_id = $2")
        count = await conn.fetchval(q, order_id, client_id)
        if count != 1:
            return HTTPFound(request.app.router["list_order"].url_for())

        # get batch id and batch date
        q = ("SELECT batch_id, b.date, b.capacity FROM order_ AS o "
             "INNER JOIN batch AS b ON b.id = batch_id "
             "WHERE o.id = $1")
        row = await conn.fetchrow(q, order_id)
        batch_date = row["date"]
        batch_id = row["batch_id"]
        batch_capacity = row["capacity"]

        # check that's its not too late to modify the order
        if datetime.now() > batch_date - timedelta(hours=12):
            flash(request,
                  ("warning",
                   _("Il est trop tard pour modifier votre commande.")))
            return HTTPFound(request.app.router["list_order"].url_for())

        # get all available products
        q = ("SELECT * FROM product WHERE available")
        rows = await conn.fetch(q)
        products = products_for_context(rows, get_current_locale())

        template_context = {
            "batch_date": batch_date,
            "batch_id": batch_id,
            "order_id": order_id,
            "products": products.values()
        }

        if request.method == "POST":
            data = await request.post()
            form = FillOrderForm(await request.post(),
                                 meta=await generate_csrf_meta(request))
            data = remove_special_data(dict(data))

            template_context["form"] = form

            # just for csrf !
            if not form.validate():
                flash(request,
                      ("danger", _("Le formulaire contient des erreurs.")))
                return template_context

            # compute total price and total_load of the order
            total_price = 0
            total_load = 0
            for product_id, product in products.items():
                ordered = data["product_qty_{}".format(product_id)].strip()
                if ordered == '':
                    ordered = 0
                else:
                    try:
                        ordered = int(ordered)
                        if ordered < 0:
                            raise ValueError("negative quantity")
                    except ValueError:
                        flash(request,
                              ("danger", _("Quantité(s) invalide(s).")))
                        return template_context
                product["ordered"] = ordered
                total_price += ordered * products[product_id]["price"]
                total_load += ordered * products[product_id]["load"]

            # check that at least one product has been ordered
            if total_load == 0:
                flash(request,
                      ("warning", _("Veuillez choisir au moins un produit")))
                return template_context

            # checked that the load of ordered products is less than batch capacity
            products_load = await get_ordered_products_load(conn,
                                                            batch_id,
                                                            excluded=order_id)
            if total_load + products_load > batch_capacity:
                flash(request,
                      ("warning",
                       _("Votre commande dépasse la capacité de la fournée.")))
                return template_context

            # delete and re-create the order in a transaction
            try:
                async with conn.transaction():
                    # delete order to products association records
                    q = "DELETE FROM order_product_association WHERE order_id = $1"
                    await conn.execute(q, order_id)

                    # and re-create them
                    # create order to products
                    for product_id, product in products.items():
                        ordered = product["ordered"]
                        if ordered != 0:
                            q = ("INSERT INTO order_product_association ("
                                 "   quantity, order_id, product_id"
                                 ") "
                                 "VALUES ($1, $2, $3)")
                            await conn.execute(q, ordered, order_id,
                                               product_id)

                    # update order total
                    q = ("UPDATE order_ SET total = $1, date=NOW() "
                         "WHERE id = $2")
                    await conn.fetchval(q, total_price, order_id)

            except Exception:
                flash(
                    request,
                    ("warning", _("Votre commande n'a pas pu être modifiée.")))
                return template_context

            flash(request, ("success", _("Votre commande a été modifiée.")))
            return HTTPFound(request.app.router["list_order"].url_for())

        elif request.method == "GET":
            # select all the products from the order
            q = ("SELECT p.id, quantity "
                 "FROM order_product_association AS opa "
                 "INNER JOIN product AS p ON opa.product_id = p.id "
                 "WHERE p.available AND opa.order_id = $1")
            rows = await conn.fetch(q, order_id)

            # update the batch products with the order products
            for row in rows:
                products[row["id"]]["ordered"] = row["quantity"]

            form = FillOrderForm(meta=await generate_csrf_meta(request))
            template_context["form"] = form
            return template_context

        else:
            raise HTTPMethodNotAllowed()
async def create_order(request):
    login = await authorized_userid(request)

    async with request.app["db-pool"].acquire() as conn:
        q = ("SELECT c.id, c.disabled, r.days "
             "FROM client AS c "
             "INNER JOIN repository AS r ON c.repository_id = r.id "
             "WHERE c.login = $1 ")
        client = await conn.fetchrow(q, login)
        client_id = client["id"]

        if client["disabled"]:
            flash(request,
                  ("warning", _("Vous ne pouvez pas passer de commande.")))
            return HTTPFound(request.app.router["list_order"].url_for())

        # select opened batches that have no order from the client
        # select opened batches that have no order on them
        # from all above selected batches, select batches :
        #    - whose date is 12 hours in the future
        #    - client's delivery days corresponds to the batch date
        q = (
            "WITH batch_choices AS ( "
            "    SELECT b.id AS batch_id, b.date AS batch_date_, c.id AS client_id FROM batch AS b "
            "    LEFT JOIN order_ AS o ON b.id = o.batch_id "
            "    LEFT JOIN client AS c ON c.id = o.client_id "
            "    WHERE b.opened AND b.date > (NOW() + INTERVAL '12 hour') AND "
            "          (string_to_array($2, ',')::BOOLEAN[])[EXTRACT(DOW FROM b.date) + 1] "
            "    GROUP BY b.id, b.date, c.id "
            "    ORDER BY b.id, b.date"
            ") "
            "SELECT DISTINCT batch_id, TO_CHAR(batch_date_::DATE, 'dd-mm-yyyy') AS batch_date "
            "FROM batch_choices "
            "WHERE batch_id NOT IN ("
            "    SELECT batch_id FROM batch_choices "
            "    WHERE client_id = $1"
            ")")
        rows = await conn.fetch(q, client_id, str(client["days"]).strip("[]"))
        batch_choices = [(row["batch_id"], row["batch_date"]) for row in rows]

        if not batch_choices:
            flash(request,
                  ("warning", _("Il n'y a pas de fournée disponible.")))
            return HTTPFound(request.app.router["list_order"].url_for())

        # get all available products
        q = ("SELECT * FROM product WHERE available")
        rows = await conn.fetch(q)
        products = products_for_context(rows, get_current_locale())

        template_context = {"products": products.values()}

        if request.method == "POST":
            data = await request.post()
            form = CreateOrderForm(data,
                                   meta=await generate_csrf_meta(request))
            form.batch_id.choices = batch_choices

            data = remove_special_data(dict(data))
            batch_id = int(data["batch_id"])

            # just for csrf !
            if not form.validate():
                flash(request,
                      ("danger", _("Le formulaire contient des erreurs.")))
                return HTTPFound(request.app.router["list_order"].url_for())

            # get the batch date and capacity
            q = "SELECT date, capacity FROM batch WHERE id = $1"
            row = await conn.fetchrow(q, batch_id)
            batch_date = row["date"]
            batch_capacity = row["capacity"]

            # check that the batch corresponds to the delivery days
            if not client["days"][(batch_date.weekday() + 1) % 7]:
                flash(request,
                      ("warning",
                       _("La fournée choisie ne permet de vous livrer.")))
                return HTTPFound(request.app.router["list_order"].url_for())

            template_context["form"] = form

            # compute total price and total_load of the order
            total_price = 0
            total_load = 0
            for product_id, product in products.items():
                ordered = data["product_qty_{}".format(product_id)].strip()
                if ordered == '':
                    ordered = 0
                else:
                    try:
                        ordered = int(ordered)
                        if ordered < 0:
                            raise ValueError("negative quantity")
                    except ValueError:
                        flash(request,
                              ("danger", _("Quantité(s) invalide(s).")))
                        return template_context
                product["ordered"] = ordered
                total_price += ordered * products[product_id]["price"]
                total_load += ordered * products[product_id]["load"]

            # check that at least one product has been ordered
            if total_load == 0:
                flash(request,
                      ("warning", _("Veuillez choisir au moins un produit")))
                return template_context

            # checked that the load of ordered products is less than the batch capacity
            products_load = await get_ordered_products_load(conn, batch_id)
            if total_load + products_load > batch_capacity:
                flash(request,
                      ("warning",
                       _("Votre commande dépasse la capacité de la fournée.")))
                return template_context

            try:
                async with conn.transaction():
                    # create the order
                    q = ("INSERT INTO order_ (total, client_id, batch_id) "
                         "VALUES ($1, $2, $3) RETURNING id")
                    order_id = await conn.fetchval(q, total_price, client_id,
                                                   batch_id)

                    # create order to products
                    for product_id, product in products.items():
                        ordered = product["ordered"]
                        if ordered != 0:
                            q = ("INSERT INTO order_product_association ("
                                 "   quantity, order_id, product_id"
                                 ") "
                                 "VALUES ($1, $2, $3)")
                            await conn.execute(q, ordered, order_id,
                                               product_id)
            except Exception:
                flash(request,
                      ("warning", _("Votre commande n'a pas pu être passée.")))
                return template_context

            flash(request,
                  ("success", _("Votre commande a été passée avec succès")))
            return HTTPFound(request.app.router["list_order"].url_for())

        elif request.method == "GET":
            form = CreateOrderForm(meta=await generate_csrf_meta(request))
            form.batch_id.choices = batch_choices
            template_context["form"] = form
            return template_context

        else:
            raise HTTPMethodNotAllowed()
Exemple #26
0
 def _raise_allowed_methods(self) -> None:
     allowed_methods = {
         m
         for m in hdrs.METH_ALL if hasattr(self, m.lower())
     }
     raise HTTPMethodNotAllowed(self.request.method, allowed_methods)