With a huge vote of gratitude to @max_morais_dmm, here’s what I have been able to put together.
Hopefully this will be helpful to others in the future.
Before
Beginning with the state of the system before trying to create a Script Report, here is the initial content of the directory hierarchy of my new app “returnables”:
├── LICENSE
├── license.txt
├── MANIFEST.in
├── __pycache__
├── README.md
├── requirements.txt
├── returnable
│ ├── config
│ ├── fixtures
│ ├── hooks.py
│ ├── __init__.py
│ ├── modules.txt
│ ├── patches.txt
│ ├── __pycache__
│ ├── returnable
│ │ ├── doctype
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ ├── returnable
│ │ ├── __init__.py
│ │ └── __pycache__
│ └── templates
├── returnable.egg-info
└── setup.py
Here’s the database query to get the Script Report:
MariaDB [_100fd7dc74a5feb0]> select * from `tabReport` where name = 'Minimal Script Report' \G
Empty set (0.000 sec)
I prepare a new report form like this:
After saving it and adding the minimal code shown in the first post it looks like this:
Re-examining my app directory hierarchy I find that it is unchanged from before. This would be because is_standard
is not yet selected (as @max_morais_dmm pointed out).
Meanwhile, the database query now returns:
MariaDB [_100fd7dc74a5feb0]> select * from `tabReport` where name = 'Minimal Script Report' \G
*************************** 1. row ***************************
name: Minimal Script Report
creation: 2020-09-28 19:32:39.672696
modified: 2020-09-28 19:37:32.407572
modified_by: Administrator
owner: Administrator
docstatus: 0
parent: NULL
parentfield: NULL
parenttype: NULL
idx: 0
report_name: Minimal Script Report
ref_doctype: Returnable
reference_report: NULL
is_standard: No
module: Returnable
report_type: Script Report
letter_head: NULL
add_total_row: 1
disabled: 0
disable_prepared_report: 0
prepared_report: 0
query: NULL
javascript: frappe.query_reports["Minimal Script Report"] = {
"filters": [
{
fieldname: "someField",
label: "Some Field",
fieldtype: "Select",
options: "30\n45\n60",
default: 30
},
]
};
report_script: def get_columns():
return [
{
"fieldname": "someField",
"fieldtype": "Int",
"label": "Some Field",
"width": 100
},
]
def get_data(fltr):
values = [
{ "someField": 30 },
{ "someField": 45 },
{ "someField": 60 },
{ "someField": 75 },
{ "someField": 90 },
{ "someField": 105 }
]
return [ v for v in values if v["someField"] > int(fltr.someField) ]
def execute(filters=None):
return get_columns(), get_data(filters)
json: NULL
_user_tags: NULL
_comments: NULL
_assign: NULL
_liked_by: NULL
1 row in set (0.000 sec)
Clearly, this is not meaningfully different from the result I showed in my original post.
Also, sadly, adding filters
as a parameter to the execute function (def execute(filters=None):
) made only a little difference; running the report produces:
The next thing I tried was to set is_standard
to true, and save again. I discovered that:
- The advantage of doing so is to 1) have the required files generated on disk 2) have a report that actually displays data!
- The disadvantage of doing it is that the report metadata can no longer be altered. Only the newly created files can be altered.
With that done the app directory now contains:
: : : : :
│ ├── patches.txt
│ ├── __pycache__
│ ├── returnable
│ │ ├── doctype
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ ├── returnable
│ │ ├── __init__.py
│ │ └── __pycache__
│ │ └── report
│ │ ├── __init__.py
│ │ └── minimal_script_report
│ │ ├── __init__.py
│ │ ├── minimal_script_report.js
│ │ ├── minimal_script_report.json
│ │ └── minimal_script_report.py
│ └── templates
: : : : :
The previous database query result is unchanged except for …
: : : : :
is_standard: Yes
: : : : :
… which is reflected in the contents of the json
file :
erpdev@erpserver:~/frappe-bench-DYPW/apps$ jq -r '.' returnable/returnable/returnable/report/minimal_script_report/minimal_script_report.json
{
"add_total_row": 1,
"creation": "2020-09-28 10:01:52.986921",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"javascript": "frappe.query_reports[\"Minimal Script Report\"] = {\n \"filters\": [\n {\n fieldname: \"someField\",\n label: \"Some Field\",\n fieldtype: \"Select\",\n options: [30, 60, 90],\n default: 30\n },\n ]\n};\n",
"modified": "2020-09-28 15:20:52.097912",
"modified_by": "Administrator",
"module": "Returnable",
"name": "Minimal Script Report",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Returnable",
"report_name": "Minimal Script Report",
"report_script": "def get_columns():\n return [\n {\n \"fieldname\": \"someField\",\n \"fieldtype\": \"Int\",\n \"label\": \"Some Field\",\n \"width\": 100\n },\n ]\n\n\ndef get_data():\n return [ { \"someField\": 30 }, { \"someField\": 60 } ]\n\n\ndef execute(filters=None):\n return get_columns(), get_data()\n",
"report_type": "Script Report",
"roles": [
{
"role": "System Manager"
},
{
"role": "Stock User"
},
{
"role": "Stock Manager"
},
{
"role": "Purchase User"
},
{
"role": "Sales User"
},
{
"role": "Sales Manager"
},
{
"role": "Sales Master Manager"
},
{
"role": "Purchase Manager"
},
{
"role": "Purchase Master Manager"
}
]
}
erpdev@erpserver:~/frappe-bench-DYPW/apps$
A critical fact is the lack of code in the JavaScript and the Python file!
You only get …
frappe.query_reports["Minimal Script Report"] = {
"filters": [
]
};
… and …
from __future__ import unicode_literals
# import frappe
def execute(filters=None):
columns, data = [], []
return columns, data
Getting it working
I replaced the above placeholder code snippets with the intended code …
minimal_script_report.js
frappe.query_reports["Minimal Script Report"] = {
"filters": [
{
fieldname: "someField",
label: "Some Field",
fieldtype: "Select",
options: "30\n45\n60",
default: 30
},
]
};
minimal_script_report.py
def get_columns():
return [
{
"fieldname": "someField",
"fieldtype": "Int",
"label": "Some Field",
"width": 100
},
]
def get_data(fltr):
values = [
{ "someField": 30 },
{ "someField": 45 },
{ "someField": 60 },
{ "someField": 75 },
{ "someField": 90 },
{ "someField": 105 }
]
return [ v for v in values if v["someField"] > int(fltr.someField) ]
def execute(filters=None):
return get_columns(), get_data(filters)
… which results in:
Curious fact
I deleted the javascript
and reportscript
field values both 1) in the database record and 2) in the file returnable/returnable/returnable/report/minimal_script_report/minimal_script_report.json
.
They seem to serve no purpose at all. The file contents are everything.