Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Obteniendo órdenes del perfil

13/20
Recursos

Aportes 7

Preguntas 1

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Reto
En order.router.js
En el body se enviaría el sub del payload como body.

// POST
router.post('/',
  passport.authenticate('jwt', { session: false }),
  async (req, res, next) => {
    try {
      const body = {
        userId: req.user.sub
      }
      const newOrder = await service.create(body);
      res.status(201).json(newOrder);
    } catch (err) {
      next(err);
    }
  }
);

Luego en order.service.js Modificamos el create() para que la inserción sea automatizada con solo enviar el sub o userId, hacemos una busqueda findOne al customer Where user .id sea igual a data.userId. Donde este se almacenará en customer el cual tendremos que extraer el ID para enviarlo al create de order. Si no se encuentra se regresa un no encontrado.

async create(data) {
    const customer = await models.Customer.findOne({
      where: {
        '$user.id$': data.userId
      },
      include: ['user']
    })
    if (!customer) {
      throw boom.badRequest('Customer not found');
    }
    const newOrder = await models.Order.create({ customerId: customer.id });
    return newOrder;
  }

Para resolver el reto de crear la order sin neesidad de enviar el customerId en el body de la petición hariamos lo siguente:

En order.router.js

router.post(
  '/',
  passport.authenticate('jwt', { session: false }),
  //validatorHandler(createOrderSchema, 'body'),
  async (req, res, next) => {
    try {
      const body = {
        userId: req.user.sub
      };
      const order = await service.create(body);
      res.status(201).json(order);
    } catch (error) {
      next(error);
    }
  }
);

Hay que eliminar el validator porque ahora no es necesario pasar en el body el customer id, pero se agrega el jwt validator

Luego en order.service.js

  async create(data) {
    // Accedemos al modelo Customer y usando where encadenamos hacia user
    const customer = await models.Customer.findAll({
      where: {
        '$user.id$': data.userId
      },
      include: ['user']
    });
    // Validamos que exista el customer
    if (!customer) {
      throw boom.notFound('Customer not found');
    }
    // Creamos un objeto con el customerId obtenido de la consulta
    const dataOrder = {
      customerId: customer[0].id
    };
    const newOrder = await models.Order.create(dataOrder);
    return newOrder;
  }

TRas un poco de depuración con este metodo vi que los resultados de la consulta llegan en un Array, si se canbia el FinAll por findOne en ese caso solo retorna un unico objeto y no haria falta usar el customer[0] sino directamente customer

Hola a todos.
Estaba recibiendo este error:

Y esto se remonta al curso pasado de “Node con Postgres” donde yo continue con mi progreso.
Si realizaba hacer un post de una orden o consultar todas las ordenes, me salia el mismo error.
Esto se corrige poniendo una condicional en el modelo de ordenes en el campo virtual total. Ponemos una condicional que diga si exista “items” entonces que proceda con su respectiva validacion de “this.items.length”

total: {
    type: DataTypes.VIRTUAL,
    get() {
      //Reviso si tenemos productos
      if(this.items) {
        console.log('existen items en la orden')
        if(this.items.length > 0) {
          return this.items.reduce((total, item) => {
            return total + (item.price * item.OrderProduct.amount)
          }, 0)
        }
        return 0
      }
    }
  }

Para poder ver las órdenes de compra de un usuario podemos usar el token que tiene, obtener el sub y obtener la información directamente sin necesidad de enviar el ID del usuario.

Se crea un nuevo método en el servicio orders, el método findByUser recibe el userId y realiza una consulta respecto a éste donde se incluye la asociación de user y customer, y se desea obtener el id del customer ya que únicamente se cuenta con el id de usuario, es por ello que se utiliza where: {'$customer.user.id$': userId}, es decir, le estamos diciendo a qué asociaciones estamos haciendo la consulta.

Más información sobre este tipo de consultas https://sequelize.org/master/manual/eager-loading.html#complex-where-clauses-at-the-top-level

async findByUser(userId) {
    const orders = await models.Order.findAll({
      where: {
        '$customer.user.id$': userId,
      },
      include: [
        {
          association: 'customer',
          include: ['user'],
        },
      ],
    });
    return orders;
  }

Se crea un nuevo router profile.router.js, aquí se obtiene el id del usuario que está en este momento con sesión (const orders = await service.findByUser(user.sub)), el id está en el sub del payload. Es decir, ya no es necesario enviar el user id porque ya viene en el token y se va a obtener de ahí.

const express = require('express');
const passport = require('passport');

const OrderService = require('../../services/order.service');

const router = express.Router();
const service = new OrderService();

router.get(
  '/my-orders',
  passport.authenticate('jwt', { session: false }),
  async (req, res, next) => {
    try {
      const user = req.user;
      const orders = await service.findByUser(user.sub);
      res.json(orders);
    } catch (error) {
      next(error);
    }
  }
);

module.exports = router;

Agregamos la nueva ruta a routes/index.js:

const express = require('express');

const productsRouter = require('./api/products.route');
const usersRouter = require('./api/users.router');
const categoriesRouter = require('./api/categories.route');
const orderRouter = require('./api/orders.router');
const customerRouter = require('./api/customer.route');
const authRouter = require('./api/auth.route');
const profileRouter = require('./api/profile.router');

function routerApi(app) {
  const router = express.Router();
  app.use('/api', router);
  router.use('/products', productsRouter);
  router.use('/users', usersRouter);
  router.use('/categories', categoriesRouter);
  router.use('/orders', orderRouter);
  router.use('/customers', customerRouter);
  router.use('/auth', authRouter);
  router.use('/profile', profileRouter);
}

module.exports = routerApi;

En mi caso enlace directamente la orden al usuario, pero yo creo que según la idea de negocio no debería permitir crear ordenes si ya tiene una activa, en mi modelo puse un campo int para validar 0 creada, 1 cancelada, 3 pagada, por lo que en el servicio primero consulto si cuenta con una orden creada para permitir crear mas ordenes…!

  // Crea una Order y la retorna
  async create(user) {
    const body = {userId: user};
    if (await this.getOrderUserActive(user) !== 0) {
      throw boom.conflict('This user have a order pending..!')
    }
    console.log(body)
    const newOrder = await models.Order.create(body)
    return await this.getOne(newOrder.id);
  }

  // Verifica que el usuario no tenga ordenes activas
  async getOrderUserActive(user){
    const order = await models.Order.findAndCountAll({
      where: {
        user_id: user,
        state: 0
      }
    })
    return order.count
  }

Al momento de tomar el usuario del sub del JWT hay que recordar en el schema colocar que el user id es opcional o simplemente no usar el validador…!

RETO

order.router.js

router.post(
  '/',
  passport.authenticate('jwt', { session: false }),
  async (req, res, next) => {
    try {
      const body = { userId: req.user.sub };
      const newOrder = await service.create(body);
      res.status(201).json(newOrder);
    } catch (error) {
      next(error);
    }
  }
);

order.services.js

async create(data) {
    const customer = await models.Customer.findOne({
      where: {
        '$user.id$': data.userId,
      },
      include: ['user'],
    });

    data.customerId = customer.id;
    delete data.userId;

    const newOrder = await models.Order.create(data);
    return newOrder;
  }

Para el reto, creamos una nueva ruta, post:

router.post("/my-orders",
    passport.authenticate('jwt', { session: false }),
    async(req, res, next) => {
        const data = { id: req.user.sub }

        try {
            res.status(201).json(await service.createFromProfile(data));
        } catch (error) {
            next(error);
        }
    }
);

Para que esto funcione, necesitamos que el servicio tenga una nueva función:

   async createFromProfile(data) {
        const user = await service.findOne(data.id);
        const customerId = user.customer.id;
        return models.Order.create({ customerId });
    }


Y listo, con eso debe estar funcionando (: