Merge pull request #35709 from ruchamahabal/remove-hierarchy-chart
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
index 043fbdd..5d239c9 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -186,7 +186,7 @@
# round off balance based on currency precision
# and consider debit-credit difference allowance
currency_precision = get_currency_precision()
- rounding_loss_allowance = rounding_loss_allowance or 0.05
+ rounding_loss_allowance = float(rounding_loss_allowance) or 0.05
for acc in account_details:
acc.balance_in_account_currency = flt(acc.balance_in_account_currency, currency_precision)
if abs(acc.balance_in_account_currency) <= rounding_loss_allowance:
@@ -552,7 +552,7 @@
@frappe.whitelist()
def get_account_details(
- company, posting_date, account, party_type=None, party=None, rounding_loss_allowance=None
+ company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float = None
):
if not (company and posting_date):
frappe.throw(_("Company and Posting Date is mandatory"))
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 42eb018..45bddfc 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -642,13 +642,6 @@
gle_filters={"account": "Stock In Hand - TCP1"},
)
- # assert loss booked in COGS
- self.assertGLEs(
- return_pi,
- [{"credit": 0, "debit": 200}],
- gle_filters={"account": "Cost of Goods Sold - TCP1"},
- )
-
def test_return_with_lcv(self):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import (
@@ -1671,6 +1664,21 @@
self.assertTrue(return_pi.docstatus == 1)
+ def test_gl_entries_for_standalone_debit_note(self):
+ make_purchase_invoice(qty=5, rate=500, update_stock=True)
+
+ returned_inv = make_purchase_invoice(qty=-5, rate=5, update_stock=True, is_return=True)
+
+ # override the rate with valuation rate
+ sle = frappe.get_all(
+ "Stock Ledger Entry",
+ fields=["stock_value_difference", "actual_qty"],
+ filters={"voucher_no": returned_inv.name},
+ )[0]
+
+ rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
+ self.assertAlmostEqual(returned_inv.items[0].rate, rate)
+
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql(
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index c64b917..bfef57e 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -513,18 +513,22 @@
},
item=asset,
),
- asset.get_gl_dict(
- {
- "account": accumulated_depr_account,
- "debit_in_account_currency": accumulated_depr_amount,
- "debit": accumulated_depr_amount,
- "cost_center": depreciation_cost_center,
- "posting_date": date,
- },
- item=asset,
- ),
]
+ if accumulated_depr_amount:
+ gl_entries.append(
+ asset.get_gl_dict(
+ {
+ "account": accumulated_depr_account,
+ "debit_in_account_currency": accumulated_depr_amount,
+ "debit": accumulated_depr_amount,
+ "cost_center": depreciation_cost_center,
+ "posting_date": date,
+ },
+ item=asset,
+ ),
+ )
+
profit_amount = flt(selling_amount) - flt(value_after_depreciation)
if profit_amount:
get_profit_gl_entries(
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index ad6a49a..a3a1461 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -26,6 +26,8 @@
self.flags.ignore_permlevel_for_fields = ["buying_price_list", "price_list_currency"]
def validate(self):
+ self.set_rate_for_standalone_debit_note()
+
super(BuyingController, self).validate()
if getattr(self, "supplier", None) and not self.supplier_name:
self.supplier_name = frappe.db.get_value("Supplier", self.supplier, "supplier_name")
@@ -100,6 +102,30 @@
do_not_submit=True,
)
+ def set_rate_for_standalone_debit_note(self):
+ if self.get("is_return") and self.get("update_stock") and not self.return_against:
+ for row in self.items:
+
+ # override the rate with valuation rate
+ row.rate = get_incoming_rate(
+ {
+ "item_code": row.item_code,
+ "warehouse": row.warehouse,
+ "posting_date": self.get("posting_date"),
+ "posting_time": self.get("posting_time"),
+ "qty": row.qty,
+ "serial_and_batch_bundle": row.get("serial_and_batch_bundle"),
+ "company": self.company,
+ "voucher_type": self.doctype,
+ "voucher_no": self.name,
+ },
+ raise_error_if_no_rate=False,
+ )
+
+ row.discount_percentage = 0.0
+ row.discount_amount = 0.0
+ row.margin_rate_or_amount = 0.0
+
def set_missing_values(self, for_validate=False):
super(BuyingController, self).set_missing_values(for_validate)
diff --git a/erpnext/manufacturing/report/process_loss_report/process_loss_report.py b/erpnext/manufacturing/report/process_loss_report/process_loss_report.py
index ce8f4f3..c3dd9cf 100644
--- a/erpnext/manufacturing/report/process_loss_report/process_loss_report.py
+++ b/erpnext/manufacturing/report/process_loss_report/process_loss_report.py
@@ -33,10 +33,9 @@
wo.name,
wo.status,
wo.production_item,
- wo.qty,
wo.produced_qty,
wo.process_loss_qty,
- (wo.produced_qty - wo.process_loss_qty).as_("actual_produced_qty"),
+ wo.qty.as_("qty_to_manufacture"),
Sum(se.total_incoming_value).as_("total_fg_value"),
Sum(se.total_outgoing_value).as_("total_rm_value"),
)
@@ -44,6 +43,7 @@
(wo.process_loss_qty > 0)
& (wo.company == filters.company)
& (se.docstatus == 1)
+ & (se.purpose == "Manufacture")
& (se.posting_date.between(filters.from_date, filters.to_date))
)
.groupby(se.work_order)
@@ -80,19 +80,29 @@
},
{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": "100"},
{
+ "label": _("Qty To Manufacture"),
+ "fieldname": "qty_to_manufacture",
+ "fieldtype": "Float",
+ "width": "150",
+ },
+ {
"label": _("Manufactured Qty"),
"fieldname": "produced_qty",
"fieldtype": "Float",
"width": "150",
},
- {"label": _("Loss Qty"), "fieldname": "process_loss_qty", "fieldtype": "Float", "width": "150"},
{
- "label": _("Actual Manufactured Qty"),
- "fieldname": "actual_produced_qty",
+ "label": _("Process Loss Qty"),
+ "fieldname": "process_loss_qty",
"fieldtype": "Float",
"width": "150",
},
- {"label": _("Loss Value"), "fieldname": "total_pl_value", "fieldtype": "Float", "width": "150"},
+ {
+ "label": _("Process Loss Value"),
+ "fieldname": "total_pl_value",
+ "fieldtype": "Float",
+ "width": "150",
+ },
{"label": _("FG Value"), "fieldname": "total_fg_value", "fieldtype": "Float", "width": "150"},
{
"label": _("Raw Material Value"),
@@ -105,5 +115,5 @@
def update_data_with_total_pl_value(data: Data) -> None:
for row in data:
- value_per_unit_fg = row["total_fg_value"] / row["actual_produced_qty"]
+ value_per_unit_fg = row["total_fg_value"] / row["qty_to_manufacture"]
row["total_pl_value"] = row["process_loss_qty"] * value_per_unit_fg
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 953a893..9335567 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -2340,14 +2340,11 @@
frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() {
if (in_list(["Sales Invoice", "Delivery Note"], frm.doc.doctype)) {
- item_row.outward = frm.doc.is_return ? 0 : 1;
+ item_row.type_of_transaction = frm.doc.is_return ? "Inward" : "Outward";
} else {
- item_row.outward = frm.doc.is_return ? 1 : 0;
+ item_row.type_of_transaction = frm.doc.is_return ? "Outward" : "Inward";
}
- item_row.type_of_transaction = (item_row.outward === 1
- ? "Outward":"Inward");
-
new erpnext.SerialBatchPackageSelector(frm, item_row, (r) => {
if (r) {
let update_values = {
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 58aa8d7..a859a67 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -350,6 +350,38 @@
}
},
+
+ pick_serial_and_batch_bundle(frm, cdt, cdn, type_of_transaction, warehouse_field) {
+ let item_row = frappe.get_doc(cdt, cdn);
+ item_row.type_of_transaction = type_of_transaction;
+
+ frappe.db.get_value("Item", item_row.item_code, ["has_batch_no", "has_serial_no"])
+ .then((r) => {
+ item_row.has_batch_no = r.message.has_batch_no;
+ item_row.has_serial_no = r.message.has_serial_no;
+
+ frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() {
+ new erpnext.SerialBatchPackageSelector(frm, item_row, (r) => {
+ if (r) {
+ let update_values = {
+ "serial_and_batch_bundle": r.name,
+ "qty": Math.abs(r.total_qty)
+ }
+
+ if (!warehouse_field) {
+ warehouse_field = "warehouse";
+ }
+
+ if (r.warehouse) {
+ update_values[warehouse_field] = r.warehouse;
+ }
+
+ frappe.model.set_value(item_row.doctype, item_row.name, update_values);
+ }
+ });
+ });
+ });
+ }
});
erpnext.utils.select_alternate_items = function(opts) {
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index f9eec2a..27a7968 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -26,7 +26,7 @@
title: this.item?.title || primary_label,
fields: this.get_dialog_fields(),
primary_action_label: primary_label,
- primary_action: () => this.update_ledgers(),
+ primary_action: () => this.update_bundle_entries(),
secondary_action_label: __('Edit Full Form'),
secondary_action: () => this.edit_full_form(),
});
@@ -36,7 +36,7 @@
}
get_serial_no_filters() {
- let warehouse = this.item?.outward ?
+ let warehouse = this.item?.type_of_transaction === "Outward" ?
(this.item.warehouse || this.item.s_warehouse) : "";
return {
@@ -121,7 +121,7 @@
});
}
- if (this.item?.outward) {
+ if (this.item?.type_of_transaction === "Outward") {
fields = [...this.get_filter_fields(), ...fields];
} else {
fields = [...fields, ...this.get_attach_field()];
@@ -267,7 +267,7 @@
label: __('Batch No'),
in_list_view: 1,
get_query: () => {
- if (!this.item.outward) {
+ if (this.item.type_of_transaction !== "Outward") {
return {
filters: {
'item': this.item.item_code,
@@ -356,7 +356,7 @@
this.dialog.fields_dict.entries.grid.refresh();
}
- update_ledgers() {
+ update_bundle_entries() {
let entries = this.dialog.get_values().entries;
let warehouse = this.dialog.get_value('warehouse');
@@ -390,7 +390,7 @@
_new.warehouse = this.get_warehouse();
_new.has_serial_no = this.item.has_serial_no;
_new.has_batch_no = this.item.has_batch_no;
- _new.type_of_transaction = this.get_type_of_transaction();
+ _new.type_of_transaction = this.item.type_of_transaction;
_new.company = this.frm.doc.company;
_new.voucher_type = this.frm.doc.doctype;
bundle_id = _new.name;
@@ -401,15 +401,11 @@
}
get_warehouse() {
- return (this.item?.outward ?
+ return (this.item?.type_of_transaction === "Outward" ?
(this.item.warehouse || this.item.s_warehouse)
: (this.item.warehouse || this.item.t_warehouse));
}
- get_type_of_transaction() {
- return (this.item?.outward ? 'Outward' : 'Inward');
- }
-
render_data() {
if (!this.frm.is_new() && this.bundle) {
frappe.call({
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index e6b2b3b..b6e567c 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -379,7 +379,6 @@
frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", () => {
let frm = this.events.get_frm();
let item_row = this.item_row;
- item_row.outward = 1;
item_row.type_of_transaction = "Outward";
new erpnext.SerialBatchPackageSelector(frm, item_row, (r) => {
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 98ad8a7..87c0fae 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -317,7 +317,6 @@
item.has_serial_no = r.message.has_serial_no;
item.has_batch_no = r.message.has_batch_no;
item.type_of_transaction = item.qty > 0 ? "Outward":"Inward";
- item.outward = item.qty > 0 ? 1 : 0;
item.title = item.has_serial_no ?
__("Select Serial No") : __("Select Batch No");
diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py
index 0237731..2f1270e 100644
--- a/erpnext/stock/deprecated_serial_batch.py
+++ b/erpnext/stock/deprecated_serial_batch.py
@@ -125,6 +125,9 @@
if batch_no not in self.non_batchwise_valuation_batches:
continue
+ if not self.non_batchwise_balance_qty:
+ continue
+
self.batch_avg_rate[batch_no] = (
self.non_batchwise_balance_value / self.non_batchwise_balance_qty
)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index f7fb633..3d497ac 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -1114,7 +1114,7 @@
if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) {
item.has_serial_no = r.message.has_serial_no;
item.has_batch_no = r.message.has_batch_no;
- item.outward = item.s_warehouse ? 1 : 0;
+ item.type_of_transaction = item.s_warehouse ? "Outward" : "Inward";
frappe.require(path, function() {
new erpnext.SerialBatchPackageSelector(
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 56cc21c..6afbf01 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -286,6 +286,10 @@
}
},
+ add_serial_batch_bundle(frm, cdt, cdn) {
+ erpnext.utils.pick_serial_and_batch_bundle(frm, cdt, cdn, "Inward");
+ }
+
});
erpnext.stock.StockReconciliation = class StockReconciliation extends erpnext.stock.StockController {
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 4004c00..6ea27ed 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -193,7 +193,13 @@
def _changed(item):
if item.current_serial_and_batch_bundle:
- self.calculate_difference_amount(item, frappe._dict({}))
+ bundle_data = frappe.get_all(
+ "Serial and Batch Bundle",
+ filters={"name": item.current_serial_and_batch_bundle},
+ fields=["total_qty as qty", "avg_rate as rate"],
+ )[0]
+
+ self.calculate_difference_amount(item, bundle_data)
return True
item_dict = get_stock_balance_for(
@@ -446,16 +452,17 @@
sl_entries.append(args)
- args = self.get_sle_for_items(row)
- args.update(
- {
- "actual_qty": row.qty,
- "incoming_rate": row.valuation_rate,
- "serial_and_batch_bundle": row.serial_and_batch_bundle,
- }
- )
+ if row.qty != 0:
+ args = self.get_sle_for_items(row)
+ args.update(
+ {
+ "actual_qty": row.qty,
+ "incoming_rate": row.valuation_rate,
+ "serial_and_batch_bundle": row.serial_and_batch_bundle,
+ }
+ )
- sl_entries.append(args)
+ sl_entries.append(args)
def update_valuation_rate_for_serial_no(self):
for d in self.items:
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
index 8738f4a..62d6e4c 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -103,7 +103,8 @@
{
"fieldname": "serial_no",
"fieldtype": "Long Text",
- "label": "Serial No"
+ "label": "Serial No",
+ "read_only": 1
},
{
"fieldname": "column_break_11",
@@ -213,7 +214,7 @@
],
"istable": 1,
"links": [],
- "modified": "2023-05-27 17:35:31.026852",
+ "modified": "2023-06-15 11:45:55.808942",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reconciliation Item",
diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py
index a75c3b0..2c18f99 100644
--- a/erpnext/stock/serial_batch_bundle.py
+++ b/erpnext/stock/serial_batch_bundle.py
@@ -5,7 +5,7 @@
from frappe import _, bold
from frappe.model.naming import make_autoname
from frappe.query_builder.functions import CombineDatetime, Sum
-from frappe.utils import cint, flt, now, nowtime, today
+from frappe.utils import cint, flt, get_link_to_form, now, nowtime, today
from erpnext.stock.deprecated_serial_batch import (
DeprecatedBatchNoValuation,
@@ -79,9 +79,24 @@
self.set_serial_and_batch_bundle(sn_doc)
def validate_actual_qty(self, sn_doc):
+ link = get_link_to_form("Serial and Batch Bundle", sn_doc.name)
+
+ condition = {
+ "Inward": self.sle.actual_qty > 0,
+ "Outward": self.sle.actual_qty < 0,
+ }.get(sn_doc.type_of_transaction)
+
+ if not condition:
+ correct_type = "Inward"
+ if sn_doc.type_of_transaction == "Inward":
+ correct_type = "Outward"
+
+ msg = f"The type of transaction of Serial and Batch Bundle {link} is {bold(sn_doc.type_of_transaction)} but as per the Actual Qty {self.sle.actual_qty} for the item {bold(self.sle.item_code)} in the {self.sle.voucher_type} {self.sle.voucher_no} the type of transaction should be {bold(correct_type)}"
+ frappe.throw(_(msg), title=_("Incorrect Type of Transaction"))
+
precision = sn_doc.precision("total_qty")
if flt(sn_doc.total_qty, precision) != flt(self.sle.actual_qty, precision):
- msg = f"Total qty {flt(sn_doc.total_qty, precision)} of Serial and Batch Bundle {sn_doc.name} is not equal to Actual Qty {flt(self.sle.actual_qty, precision)} in the {self.sle.voucher_type} {self.sle.voucher_no}"
+ msg = f"Total qty {flt(sn_doc.total_qty, precision)} of Serial and Batch Bundle {link} is not equal to Actual Qty {flt(self.sle.actual_qty, precision)} in the {self.sle.voucher_type} {self.sle.voucher_no}"
frappe.throw(_(msg))
def validate_item(self):