Exemple #1
0
    def create_shipment_of_all_products(self, supplier=None):
        """
        Create a shipment of all the products in this Order, no matter whether or not any have been previously
        marked as shipped or not.

        See the documentation for `create_shipment`.

        :param supplier: The Supplier to use. If `None`, the first supplier in
                         the order is used. (If several are in the order, this fails.)
        :return: Saved, complete Shipment object
        :rtype: shuup.shop.models.Shipment
        """
        suppliers_to_product_quantities = defaultdict(lambda: defaultdict(lambda: 0))
        lines = (
            self.lines
            .filter(type=OrderLineType.PRODUCT)
            .values_list("supplier_id", "product_id", "quantity"))
        for supplier_id, product_id, quantity in lines:
            if product_id:
                suppliers_to_product_quantities[supplier_id][product_id] += quantity

        if not suppliers_to_product_quantities:
            raise NoProductsToShipException("Could not find any products to ship.")

        if supplier is None:
            if len(suppliers_to_product_quantities) > 1:  # pragma: no cover
                raise ValueError("Can only use create_shipment_of_all_products when there is only one supplier")
            supplier_id, quantities = suppliers_to_product_quantities.popitem()
            supplier = Supplier.objects.get(pk=supplier_id)
        else:
            quantities = suppliers_to_product_quantities[supplier.id]

        products = dict((product.pk, product) for product in Product.objects.filter(pk__in=quantities.keys()))
        quantities = dict((products[product_id], quantity) for (product_id, quantity) in quantities.items())
        return self.create_shipment(quantities, supplier=supplier)
Exemple #2
0
    def ship_products(self, shipment, product_quantities, *args, **kwargs):
        # stocks are managed, do stocks check
        if self.supplier.stock_managed:
            insufficient_stocks = {}

            for product, quantity in product_quantities.items():
                if quantity > 0 and product.kind in self.get_supported_product_kinds_values(
                ):
                    stock_status = self.get_stock_status(product.pk)
                    if stock_status.stock_managed and stock_status.physical_count < quantity:
                        insufficient_stocks[
                            product] = stock_status.physical_count

            if insufficient_stocks:
                formatted_counts = [
                    _("%(name)s (physical stock: %(quantity)s)") % {
                        "name": force_text(name),
                        "quantity": force_text(int(quantity))
                    } for (name, quantity) in insufficient_stocks.items()
                ]
                raise NoProductsToShipException(
                    _("Insufficient physical stock count for the following products: `%(product_counts)s`."
                      ) % {"product_counts": ", ".join(formatted_counts)})

        for product, quantity in product_quantities.items():
            if quantity == 0 or product.kind not in self.get_supported_product_kinds_values(
            ):
                continue

            sp = shipment.products.create(product=product, quantity=quantity)
            sp.cache_values()
            sp.save()

        shipment.cache_values()
        shipment.save()
Exemple #3
0
    def create_shipment(self,
                        product_quantities,
                        supplier=None,
                        shipment=None):
        """
        Create a shipment for this order from `product_quantities`.
        `product_quantities` is expected to be a dict, which maps Product instances to quantities.

        Only quantities over 0 are taken into account, and if the mapping is empty or has no quantity value
        over 0, `NoProductsToShipException` will be raised.

        Orders without a shipping address defined, will raise `NoShippingAddressException`.

        :param product_quantities: a dict mapping Product instances to quantities to ship.
        :type product_quantities: dict[shuup.shop.models.Product, decimal.Decimal]
        :param supplier: Optional Supplier for this product. No validation is made.
        :param shipment: Optional unsaved Shipment for ShipmentProduct's. If not given
                         Shipment is created based on supplier parameter.
        :raises: NoProductsToShipException, NoShippingAddressException
        :return: Saved, complete Shipment object.
        :rtype: shuup.core.models.Shipment
        """
        if not product_quantities or not any(
                quantity > 0 for quantity in product_quantities.values()):
            raise NoProductsToShipException(
                "Error! No products to ship (`quantities` is empty or has no quantity over 0)."
            )

        if self.shipping_address is None:
            raise NoShippingAddressException(
                "Error! Shipping address is not defined for this order.")

        assert supplier or shipment
        if shipment:
            assert shipment.order == self
        else:
            from ._shipments import Shipment

            shipment = Shipment(order=self, supplier=supplier)
        shipment.save()

        if not supplier:
            supplier = shipment.supplier

        supplier.module.ship_products(shipment, product_quantities)

        self.add_log_entry(
            _("Success! Shipment #%d was created.") % shipment.id)
        self.update_shipping_status()
        shipment_created.send(sender=type(self), order=self, shipment=shipment)
        shipment_created_and_processed.send(sender=type(self),
                                            order=self,
                                            shipment=shipment)
        return shipment
Exemple #4
0
    def create_shipment(self,
                        product_quantities,
                        supplier=None,
                        shipment=None):
        """
        Create a shipment for this order from `product_quantities`.
        `product_quantities` is expected to be a dict mapping Product instances to quantities.

        Only quantities over 0 are taken into account, and if the mapping is empty or has no quantity value
        over 0, `NoProductsToShipException` will be raised.

        :param product_quantities: a dict mapping Product instances to quantities to ship
        :type product_quantities: dict[shuup.shop.models.Product, decimal.Decimal]
        :param supplier: Optional Supplier for this product. No validation is made
                         as to whether the given supplier supplies the products.
        :param shipment: Optional unsaved Shipment for ShipmentProduct's. If not given
                         Shipment is created based on supplier parameter.
        :raises: NoProductsToShipException
        :return: Saved, complete Shipment object
        :rtype: shuup.core.models.Shipment
        """
        if not product_quantities or not any(
                quantity > 0 for quantity in product_quantities.values()):
            raise NoProductsToShipException(
                "No products to ship (`quantities` is empty or has no quantity over 0)."
            )

        assert (supplier or shipment)
        if shipment:
            assert shipment.order == self

        from ._shipments import ShipmentProduct
        if not shipment:
            from ._shipments import Shipment
            shipment = Shipment(order=self, supplier=supplier)
        shipment.save()

        for product, quantity in product_quantities.items():
            if quantity > 0:
                sp = ShipmentProduct(shipment=shipment,
                                     product=product,
                                     quantity=quantity)
                sp.cache_values()
                sp.save()

        shipment.cache_values()
        shipment.save()

        self.add_log_entry(_(u"Shipment #%d created.") % shipment.id)
        self.update_shipping_status()
        shipment_created.send(sender=type(self), order=self, shipment=shipment)
        return shipment
Exemple #5
0
    def create_shipment(self,
                        product_quantities,
                        supplier=None,
                        shipment=None):
        """
        Create a shipment for this order from `product_quantities`.
        `product_quantities` is expected to be a dict mapping Product instances to quantities.

        Only quantities over 0 are taken into account, and if the mapping is empty or has no quantity value
        over 0, `NoProductsToShipException` will be raised.

        Orders without a shipping address defined, will raise `NoShippingAddressException`.

        :param product_quantities: a dict mapping Product instances to quantities to ship
        :type product_quantities: dict[shuup.shop.models.Product, decimal.Decimal]
        :param supplier: Optional Supplier for this product. No validation is made
        :param shipment: Optional unsaved Shipment for ShipmentProduct's. If not given
                         Shipment is created based on supplier parameter.
        :raises: NoProductsToShipException, NoShippingAddressException
        :return: Saved, complete Shipment object
        :rtype: shuup.core.models.Shipment
        """
        if not product_quantities or not any(
                quantity > 0 for quantity in product_quantities.values()):
            raise NoProductsToShipException(
                "No products to ship (`quantities` is empty or has no quantity over 0)."
            )

        if self.shipping_address is None:
            raise NoShippingAddressException(
                "Shipping address is not set on this order")

        assert (supplier or shipment)
        from ._shipments import ShipmentProduct
        if shipment:
            assert shipment.order == self
        else:
            from ._shipments import Shipment
            shipment = Shipment(order=self, supplier=supplier)
        shipment.save()

        if not supplier:
            supplier = shipment.supplier

        insufficient_stocks = {}
        for product, quantity in product_quantities.items():
            if quantity > 0:
                stock_status = supplier.get_stock_status(product.pk)
                if (product.stock_behavior == StockBehavior.STOCKED) and (
                        stock_status.physical_count < quantity):
                    insufficient_stocks[product] = stock_status.physical_count
                sp = ShipmentProduct(shipment=shipment,
                                     product=product,
                                     quantity=quantity)
                sp.cache_values()
                sp.save()

        if insufficient_stocks:
            formatted_counts = [
                _("%(name)s (physical stock: %(quantity)s)") % {
                    "name": force_text(name),
                    "quantity": force_text(quantity)
                } for (name, quantity) in insufficient_stocks.items()
            ]
            raise Problem(
                _("Insufficient physical stock count for following products: %(product_counts)s"
                  ) % {"product_counts": ", ".join(formatted_counts)})

        shipment.cache_values()
        shipment.save()

        self.add_log_entry(_(u"Shipment #%d created.") % shipment.id)
        self.update_shipping_status()
        shipment_created.send(sender=type(self), order=self, shipment=shipment)
        return shipment