Create a new record in another doctype

Hi guys,
Modeling small agricultural business activities still. I have a custom app built where I want to create a record for an “egg collection” and on_submit, create a new stock entry with that count passed to it. I’m sure there’s a way to do this but I’m not sure what method to be looking for in the documentation. Since “harvest” is a critical step in any agricultural activity, this is a technique I plan to reuse often.
Thanks,
Tyler

@tmatteson hi, on_submit try:

frappe.get_doc (“Stock Entry”, …)

My test driven mindset aim is to find an ‘rough’ example test case for what you require.

This query has candidates in stock entry, production, sales, journal entry and so on:

frappe@erpnext:~/frappe-bench$ find . -name .py | grep test_ | xargs grep submit

For eg this one has serial submits among multiple documents

./apps/erpnext/erpnext/manufacturing/doctype/production_order/test_production_order.py

Hope this helps

Thank you @JoEz and @clarkej. Do you happen to know if you have to pass all the mandatory fields to the new document? It seems like the Stock Entry DocType and Stock Entry Detail DocType are a lot more detailed than I need.

Well yes, but presumably make_stock_entry here ./apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry_utils.py

will handle all those mandatory fields you prefer to ignore…the tests will point way here.

Thank you @clarkej, that IS very helpful!

@tmatteson
you can use
frappe.new_doc() for record entry.

Event_doc=frappe.new_doc(“Event”)
Event_doc.subject=“Meeting”
Event_doc.event_type=“Private”
Event_doc.flags.ignore_mandatory = True
Event_doc.save()

Thanks, Sukrut
New Indictrans Technologies pvt.ltd

1 Like

Thanks @jsukrut!

On a related note, how do you test these integrations in a sandboxed app? I’ve followed the example given by the Frappe Team for the library_management tutorial, which doesn’t integrate with any other apps or modules. It’s a good example, but the interconnectivity is why you’d use a program like ERPNext in the first place. Do you start a new app on a site with ERPNext already installed? Is it that simple? What am I missing?

Sorry, solved:
bench install-app

So I ran into an issue while trying to use @jsukrut method last night: the stock_entry doctype has a table (child doctype? is that the right term/ idea?) stock_entry_detail that needs to be passed some values for a valid entry as well. Notably, this is where the item quantity is stored. What is the right approach for this? Create two documents with a link from parent to child? stock_entry.stock_entry_detail didn’t work, maybe that’s too clever.

Thank you all for your help. I’m worried that the stock module is way more than I need, or at least requires things that I do not wish it to require. The business use case for a manufacturer’s inventory management is very different than a farmer’s, even field-to-kitchen-to-store sort of farmer.

Making some progress here but while trying to populate the stock_entry.purpose(“Material Receipt”) (a select), it tells me I can’t pass it a unicode type. What type is it looking for? How do I select from the the list in my function? I tried passing it an integer as well with no luck.

Thanks!
-T

Sample code to add rows in child table Stock Entry Detail:

se = frappe.new_doc('Stock Entry')
se.purpose = "Material Receipt"
se.append("items", {
  "item_code": "your item code",
  "qty": 5
})
se.save()
1 Like

Thanks @nabinhait, that seems to be working in theory but I can’t get it to run error-free and I’m not sure what I’m doing wrong.

I’m still getting an AttributeError: ‘NoneType’ object has no attribute ‘options’. I’ve tried debugging this a couple of different ways, but it comes up every time. It seems to come up before the .save() regardless of what I comment out in the code, which makes me think it’s a test or something.

self.meta.get_field(fieldname).options - what is this piece of code looking for?

Thank you for your patience everyone, it’s been a long time since I’ve tackled a project like this and I feel like I’ve forgotten everything I once knew.

def on_update(self): #frappe.throw("Eggs collected: %d" % (self.eggs_collected)) pass_to_stock=frappe.new_doc('Stock Entry') pass_to_stock.purpose = "Material Receipt" pass_to_stock.append("Items", { "item_code": "Eggs - Dozen", "qty": self.eggs_collected, "uom": "Nos", "stock_uom": "Dozen", "conversion_factor": 1, "transfer_qty": self.eggs_collected, }) pass_to_stock.save()

Here’s the error log:

Traceback (most recent call last):
File “/home/tyler/frappe-bench/apps/frappe/frappe/desk/form/save.py”, line 22, in savedocs
doc.save()
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 224, in save
return self._save(*args, **kwargs)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 247, in _save
self.insert()
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 213, in insert
self.run_post_save_methods()
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 777, in run_post_save_methods
self.run_method(“on_update”)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 656, in run_method
out = Document.hook(fn)(self, *args, **kwargs)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 877, in composer
return composed(self, method, *args, **kwargs)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 860, in runner
add_to_return_value(self, fn(self, *args, **kwargs))
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 650, in
fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs)
File “/home/tyler/frappe-bench/apps/livestock/livestock/livestock/doctype/egg_collection/egg_collection.py”, line 25, in on_update
“transfer_qty”: self.eggs_collected,
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/base_document.py”, line 136, in append
value = self._init_child(value, key)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/base_document.py”, line 160, in _init_child
value[“doctype”] = self.get_table_field_doctype(key)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/base_document.py”, line 271, in get_table_field_doctype
return self.meta.get_field(fieldname).options
AttributeError: ‘NoneType’ object has no attribute ‘options’

Traceback (most recent call last):
File “/home/tyler/frappe-bench/apps/frappe/frappe/app.py”, line 56, in application
response = frappe.handler.handle()
File “/home/tyler/frappe-bench/apps/frappe/frappe/handler.py”, line 19, in handle
execute_cmd(cmd)
File “/home/tyler/frappe-bench/apps/frappe/frappe/handler.py”, line 42, in execute_cmd
ret = frappe.call(method, **frappe.form_dict)
File “/home/tyler/frappe-bench/apps/frappe/frappe/init.py”, line 903, in call
return fn(*args, **newargs)
File “/home/tyler/frappe-bench/apps/frappe/frappe/desk/form/save.py”, line 22, in savedocs
doc.save()
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 224, in save
return self._save(*args, **kwargs)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 247, in _save
self.insert()
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 213, in insert
self.run_post_save_methods()
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 777, in run_post_save_methods
self.run_method(“on_update”)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 656, in run_method
out = Document.hook(fn)(self, *args, **kwargs)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 877, in composer
return composed(self, method, *args, **kwargs)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 860, in runner
add_to_return_value(self, fn(self, *args, **kwargs))
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/document.py”, line 650, in
fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs)
File “/home/tyler/frappe-bench/apps/livestock/livestock/livestock/doctype/egg_collection/egg_collection.py”, line 25, in on_update
“transfer_qty”: self.eggs_collected,
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/base_document.py”, line 136, in append
value = self._init_child(value, key)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/base_document.py”, line 160, in _init_child
value[“doctype”] = self.get_table_field_doctype(key)
File “/home/tyler/frappe-bench/apps/frappe/frappe/model/base_document.py”, line 271, in get_table_field_doctype
return self.meta.get_field(fieldname).options
AttributeError: ‘NoneType’ object has no attribute ‘options’

Try “items” instead of “Items”, it can be a potential fix.

Hi @nabinhait. Unfortunately, that did not work. The result was that it hangs the webpage, and does not save either document (eggs or stock entry).

There is nothing wrong in the above code. What is the error message you getting now? I think this time it is another error which is creating problem.

I have tried the following code in my local, and its working. Have you entered company, rate and warehouse in your code?

se = frappe.new_doc('Stock Entry')
se.purpose = "Material Receipt"
se.append("items", {
  "item_code": "FG1",
  "qty": 5,
  "uom": "Nos",
  "conversion_factor": 1,
  "t_warehouse": "Stores - TC",
  "basic_rate": 10
})
se.company = "Test Company"
se.save()
2 Likes

Hi Nabin,
I was able to get it working, though I think it could improved upon (a lot) with some more API calls, so that it could be more reusable code. I’m going to try to keep improving it. The conversion factor is definitely not working right, but everything is populating appropriately.

def on_update(self): pass_to_stock=frappe.new_doc('Stock Entry') pass_to_stock.purpose = "Material Receipt" pass_to_stock.append("items", { "item_code": "Eggs - Dozen", "qty": self.eggs_collected, "uom": "Nos", "stock_uom": "Dozen", "conversion_factor": "1", "transfer_qty": (self.eggs_collected) }) pass_to_stock.company = frappe.db.get_value("Global Defaults", None, "default_company") pass_to_stock.to_warehouse = "Finished Goods - " + frappe.db.get_value("Company", pass_to_stock.company, "abbr") pass_to_stock.date = self.date_collected pass_to_stock.time = self.time_collected pass_to_stock.save()
2 Likes