I'm trying to use a modified version of the django-oscar import_oscar_catalogue class to import a bunch of products from a CSV, and on the first encounter of a product (defined by title), create a canonical parent product, and then for all future encounters create a child product under that parent product.
This seems to work, but the canonical product does not reflect the combined stock levels of the child product, nor display the correct attributes for that product. It does correctly list them as variations within the django dashboard though.
How can I programmatically create this child/parent relationship in products, with the correct stock records?
Relevant code:
def _create_item(self, upc, title, product_class, other_product_attributes):
product_class, __ \
= ProductClass.objects.get_or_create(name=product_class)
try:
parent = Product.objects.get(title=title)
item = Product()
item.parent = parent
except Product.DoesNotExist:
# Here is where I think it might need to be changed
# Maybe pitem = ParentProduct() or something?
pitem = Product()
pitem.upc = upc
pitem.title = title
pitem.other_product_attributes = other_product_attributes
# Here parent item is saved to db
pitem.save()
# Create item because no parent was found
item = Product()
parent = Product.objects.get(title=title)
#Set parent
item.parent = parent
# Customize child attributes
item.product_class = product_class
item.title = title
item.other_product_attributes = other_product_attributes
# Save the child item
item.save()
def _create_stockrecord(self, item, partner_name, partner_sku, price_excl_tax,
num_in_stock, stats):
# Create partner and stock record
partner, _ = Partner.objects.get_or_create(
name=partner_name)
try:
stock = StockRecord.objects.get(partner_sku=partner_sku)
except StockRecord.DoesNotExist:
stock = StockRecord()
stock.num_in_stock = 0
# General attributes
stock.product = item
stock.partner = partner
# SKU will be unique for every object
stock.partner_sku = partner_sku
stock.price_excl_tax = D(price_excl_tax)
stock.num_in_stock += int(num_in_stock)
# Save the object to database
stock.save()
The create_stockrecord() creates a record of 1 stock for each unique item variation, but these variation's stockrecords don't translate to the parent item.
Thanks for any suggestions.
EDIT: I've updated the class with a method that explicitly calls ProductClass.objects.track_stock() against the ProductClass instance, and I'm calling it after looping through all rows of the CSV file (passing it the name of the one product class I use currently). However, when looking at the stock in dashboard, none of the child/variations stock is being counted against the parent.
def track_stock(self, class_name):
self.logger.info("ProductClass name: %s" % class_name)
product_class = ProductClass.objects.get_or_create(name=class_name)
self.logger.info("ProductClass: %s" % str(product_class))
self.logger.info("TrackStock: %s" % str(product_class[0].track_stock))
product_class[0].track_stock = True
self.logger.info("TrackStock: %s" % str(product_class[0].track_stock))
product_class[0].save()
INFO Starting catalogue import
INFO - Importing records from 'sample_inventory.csv'
INFO - Flushing product data before import
INFO Parent items: 6, child items: 10
INFO ProductClass name: ClassName
INFO ProductClass: (<ProductClass: ClassName>, False)
INFO TrackStock: True
INFO TrackStock: True
I've checked the admin page, only 1 ProductClass is created, and it has the same name as is being passed to track_stock(). Is there something else that needs to be done to enable this feature? track_stock() documentation is kind of sparse. In the output, track_stock looks like it is true in both instances. Does it have to be False while the child_objects are created, and then flipped to True?
EDIT: SOLUTION:
After some research from the test factory, I solved the issue by specifying
product.stucture = 'parent'
On the parent object, and
product.structure = 'child'
on the child object. I also needed to change the custom attributes of my objects to a dict product_attributes
, and then set each value on the object:
if product_attributes:
for code, value in product_attributes.items():
product_class.attributes.get_or_create(name=code, code=code)
setattr(product.attr, code, value)
It was not necessary to create a stock record for each parent object, as they track the stock records of the child objects to which they are associated. It was also not necessary to set track_stock = True
, as it is set to True
by default when creating a Product()
To be able to correctly reflect the stock levels for any Product you need to have a Partner that will supply the Product and then you need to have StockRecord that links the Partner and the Products together.
First make sure that you have all that information in the database for each one of your Product variations.
Then you need to update your ProductClass and set the "track_stock" attribute as True since its None by default.
You also need to remove the ProductClass from your child products since they inherit the ProductClass from their Parent Product.
EDIT 1:
To add attributes to a Product you have to add a ProductAttribute for the ProductClass and then you can set the attributes directly on the Product like this example.
EDIT 2:
You also need to set the "net_stock_level" on the StockRecord.
To get a more in depth look into how Oscar gets the stock levels look into Selector. This class determines which pricing, tax and stock level strategies to use which you might need to customize in the future if you want to charge tax or offer different pricing based on the user.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With