Merge pull request #34160 from frappe/version-13-hotfix

chore: release v13
This commit is contained in:
Deepesh Garg
2023-02-21 22:49:22 +05:30
committed by GitHub
21 changed files with 270 additions and 122 deletions

View File

@@ -4,7 +4,7 @@
# the repo. Unless a later match takes precedence, # the repo. Unless a later match takes precedence,
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/assets/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar erpnext/assets/ @anandbaburajan @deepeshgarg007
erpnext/erpnext_integrations/ @nextchamp-saqib erpnext/erpnext_integrations/ @nextchamp-saqib
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007 erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar

View File

@@ -248,21 +248,16 @@ class JournalEntry(AccountsController):
): ):
processed_assets.append(d.reference_name) processed_assets.append(d.reference_name)
asset = frappe.db.get_value( asset = frappe.get_doc("Asset", d.reference_name)
"Asset", d.reference_name, ["calculate_depreciation", "value_after_depreciation"], as_dict=1
)
if asset.calculate_depreciation: if asset.calculate_depreciation:
continue continue
depr_value = d.debit or d.credit depr_value = d.debit or d.credit
frappe.db.set_value( asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value)
"Asset",
d.reference_name, asset.set_status()
"value_after_depreciation",
asset.value_after_depreciation - depr_value,
)
def update_inter_company_jv(self): def update_inter_company_jv(self):
if ( if (
@@ -351,12 +346,9 @@ class JournalEntry(AccountsController):
else: else:
depr_value = d.debit or d.credit depr_value = d.debit or d.credit
frappe.db.set_value( asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value)
"Asset",
d.reference_name, asset.set_status()
"value_after_depreciation",
asset.value_after_depreciation + depr_value,
)
def unlink_inter_company_jv(self): def unlink_inter_company_jv(self):
if ( if (

View File

@@ -21,8 +21,24 @@ class POSClosingEntry(StatusUpdater):
if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open": if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry")) frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
self.validate_duplicate_pos_invoices()
self.validate_pos_invoices() self.validate_pos_invoices()
def validate_duplicate_pos_invoices(self):
pos_occurences = {}
for idx, inv in enumerate(self.pos_transactions, 1):
pos_occurences.setdefault(inv.pos_invoice, []).append(idx)
error_list = []
for key, value in pos_occurences.items():
if len(value) > 1:
error_list.append(
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
)
if error_list:
frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True)
def validate_pos_invoices(self): def validate_pos_invoices(self):
invalid_rows = [] invalid_rows = []
for d in self.pos_transactions: for d in self.pos_transactions:

View File

@@ -18,6 +18,22 @@ class POSInvoiceMergeLog(Document):
def validate(self): def validate(self):
self.validate_customer() self.validate_customer()
self.validate_pos_invoice_status() self.validate_pos_invoice_status()
self.validate_duplicate_pos_invoices()
def validate_duplicate_pos_invoices(self):
pos_occurences = {}
for idx, inv in enumerate(self.pos_invoices, 1):
pos_occurences.setdefault(inv.pos_invoice, []).append(idx)
error_list = []
for key, value in pos_occurences.items():
if len(value) > 1:
error_list.append(
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
)
if error_list:
frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True)
def validate_customer(self): def validate_customer(self):
if self.merge_invoices_based_on == "Customer Group": if self.merge_invoices_based_on == "Customer Group":
@@ -427,6 +443,8 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
if closing_entry: if closing_entry:
closing_entry.set_status(update=True, status="Failed") closing_entry.set_status(update=True, status="Failed")
if type(error_message) == list:
error_message = frappe.json.dumps(error_message)
closing_entry.db_set("error_message", error_message) closing_entry.db_set("error_message", error_message)
raise raise

View File

@@ -256,7 +256,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers) tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers)
if cint(tax_details.round_off_tax_amount): if cint(tax_details.round_off_tax_amount):
tax_amount = round(tax_amount) tax_amount = normal_round(tax_amount)
return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount
@@ -555,3 +555,20 @@ def is_valid_certificate(
valid = True valid = True
return valid return valid
def normal_round(number):
"""
Rounds a number to the nearest integer.
:param number: The number to round.
"""
decimal_part = number - int(number)
if decimal_part >= 0.5:
decimal_part = 1
else:
decimal_part = 0
number = int(number) + decimal_part
return number

View File

@@ -246,7 +246,7 @@ frappe.ui.form.on('Asset', {
$.each(depr_entries || [], function(i, v) { $.each(depr_entries || [], function(i, v) {
x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' })); x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' }));
let last_asset_value = asset_values[asset_values.length - 1] let last_asset_value = asset_values[asset_values.length - 1]
asset_values.push(last_asset_value - v.value); asset_values.push(flt(last_asset_value - v.value, precision('gross_purchase_amount')));
}); });
} }

View File

@@ -667,11 +667,15 @@ class Asset(AccountsController):
if self.journal_entry_for_scrap: if self.journal_entry_for_scrap:
status = "Scrapped" status = "Scrapped"
elif self.finance_books: else:
idx = self.get_default_finance_book_idx() or 0 expected_value_after_useful_life = 0
value_after_depreciation = self.value_after_depreciation
expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life if self.calculate_depreciation:
value_after_depreciation = self.finance_books[idx].value_after_depreciation idx = self.get_default_finance_book_idx() or 0
expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life
value_after_depreciation = self.finance_books[idx].value_after_depreciation
if flt(value_after_depreciation) <= expected_value_after_useful_life: if flt(value_after_depreciation) <= expected_value_after_useful_life:
status = "Fully Depreciated" status = "Fully Depreciated"
@@ -703,24 +707,6 @@ class Asset(AccountsController):
if d.finance_book == self.default_finance_book: if d.finance_book == self.default_finance_book:
return cint(d.idx) - 1 return cint(d.idx) - 1
@frappe.whitelist()
def get_manual_depreciation_entries(self):
(_, _, depreciation_expense_account) = get_depreciation_accounts(self)
gle = frappe.qb.DocType("GL Entry")
records = (
frappe.qb.from_(gle)
.select(gle.voucher_no.as_("name"), gle.debit.as_("value"), gle.posting_date)
.where(gle.against_voucher == self.name)
.where(gle.account == depreciation_expense_account)
.where(gle.debit != 0)
.where(gle.is_cancelled == 0)
.orderby(gle.posting_date)
).run(as_dict=True)
return records
def validate_make_gl_entry(self): def validate_make_gl_entry(self):
purchase_document = self.get_purchase_document() purchase_document = self.get_purchase_document()
if not purchase_document: if not purchase_document:
@@ -837,6 +823,25 @@ class Asset(AccountsController):
make_gl_entries(gl_entries) make_gl_entries(gl_entries)
self.db_set("booked_fixed_asset", 1) self.db_set("booked_fixed_asset", 1)
@frappe.whitelist()
def get_manual_depreciation_entries(self):
(_, _, depreciation_expense_account) = get_depreciation_accounts(self)
gle = frappe.qb.DocType("GL Entry")
records = (
frappe.qb.from_(gle)
.select(gle.voucher_no.as_("name"), gle.debit.as_("value"), gle.posting_date)
.where(gle.against_voucher == self.name)
.where(gle.account == depreciation_expense_account)
.where(gle.debit != 0)
.where(gle.is_cancelled == 0)
.orderby(gle.posting_date)
.orderby(gle.creation)
).run(as_dict=True)
return records
@frappe.whitelist() @frappe.whitelist()
def get_depreciation_rate(self, args, on_validate=False): def get_depreciation_rate(self, args, on_validate=False):
if isinstance(args, string_types): if isinstance(args, string_types):

View File

@@ -144,7 +144,7 @@ def make_depreciation_entry(asset_name, date=None):
finance_books.value_after_depreciation -= d.depreciation_amount finance_books.value_after_depreciation -= d.depreciation_amount
finance_books.db_update() finance_books.db_update()
frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Successful") asset.db_set("depr_entry_posting_status", "Successful")
asset.set_status() asset.set_status()

View File

@@ -82,6 +82,9 @@ class AssetRepair(AccountsController):
self.asset_doc.prepare_depreciation_data() self.asset_doc.prepare_depreciation_data()
self.asset_doc.save() self.asset_doc.save()
def after_delete(self):
frappe.get_doc("Asset", self.asset).set_status()
def check_repair_status(self): def check_repair_status(self):
if self.repair_status == "Pending": if self.repair_status == "Pending":
frappe.throw(_("Please update Repair Status.")) frappe.throw(_("Please update Repair Status."))

View File

@@ -151,6 +151,7 @@ def prepare_chart_data(data, filters):
filters.filter_based_on, filters.filter_based_on,
"Monthly", "Monthly",
company=filters.company, company=filters.company,
ignore_fiscal_year=True,
) )
for d in period_list: for d in period_list:

View File

@@ -2,53 +2,60 @@
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
frappe.query_reports["Employee Leave Balance"] = { frappe.query_reports["Employee Leave Balance"] = {
"filters": [ filters: [
{ {
"fieldname": "from_date", fieldname: "from_date",
"label": __("From Date"), label: __("From Date"),
"fieldtype": "Date", fieldtype: "Date",
"reqd": 1, reqd: 1,
"default": frappe.defaults.get_default("year_start_date") default: frappe.defaults.get_default("year_start_date")
}, },
{ {
"fieldname": "to_date", fieldname: "to_date",
"label": __("To Date"), label: __("To Date"),
"fieldtype": "Date", fieldtype: "Date",
"reqd": 1, reqd: 1,
"default": frappe.defaults.get_default("year_end_date") default: frappe.defaults.get_default("year_end_date")
}, },
{ {
"fieldname": "company", label: __("Company"),
"label": __("Company"), fieldname: "company",
"fieldtype": "Link", fieldtype: "Link",
"options": "Company", options: "Company",
"reqd": 1, reqd: 1,
"default": frappe.defaults.get_user_default("Company") default: frappe.defaults.get_user_default("Company")
}, },
{ {
"fieldname": "department", fieldname: "department",
"label": __("Department"), label: __("Department"),
"fieldtype": "Link", fieldtype: "Link",
"options": "Department", options: "Department",
}, },
{ {
"fieldname": "employee", fieldname: "employee",
"label": __("Employee"), label: __("Employee"),
"fieldtype": "Link", fieldtype: "Link",
"options": "Employee", options: "Employee",
}, },
{ {
"fieldname": "employee_status", fieldname: "employee_status",
"label": __("Employee Status"), label: __("Employee Status"),
"fieldtype": "Select", fieldtype: "Select",
"options": [ options: [
"", "",
{ "value": "Active", "label": __("Active") }, { "value": "Active", "label": __("Active") },
{ "value": "Inactive", "label": __("Inactive") }, { "value": "Inactive", "label": __("Inactive") },
{ "value": "Suspended", "label": __("Suspended") }, { "value": "Suspended", "label": __("Suspended") },
{ "value": "Left", "label": __("Left") }, { "value": "Left", "label": __("Left") },
], ],
"default": "Active", default: "Active",
},
{
fieldname: "consolidate_leave_types",
label: __("Consolidate Leave Types"),
fieldtype: "Check",
default: 1,
depends_on: "eval: !doc.employee",
} }
], ],

View File

@@ -7,7 +7,7 @@ from typing import Dict, List, Optional, Tuple
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import add_days, getdate from frappe.utils import add_days, cint, getdate
from erpnext.hr.doctype.leave_allocation.leave_allocation import get_previous_allocation from erpnext.hr.doctype.leave_allocation.leave_allocation import get_previous_allocation
from erpnext.hr.doctype.leave_application.leave_application import ( from erpnext.hr.doctype.leave_application.leave_application import (
@@ -24,7 +24,7 @@ def execute(filters: Optional[Filters] = None) -> Tuple:
columns = get_columns() columns = get_columns()
data = get_data(filters) data = get_data(filters)
charts = get_chart_data(data) charts = get_chart_data(data, filters)
return columns, data, None, charts return columns, data, None, charts
@@ -89,7 +89,7 @@ def get_data(filters: Filters) -> List:
conditions = get_conditions(filters) conditions = get_conditions(filters)
user = frappe.session.user user = frappe.session.user
department_approver_map = get_department_leave_approver_map(filters.get("department")) department_approver_map = get_department_leave_approver_map(filters.department)
active_employees = frappe.get_list( active_employees = frappe.get_list(
"Employee", "Employee",
@@ -97,22 +97,27 @@ def get_data(filters: Filters) -> List:
fields=["name", "employee_name", "department", "user_id", "leave_approver"], fields=["name", "employee_name", "department", "user_id", "leave_approver"],
) )
precision = cint(frappe.db.get_single_value("System Settings", "float_precision", cache=True))
consolidate_leave_types = len(active_employees) > 1 and filters.consolidate_leave_types
row = None
data = [] data = []
for leave_type in leave_types: for leave_type in leave_types:
if len(active_employees) > 1: if consolidate_leave_types:
data.append({"leave_type": leave_type}) data.append({"leave_type": leave_type})
else: else:
row = frappe._dict({"leave_type": leave_type}) row = frappe._dict({"leave_type": leave_type})
for employee in active_employees: for employee in active_employees:
leave_approvers = department_approver_map.get(employee.department_name, []).append( leave_approvers = department_approver_map.get(employee.department_name, []).append(
employee.leave_approver employee.leave_approver
) )
if len(active_employees) > 1: if consolidate_leave_types:
row = frappe._dict() row = frappe._dict()
else:
row = frappe._dict({"leave_type": leave_type})
row.employee = employee.name row.employee = employee.name
row.employee_name = employee.employee_name row.employee_name = employee.employee_name
@@ -166,17 +171,17 @@ def get_opening_balance(
def get_conditions(filters: Filters) -> Dict: def get_conditions(filters: Filters) -> Dict:
conditions = {} conditions = {}
if filters.get("employee"): if filters.employee:
conditions["name"] = filters.get("employee") conditions["name"] = filters.employee
if filters.get("company"): if filters.company:
conditions["company"] = filters.get("company") conditions["company"] = filters.company
if filters.get("department"): if filters.department:
conditions["department"] = filters.get("department") conditions["department"] = filters.department
if filters.get("employee_status"): if filters.employee_status:
conditions["status"] = filters.get("employee_status") conditions["status"] = filters.employee_status
return conditions return conditions
@@ -268,12 +273,15 @@ def get_leave_ledger_entries(
return records return records
def get_chart_data(data: List) -> Dict: def get_chart_data(data: List, filters: Filters) -> Dict:
labels = [] labels = []
datasets = [] datasets = []
employee_data = data employee_data = data
if data and data[0].get("employee_name"): if not data:
return None
if data and filters.employee:
get_dataset_for_chart(employee_data, datasets, labels) get_dataset_for_chart(employee_data, datasets, labels)
chart = { chart = {

View File

@@ -119,7 +119,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
frappe.model.round_floats_in(item); frappe.model.round_floats_in(item);
item.net_rate = item.rate; item.net_rate = item.rate;
item.qty = item.qty === undefined ? (me.frm.doc.is_return ? -1 : 1) : item.qty; item.qty = item.qty === undefined ? (me.frm.doc.is_return ? -1 : 1) : item.qty;
item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item));
if (!(me.frm.doc.is_return || me.frm.doc.is_debit_note)) {
item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item));
}
else {
let qty = item.qty || 1;
qty = me.frm.doc.is_return ? -1 * qty : qty;
item.net_amount = item.amount = flt(item.rate * qty, precision("amount", item));
}
item.item_tax_amount = 0.0; item.item_tax_amount = 0.0;
item.total_weight = flt(item.weight_per_unit * item.stock_qty); item.total_weight = flt(item.weight_per_unit * item.stock_qty);

View File

@@ -216,7 +216,7 @@ def get_sales_order_details(company_list, filters):
) )
if filters.get("item_group"): if filters.get("item_group"):
query = query.where(db_so_item.item_group == frappe.db.escape(filters.item_group)) query = query.where(db_so_item.item_group == filters.item_group)
if filters.get("from_date"): if filters.get("from_date"):
query = query.where(db_so.transaction_date >= filters.from_date) query = query.where(db_so.transaction_date >= filters.from_date)
@@ -225,7 +225,7 @@ def get_sales_order_details(company_list, filters):
query = query.where(db_so.transaction_date <= filters.to_date) query = query.where(db_so.transaction_date <= filters.to_date)
if filters.get("item_code"): if filters.get("item_code"):
query = query.where(db_so_item.item_group == frappe.db.escape(filters.item_code)) query = query.where(db_so_item.item_code == filters.item_code)
if filters.get("customer"): if filters.get("customer"):
query = query.where(db_so.customer == filters.customer) query = query.where(db_so.customer == filters.customer)

View File

@@ -97,12 +97,12 @@ frappe.ui.form.on("Delivery Note", {
} }
if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) { if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
let internal = me.frm.doc.is_internal_customer; let internal = frm.doc.is_internal_customer;
if (internal) { if (internal) {
let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" : let button_label = (frm.doc.company === frm.doc.represents_company) ? "Internal Purchase Receipt" :
"Inter Company Purchase Receipt"; "Inter Company Purchase Receipt";
me.frm.add_custom_button(button_label, function() { frm.add_custom_button(__(button_label), function() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt', method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
frm: frm, frm: frm,

View File

@@ -38,5 +38,19 @@
"price_list_rate": 1000, "price_list_rate": 1000,
"valid_from": "2017-04-10", "valid_from": "2017-04-10",
"valid_upto": "2017-04-17" "valid_upto": "2017-04-17"
},
{
"doctype": "Item Price",
"item_code": "_Test Item",
"price_list": "_Test Buying Price List",
"price_list_rate": 100,
"supplier": "_Test Supplier"
},
{
"doctype": "Item Price",
"item_code": "_Test Item",
"price_list": "_Test Selling Price List",
"price_list_rate": 200,
"customer": "_Test Customer"
} }
] ]

View File

@@ -31,5 +31,21 @@
"enabled": 1, "enabled": 1,
"price_list_name": "_Test Price List Rest of the World", "price_list_name": "_Test Price List Rest of the World",
"selling": 1 "selling": 1
},
{
"buying": 0,
"currency": "USD",
"doctype": "Price List",
"enabled": 1,
"price_list_name": "_Test Selling Price List",
"selling": 1
},
{
"buying": 1,
"currency": "USD",
"doctype": "Price List",
"enabled": 1,
"price_list_name": "_Test Buying Price List",
"selling": 0
} }
] ]

View File

@@ -88,8 +88,15 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru
update_party_blanket_order(args, out) update_party_blanket_order(args, out)
# Never try to find a customer price if customer is set in these Doctype
current_customer = args.customer
if args.get("doctype") in ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]:
args.customer = None
out.update(get_price_list_rate(args, item)) out.update(get_price_list_rate(args, item))
args.customer = current_customer
if args.customer and cint(args.is_pos): if args.customer and cint(args.is_pos):
out.update(get_pos_profile_item_details(args.company, args, update_data=True)) out.update(get_pos_profile_item_details(args.company, args, update_data=True))

View File

@@ -122,7 +122,7 @@ def get_reserved_qty(item_code, warehouse):
and parenttype="Sales Order" and parenttype="Sales Order"
and item_code != parent_item and item_code != parent_item
and exists (select * from `tabSales Order` so and exists (select * from `tabSales Order` so
where name = dnpi_in.parent and docstatus = 1 and status != 'Closed') where name = dnpi_in.parent and docstatus = 1 and status not in ('On Hold', 'Closed'))
) dnpi) ) dnpi)
union union
(select stock_qty as dnpi_qty, qty as so_item_qty, (select stock_qty as dnpi_qty, qty as so_item_qty,
@@ -132,7 +132,7 @@ def get_reserved_qty(item_code, warehouse):
and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0) and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0)
and exists(select * from `tabSales Order` so and exists(select * from `tabSales Order` so
where so.name = so_item.parent and so.docstatus = 1 where so.name = so_item.parent and so.docstatus = 1
and so.status != 'Closed')) and so.status not in ('On Hold', 'Closed')))
) tab ) tab
where where
so_item_qty >= so_item_delivered_qty so_item_qty >= so_item_delivered_qty

View File

@@ -0,0 +1,40 @@
import json
import frappe
from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.get_item_details import get_item_details
test_ignore = ["BOM"]
test_dependencies = ["Customer", "Supplier", "Item", "Price List", "Item Price"]
class TestGetItemDetail(FrappeTestCase):
def setUp(self):
make_test_records("Price List")
super().setUp()
def test_get_item_detail_purchase_order(self):
args = frappe._dict(
{
"item_code": "_Test Item",
"company": "_Test Company",
"customer": "_Test Customer",
"conversion_rate": 1.0,
"price_list_currency": "USD",
"plc_conversion_rate": 1.0,
"doctype": "Purchase Order",
"name": None,
"supplier": "_Test Supplier",
"transaction_date": None,
"conversion_rate": 1.0,
"price_list": "_Test Buying Price List",
"is_subcontracted": 0,
"ignore_pricing_rule": 1,
"qty": 1,
}
)
details = get_item_details(args)
self.assertEqual(details.get("price_list_rate"), 100)

View File

@@ -51,36 +51,31 @@ def get_tabs(categories):
return tab_values return tab_values
def get_category_records(categories): def get_category_records(categories: list):
categorical_data = {} categorical_data = {}
for category in categories:
if category == "item_group": for c in categories:
categorical_data["item_group"] = frappe.db.sql( if c == "item_group":
""" categorical_data["item_group"] = frappe.db.get_all(
Select "Item Group",
name, parent_item_group, is_group, image, route filters={"parent_item_group": "All Item Groups", "show_in_website": 1},
from fields=["name", "parent_item_group", "is_group", "image", "route"],
`tabItem Group`
where
parent_item_group = 'All Item Groups'
and show_in_website = 1
""",
as_dict=1,
) )
else:
doctype = frappe.unscrub(category) continue
fields = ["name"]
if frappe.get_meta(doctype, cached=True).get_field("image"): doctype = frappe.unscrub(c)
fields = ["name"]
try:
meta = frappe.get_meta(doctype, cached=True)
if meta.get_field("image"):
fields += ["image"] fields += ["image"]
categorical_data[category] = frappe.db.sql( data = frappe.db.get_all(doctype, fields=fields)
f""" categorical_data[c] = data
Select except BaseException:
{",".join(fields)} frappe.throw(_("DocType {} not found").format(doctype))
from continue
`tab{doctype}`
""",
as_dict=1,
)
return categorical_data return categorical_data