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)
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()
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
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
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