fix(sales-order): show Item Tax Template in the item grid
The column was on Sales Invoice and not on Sales Order, which reads like a Frappe default but isn't: ERPNext ships item_tax_template with in_list_view = 0 on both child doctypes. The asymmetry came from show_item_tax_template_in_sales_invoice.py, which set the property setters for the invoice and — as the name says — only the invoice. Generalised to both, and renamed, since it is no longer sales-invoice specific. The template is what decides the VAT split and which tax-article group the picker offers, so hiding it from the line the accountant works on is backwards. The property setter alone is not enough. A user with a grid layout already saved in __UserSettings gets that list rendered verbatim — Frappe does not merge a newly-in_list_view field into it — so every pre-existing Sales Order layout would stay without the column. Hence the patch, same shape as the ones that added the tax article and certificate cells. Verified headless: the column now renders in the items grid on both forms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
df63b036e3
commit
d6af639bd0
|
|
@ -0,0 +1,47 @@
|
||||||
|
"""Surface `item_tax_template` in the item grid of the selling documents.
|
||||||
|
|
||||||
|
ERPNext ships the field hidden and out of the grid (in_list_view = 0) on both
|
||||||
|
Sales Invoice Item and Sales Order Item. Here it drives everything downstream —
|
||||||
|
the VAT split and which tax-article group the picker offers — so the accountant
|
||||||
|
has to see it on the line, not dig into the row's detail view.
|
||||||
|
|
||||||
|
This used to cover Sales Invoice only, which is why the column showed up on the
|
||||||
|
invoice and not on the order.
|
||||||
|
|
||||||
|
A Property Setter is only half the story: a user who already has a saved grid
|
||||||
|
layout gets that layout rendered verbatim, new in_list_view field or not. The
|
||||||
|
patches (add_item_tax_template_to_so_grid_views) inject the column into the
|
||||||
|
layouts that predate this.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||||
|
|
||||||
|
DOCTYPES = ("Sales Invoice Item", "Sales Order Item")
|
||||||
|
|
||||||
|
PROPERTIES = {
|
||||||
|
"hidden": (0, "Check"),
|
||||||
|
"in_list_view": (1, "Check"),
|
||||||
|
"columns": (2, "Int"),
|
||||||
|
"insert_after": ("item_name", "Data"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def show_item_tax_template():
|
||||||
|
for doctype in DOCTYPES:
|
||||||
|
for prop, (value, property_type) in PROPERTIES.items():
|
||||||
|
try:
|
||||||
|
make_property_setter(
|
||||||
|
doctype=doctype,
|
||||||
|
fieldname="item_tax_template",
|
||||||
|
property=prop,
|
||||||
|
value=value,
|
||||||
|
property_type=property_type,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
frappe.log_error(
|
||||||
|
f"{doctype}.item_tax_template.{prop}: {e}",
|
||||||
|
"show_item_tax_template",
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.clear_cache()
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
import frappe
|
|
||||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
|
||||||
|
|
||||||
def show_item_tax_template_in_sales_invoice():
|
|
||||||
"""
|
|
||||||
Создает Property Setters для показа item_tax_template в Sales Invoice Item
|
|
||||||
"""
|
|
||||||
|
|
||||||
property_setters = [
|
|
||||||
{
|
|
||||||
'doctype': 'Sales Invoice Item',
|
|
||||||
'fieldname': 'item_tax_template',
|
|
||||||
'property': 'hidden',
|
|
||||||
'value': 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'doctype': 'Sales Invoice Item',
|
|
||||||
'fieldname': 'item_tax_template',
|
|
||||||
'property': 'in_list_view',
|
|
||||||
'value': 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'doctype': 'Sales Invoice Item',
|
|
||||||
'fieldname': 'item_tax_template',
|
|
||||||
'property': 'columns',
|
|
||||||
'value': 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'doctype': 'Sales Invoice Item',
|
|
||||||
'fieldname': 'item_tax_template',
|
|
||||||
'property': 'insert_after',
|
|
||||||
'value': 'item_name'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
for ps in property_setters:
|
|
||||||
try:
|
|
||||||
make_property_setter(
|
|
||||||
doctype=ps['doctype'],
|
|
||||||
fieldname=ps['fieldname'],
|
|
||||||
property=ps['property'],
|
|
||||||
value=ps['value'],
|
|
||||||
property_type=get_property_type(ps['property'])
|
|
||||||
)
|
|
||||||
print(f"✓ Set {ps['fieldname']}.{ps['property']} = {ps['value']}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Error: {e}")
|
|
||||||
|
|
||||||
frappe.clear_cache()
|
|
||||||
|
|
||||||
def get_property_type(property_name):
|
|
||||||
"""Возвращает тип свойства"""
|
|
||||||
types = {
|
|
||||||
'hidden': 'Check',
|
|
||||||
'in_list_view': 'Check',
|
|
||||||
'columns': 'Int',
|
|
||||||
'insert_after': 'Data'
|
|
||||||
}
|
|
||||||
return types.get(property_name, 'Data')
|
|
||||||
|
|
@ -168,8 +168,8 @@ def after_migrate_combined():
|
||||||
|
|
||||||
from jey_erp.custom_fields import create_custom_fields
|
from jey_erp.custom_fields import create_custom_fields
|
||||||
create_custom_fields()
|
create_custom_fields()
|
||||||
from jey_erp.custom.show_item_tax_template_in_sales_invoice import show_item_tax_template_in_sales_invoice
|
from jey_erp.custom.show_item_tax_template import show_item_tax_template
|
||||||
show_item_tax_template_in_sales_invoice()
|
show_item_tax_template()
|
||||||
from jey_erp.custom.vat_calculations import patch_sales_documents
|
from jey_erp.custom.vat_calculations import patch_sales_documents
|
||||||
patch_sales_documents()
|
patch_sales_documents()
|
||||||
from jey_erp.custom.rename_default_groups import rename_default_groups
|
from jey_erp.custom.rename_default_groups import rename_default_groups
|
||||||
|
|
|
||||||
|
|
@ -18,3 +18,4 @@ jey_erp.patches.backfill_sales_invoice_tax_articles_history
|
||||||
jey_erp.patches.add_tax_articles_to_si_grid_views
|
jey_erp.patches.add_tax_articles_to_si_grid_views
|
||||||
jey_erp.patches.add_certificates_to_si_grid_views
|
jey_erp.patches.add_certificates_to_si_grid_views
|
||||||
jey_erp.patches.add_tax_articles_to_so_grid_views
|
jey_erp.patches.add_tax_articles_to_so_grid_views
|
||||||
|
jey_erp.patches.add_item_tax_template_to_so_grid_views
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
"""Inject the "Item Tax Template" column into saved Sales Order Item grid layouts.
|
||||||
|
|
||||||
|
show_item_tax_template() flips in_list_view on for the field, but a user with a grid
|
||||||
|
layout saved in `__UserSettings` gets that list rendered verbatim — Frappe does not
|
||||||
|
merge newly-in_list_view fields into it. Sales Invoice had the property setter (and
|
||||||
|
the column) for a while; Sales Order never did, so every existing Sales Order layout
|
||||||
|
is missing it.
|
||||||
|
|
||||||
|
Insert it just before the Tax Articles cell — you pick the template, then the articles
|
||||||
|
it allows. Idempotent.
|
||||||
|
"""
|
||||||
|
rows = frappe.db.sql(
|
||||||
|
"select user, data from `__UserSettings` where doctype = 'Sales Order'",
|
||||||
|
as_dict=True,
|
||||||
|
)
|
||||||
|
for r in rows:
|
||||||
|
try:
|
||||||
|
data = json.loads(r.data or "{}")
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
grid_view = data.get("GridView") or {}
|
||||||
|
cols = grid_view.get("Sales Order Item")
|
||||||
|
if not cols:
|
||||||
|
continue
|
||||||
|
|
||||||
|
names = [c.get("fieldname") for c in cols]
|
||||||
|
if "item_tax_template" in names:
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_col = {"fieldname": "item_tax_template", "columns": 2}
|
||||||
|
if "custom_tax_articles_display" in names:
|
||||||
|
cols.insert(names.index("custom_tax_articles_display"), new_col)
|
||||||
|
else:
|
||||||
|
cols.append(new_col)
|
||||||
|
|
||||||
|
grid_view["Sales Order Item"] = cols
|
||||||
|
data["GridView"] = grid_view
|
||||||
|
frappe.db.sql(
|
||||||
|
"update `__UserSettings` set data = %s where user = %s and doctype = 'Sales Order'",
|
||||||
|
(json.dumps(data), r.user),
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.db.commit()
|
||||||
|
frappe.cache().delete_value("_user_settings")
|
||||||
Loading…
Reference in New Issue