mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-23 15:09:20 +00:00
Merge branch 'develop' into employee_query
This commit is contained in:
@@ -236,6 +236,10 @@ frappe.treeview_settings["Account"] = {
|
|||||||
root_company,
|
root_company,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
|
const node = treeview.tree.get_selected_node();
|
||||||
|
if (node.is_root) {
|
||||||
|
frappe.throw(__("Cannot create root account."));
|
||||||
|
}
|
||||||
treeview.new_node();
|
treeview.new_node();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -254,7 +258,8 @@ frappe.treeview_settings["Account"] = {
|
|||||||
].treeview.page.fields_dict.root_company.get_value() ||
|
].treeview.page.fields_dict.root_company.get_value() ||
|
||||||
frappe.flags.ignore_root_company_validation) &&
|
frappe.flags.ignore_root_company_validation) &&
|
||||||
node.expandable &&
|
node.expandable &&
|
||||||
!node.hide_add
|
!node.hide_add &&
|
||||||
|
!node.is_root
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
click: function () {
|
click: function () {
|
||||||
|
|||||||
@@ -1133,13 +1133,6 @@ def create_pos_invoice(**args):
|
|||||||
return pos_inv
|
return pos_inv
|
||||||
|
|
||||||
|
|
||||||
def make_batch_item(item_name):
|
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
|
||||||
|
|
||||||
if not frappe.db.exists(item_name):
|
|
||||||
return make_item(item_name, dict(has_batch_no=1, create_new_batch=1, is_stock_item=1))
|
|
||||||
|
|
||||||
|
|
||||||
def set_allow_partial_payment(pos_profile, value):
|
def set_allow_partial_payment(pos_profile, value):
|
||||||
pos_profile.reload()
|
pos_profile.reload()
|
||||||
pos_profile.allow_partial_payment = value
|
pos_profile.allow_partial_payment = value
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
|||||||
options: "Posting Date\nDue Date",
|
options: "Posting Date\nDue Date",
|
||||||
default: "Due Date",
|
default: "Due Date",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldname: "calculate_ageing_with",
|
||||||
|
label: __("Calculate Ageing With"),
|
||||||
|
fieldtype: "Select",
|
||||||
|
options: "Report Date\nToday Date",
|
||||||
|
default: "Report Date",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldname: "range",
|
fieldname: "range",
|
||||||
label: __("Ageing Range"),
|
label: __("Ageing Range"),
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
|||||||
options: "Posting Date\nDue Date",
|
options: "Posting Date\nDue Date",
|
||||||
default: "Due Date",
|
default: "Due Date",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldname: "calculate_ageing_with",
|
||||||
|
label: __("Calculate Ageing With"),
|
||||||
|
fieldtype: "Select",
|
||||||
|
options: "Report Date\nToday Date",
|
||||||
|
default: "Report Date",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldname: "range",
|
fieldname: "range",
|
||||||
label: __("Ageing Range"),
|
label: __("Ageing Range"),
|
||||||
|
|||||||
@@ -117,6 +117,11 @@ frappe.query_reports["Trial Balance"] = {
|
|||||||
fieldtype: "Check",
|
fieldtype: "Check",
|
||||||
default: 1,
|
default: 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldname: "show_account_name_and_number",
|
||||||
|
label: __("Show Account Name and Number"),
|
||||||
|
fieldtype: "Check",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
formatter: erpnext.financial_statements.formatter,
|
formatter: erpnext.financial_statements.formatter,
|
||||||
tree: true,
|
tree: true,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ value_fields = (
|
|||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
validate_filters(filters)
|
validate_filters(filters)
|
||||||
data = get_data(filters)
|
data = get_data(filters)
|
||||||
columns = get_columns()
|
columns = get_columns(filters)
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
@@ -407,6 +407,10 @@ def prepare_data(accounts, filters, parent_children_map, company_currency):
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if filters.get("show_account_name_and_number"):
|
||||||
|
row["acc_name"] = d.account_name
|
||||||
|
row["acc_number"] = d.account_number
|
||||||
|
|
||||||
for key in value_fields:
|
for key in value_fields:
|
||||||
row[key] = flt(d.get(key, 0.0), 3)
|
row[key] = flt(d.get(key, 0.0), 3)
|
||||||
|
|
||||||
@@ -427,7 +431,24 @@ def prepare_data(accounts, filters, parent_children_map, company_currency):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_columns():
|
def get_columns(filters):
|
||||||
|
account_name_number_cols = []
|
||||||
|
if filters.get("show_account_name_and_number"):
|
||||||
|
account_name_number_cols = [
|
||||||
|
{
|
||||||
|
"fieldname": "acc_name",
|
||||||
|
"label": _("Account Name"),
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 250,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "acc_number",
|
||||||
|
"label": _("Account Number"),
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"fieldname": "account",
|
"fieldname": "account",
|
||||||
@@ -436,6 +457,7 @@ def get_columns():
|
|||||||
"options": "Account",
|
"options": "Account",
|
||||||
"width": 300,
|
"width": 300,
|
||||||
},
|
},
|
||||||
|
*account_name_number_cols,
|
||||||
{
|
{
|
||||||
"fieldname": "currency",
|
"fieldname": "currency",
|
||||||
"label": _("Currency"),
|
"label": _("Currency"),
|
||||||
|
|||||||
@@ -1608,6 +1608,7 @@ class WorkOrder(Document):
|
|||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
},
|
},
|
||||||
fields=[
|
fields=[
|
||||||
|
"item_code",
|
||||||
"name",
|
"name",
|
||||||
"stock_qty",
|
"stock_qty",
|
||||||
"stock_reserved_qty",
|
"stock_reserved_qty",
|
||||||
|
|||||||
@@ -425,3 +425,4 @@ erpnext.patches.v15_0.rename_pos_closing_entry_fields #2025-06-13
|
|||||||
erpnext.patches.v15_0.update_pegged_currencies
|
erpnext.patches.v15_0.update_pegged_currencies
|
||||||
erpnext.patches.v15_0.set_status_cancelled_on_cancelled_pos_opening_entry_and_pos_closing_entry
|
erpnext.patches.v15_0.set_status_cancelled_on_cancelled_pos_opening_entry_and_pos_closing_entry
|
||||||
erpnext.patches.v15_0.set_company_on_pos_inv_merge_log
|
erpnext.patches.v15_0.set_company_on_pos_inv_merge_log
|
||||||
|
erpnext.patches.v15_0.rename_price_list_to_buying_price_list
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe.model.utils.rename_field import rename_field
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
if frappe.db.has_column("Material Request", "price_list"):
|
||||||
|
rename_field(
|
||||||
|
"Material Request",
|
||||||
|
"price_list",
|
||||||
|
"buying_price_list",
|
||||||
|
)
|
||||||
@@ -582,7 +582,6 @@ erpnext.buying.get_items_from_product_bundle = function(frm) {
|
|||||||
ignore_pricing_rule: frm.doc.ignore_pricing_rule,
|
ignore_pricing_rule: frm.doc.ignore_pricing_rule,
|
||||||
doctype: frm.doc.doctype
|
doctype: frm.doc.doctype
|
||||||
},
|
},
|
||||||
price_list: frm.doc.price_list,
|
|
||||||
},
|
},
|
||||||
freeze: true,
|
freeze: true,
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
}
|
}
|
||||||
item.base_rate_with_margin = item.rate_with_margin * flt(frm.doc.conversion_rate);
|
item.base_rate_with_margin = item.rate_with_margin * flt(frm.doc.conversion_rate);
|
||||||
|
|
||||||
|
cur_frm.cscript.set_gross_profit(item);
|
||||||
|
cur_frm.cscript.calculate_taxes_and_totals();
|
||||||
|
cur_frm.cscript.calculate_stock_uom_rate(frm, cdt, cdn);
|
||||||
|
|
||||||
if (item.item_code && item.rate) {
|
if (item.item_code && item.rate) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.stock.get_item_details.get_item_tax_template",
|
method: "erpnext.stock.get_item_details.get_item_tax_template",
|
||||||
@@ -63,10 +67,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript.set_gross_profit(item);
|
|
||||||
cur_frm.cscript.calculate_taxes_and_totals();
|
|
||||||
cur_frm.cscript.calculate_stock_uom_rate(frm, cdt, cdn);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on(this.frm.cscript.tax_table, "rate", function(frm, cdt, cdn) {
|
frappe.ui.form.on(this.frm.cscript.tax_table, "rate", function(frm, cdt, cdn) {
|
||||||
|
|||||||
@@ -35,9 +35,11 @@ class TestBatch(IntegrationTestCase):
|
|||||||
def make_batch_item(cls, item_name=None):
|
def make_batch_item(cls, item_name=None):
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
if not frappe.db.exists(item_name):
|
if not frappe.db.exists("Item", item_name):
|
||||||
return make_item(item_name, dict(has_batch_no=1, create_new_batch=1, is_stock_item=1))
|
return make_item(item_name, dict(has_batch_no=1, create_new_batch=1, is_stock_item=1))
|
||||||
|
|
||||||
|
return frappe.get_doc("Item", item_name)
|
||||||
|
|
||||||
def test_purchase_receipt(self, batch_qty=100):
|
def test_purchase_receipt(self, batch_qty=100):
|
||||||
"""Test automated batch creation from Purchase Receipt"""
|
"""Test automated batch creation from Purchase Receipt"""
|
||||||
self.make_batch_item("ITEM-BATCH-1")
|
self.make_batch_item("ITEM-BATCH-1")
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ frappe.ui.form.on("Material Request", {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("price_list", () => {
|
frm.set_query("buying_price_list", () => {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
buying: 1,
|
buying: 1,
|
||||||
@@ -87,7 +87,7 @@ frappe.ui.form.on("Material Request", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
|
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
|
||||||
frm.doc.price_list = frappe.defaults.get_default("buying_price_list");
|
frm.doc.buying_price_list = frappe.defaults.get_default("buying_price_list");
|
||||||
},
|
},
|
||||||
|
|
||||||
company: function (frm) {
|
company: function (frm) {
|
||||||
@@ -269,8 +269,8 @@ frappe.ui.form.on("Material Request", {
|
|||||||
from_warehouse: item.from_warehouse,
|
from_warehouse: item.from_warehouse,
|
||||||
warehouse: item.warehouse,
|
warehouse: item.warehouse,
|
||||||
doctype: frm.doc.doctype,
|
doctype: frm.doc.doctype,
|
||||||
buying_price_list: frm.doc.price_list
|
buying_price_list: frm.doc.buying_price_list
|
||||||
? frm.doc.price_list
|
? frm.doc.buying_price_list
|
||||||
: frappe.defaults.get_default("buying_price_list"),
|
: frappe.defaults.get_default("buying_price_list"),
|
||||||
currency: frappe.defaults.get_default("Currency"),
|
currency: frappe.defaults.get_default("Currency"),
|
||||||
name: frm.doc.name,
|
name: frm.doc.name,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"column_break_2",
|
"column_break_2",
|
||||||
"transaction_date",
|
"transaction_date",
|
||||||
"schedule_date",
|
"schedule_date",
|
||||||
"price_list",
|
"buying_price_list",
|
||||||
"amended_from",
|
"amended_from",
|
||||||
"warehouse_section",
|
"warehouse_section",
|
||||||
"scan_barcode",
|
"scan_barcode",
|
||||||
@@ -354,7 +354,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "price_list",
|
"fieldname": "buying_price_list",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Price List",
|
"label": "Price List",
|
||||||
"options": "Price List"
|
"options": "Price List"
|
||||||
@@ -364,7 +364,7 @@
|
|||||||
"idx": 70,
|
"idx": 70,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-07-07 13:15:28.615984",
|
"modified": "2025-07-11 21:03:26.588307",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Material Request",
|
"name": "Material Request",
|
||||||
|
|||||||
@@ -163,8 +163,8 @@ class MaterialRequest(BuyingController):
|
|||||||
|
|
||||||
self.validate_pp_qty()
|
self.validate_pp_qty()
|
||||||
|
|
||||||
if not self.price_list:
|
if not self.buying_price_list:
|
||||||
self.price_list = frappe.defaults.get_defaults().buying_price_list
|
self.buying_price_list = frappe.defaults.get_defaults().buying_price_list
|
||||||
|
|
||||||
def validate_pp_qty(self):
|
def validate_pp_qty(self):
|
||||||
items_from_pp = [item for item in self.items if item.material_request_plan_item]
|
items_from_pp = [item for item in self.items if item.material_request_plan_item]
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ def on_doctype_update():
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_items_from_product_bundle(row, price_list):
|
def get_items_from_product_bundle(row):
|
||||||
row, items = ItemDetailsCtx(json.loads(row)), []
|
row, items = ItemDetailsCtx(json.loads(row)), []
|
||||||
|
|
||||||
bundled_items = get_product_bundle_items(row["item_code"])
|
bundled_items = get_product_bundle_items(row["item_code"])
|
||||||
@@ -359,7 +359,6 @@ def get_items_from_product_bundle(row, price_list):
|
|||||||
"item_code": item.item_code,
|
"item_code": item.item_code,
|
||||||
"qty": flt(row["quantity"]) * flt(item.qty),
|
"qty": flt(row["quantity"]) * flt(item.qty),
|
||||||
"conversion_rate": 1,
|
"conversion_rate": 1,
|
||||||
"price_list": price_list,
|
|
||||||
"currency": frappe.defaults.get_defaults().currency,
|
"currency": frappe.defaults.get_defaults().currency,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2178,15 +2178,15 @@ def get_reserved_batches_for_sre(kwargs) -> dict:
|
|||||||
|
|
||||||
if kwargs.batch_no:
|
if kwargs.batch_no:
|
||||||
if isinstance(kwargs.batch_no, list):
|
if isinstance(kwargs.batch_no, list):
|
||||||
query = query.where(sb_entry.batch_no.isin(kwargs.batch_no))
|
query = query.where(sb_entry.batch_no.notin(kwargs.batch_no))
|
||||||
else:
|
else:
|
||||||
query = query.where(sb_entry.batch_no == kwargs.batch_no)
|
query = query.where(sb_entry.batch_no != kwargs.batch_no)
|
||||||
|
|
||||||
if kwargs.warehouse:
|
if kwargs.warehouse:
|
||||||
if isinstance(kwargs.warehouse, list):
|
if isinstance(kwargs.warehouse, list):
|
||||||
query = query.where(sre.warehouse.isin(kwargs.warehouse))
|
query = query.where(sre.warehouse.notin(kwargs.warehouse))
|
||||||
else:
|
else:
|
||||||
query = query.where(sre.warehouse == kwargs.warehouse)
|
query = query.where(sre.warehouse != kwargs.warehouse)
|
||||||
|
|
||||||
if kwargs.ignore_voucher_nos:
|
if kwargs.ignore_voucher_nos:
|
||||||
query = query.where(sre.name.notin(kwargs.ignore_voucher_nos))
|
query = query.where(sre.name.notin(kwargs.ignore_voucher_nos))
|
||||||
@@ -2203,12 +2203,25 @@ def get_reserved_batches_for_sre(kwargs) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
def get_auto_batch_nos(kwargs):
|
def get_auto_batch_nos(kwargs):
|
||||||
|
if kwargs.against_sales_order and (
|
||||||
|
only_consider_batches := get_batches_to_be_considered(kwargs.against_sales_order)
|
||||||
|
):
|
||||||
|
batches, warehouses = [], []
|
||||||
|
for item in only_consider_batches:
|
||||||
|
batches.append(item.batch_no)
|
||||||
|
warehouses.append(item.warehouse)
|
||||||
|
|
||||||
|
if batches:
|
||||||
|
kwargs.batch_no = batches
|
||||||
|
kwargs.warehouse = warehouses
|
||||||
available_batches = get_available_batches(kwargs)
|
available_batches = get_available_batches(kwargs)
|
||||||
qty = flt(kwargs.qty)
|
qty = flt(kwargs.qty)
|
||||||
|
|
||||||
stock_ledgers_batches = get_stock_ledgers_batches(kwargs)
|
stock_ledgers_batches = get_stock_ledgers_batches(kwargs)
|
||||||
pos_invoice_batches = get_reserved_batches_for_pos(kwargs)
|
pos_invoice_batches = get_reserved_batches_for_pos(kwargs)
|
||||||
sre_reserved_batches = get_reserved_batches_for_sre(kwargs)
|
sre_reserved_batches = get_reserved_batches_for_sre(kwargs)
|
||||||
|
kwargs.batch_no = kwargs.warehouse = None
|
||||||
|
|
||||||
picked_batches = frappe._dict()
|
picked_batches = frappe._dict()
|
||||||
if kwargs.get("is_pick_list"):
|
if kwargs.get("is_pick_list"):
|
||||||
picked_batches = get_picked_batches(kwargs)
|
picked_batches = get_picked_batches(kwargs)
|
||||||
@@ -2238,6 +2251,25 @@ def get_auto_batch_nos(kwargs):
|
|||||||
return get_qty_based_available_batches(available_batches, qty)
|
return get_qty_based_available_batches(available_batches, qty)
|
||||||
|
|
||||||
|
|
||||||
|
def get_batches_to_be_considered(sales_order_name):
|
||||||
|
parent = frappe.qb.DocType("Stock Reservation Entry")
|
||||||
|
child = frappe.qb.DocType("Serial and Batch Entry")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(parent)
|
||||||
|
.join(child)
|
||||||
|
.on(parent.name == child.parent)
|
||||||
|
.select(child.batch_no, child.warehouse)
|
||||||
|
.distinct()
|
||||||
|
.where(
|
||||||
|
(parent.docstatus == 1)
|
||||||
|
& (parent.voucher_no == sales_order_name)
|
||||||
|
& (child.delivered_qty < child.qty)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return query.run(as_dict=True)
|
||||||
|
|
||||||
|
|
||||||
def filter_zero_near_batches(available_batches, kwargs):
|
def filter_zero_near_batches(available_batches, kwargs):
|
||||||
kwargs.batch_no = [d.batch_no for d in available_batches]
|
kwargs.batch_no = [d.batch_no for d in available_batches]
|
||||||
|
|
||||||
|
|||||||
@@ -451,7 +451,9 @@ class StockEntry(StockController):
|
|||||||
additional_cost_amt = additional_costs[0][0] if additional_costs else 0
|
additional_cost_amt = additional_costs[0][0] if additional_costs else 0
|
||||||
|
|
||||||
amount += additional_cost_amt
|
amount += additional_cost_amt
|
||||||
frappe.db.set_value("Project", self.project, "total_consumed_material_cost", amount)
|
project = frappe.get_doc("Project", self.project)
|
||||||
|
project.total_consumed_material_cost = amount
|
||||||
|
project.save()
|
||||||
|
|
||||||
def validate_item(self):
|
def validate_item(self):
|
||||||
stock_items = self.get_stock_items()
|
stock_items = self.get_stock_items()
|
||||||
|
|||||||
@@ -1084,17 +1084,14 @@ class StockReconciliation(StockController):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if add_new_sle and not frappe.db.get_value(
|
||||||
add_new_sle
|
"Stock Ledger Entry",
|
||||||
and not frappe.db.get_value(
|
{"voucher_detail_no": row.name, "actual_qty": ("<", 0), "is_cancelled": 0},
|
||||||
"Stock Ledger Entry",
|
"name",
|
||||||
{"voucher_detail_no": row.name, "actual_qty": ("<", 0), "is_cancelled": 0},
|
|
||||||
"name",
|
|
||||||
)
|
|
||||||
and not row.current_serial_and_batch_bundle
|
|
||||||
):
|
):
|
||||||
self.set_current_serial_and_batch_bundle(voucher_detail_no, save=True)
|
if not row.current_serial_and_batch_bundle:
|
||||||
row.reload()
|
self.set_current_serial_and_batch_bundle(voucher_detail_no, save=True)
|
||||||
|
row.reload()
|
||||||
|
|
||||||
self.add_missing_stock_ledger_entry(row, voucher_detail_no, sle_creation)
|
self.add_missing_stock_ledger_entry(row, voucher_detail_no, sle_creation)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user