Merge branch 'develop' of github.com:aerele/erpnext into pricing_rule

This commit is contained in:
Bhavan23
2024-09-05 10:44:28 +05:30
41 changed files with 9593 additions and 17380 deletions

View File

@@ -658,7 +658,7 @@ frappe.ui.form.on("Payment Entry", {
frm.set_value("source_exchange_rate", 1);
} else if (frm.doc.paid_from) {
if (["Internal Transfer", "Pay"].includes(frm.doc.payment_type)) {
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
let company_currency = frappe.get_doc(":Company", frm.doc.company)?.default_currency;
frappe.call({
method: "erpnext.setup.utils.get_exchange_rate",
args: {

View File

@@ -1267,7 +1267,11 @@ class PurchaseInvoice(BuyingController):
def update_gross_purchase_amount_for_linked_assets(self, item):
assets = frappe.db.get_all(
"Asset",
filters={"purchase_invoice": self.name, "item_code": item.item_code},
filters={
"purchase_invoice": self.name,
"item_code": item.item_code,
"purchase_invoice_item": ("in", [item.name, ""]),
},
fields=["name", "asset_quantity"],
)
for asset in assets:

View File

@@ -2081,7 +2081,7 @@ def make_delivery_note(source_name, target_doc=None):
"postprocess": update_item,
"condition": lambda doc: doc.delivered_by_supplier != 1,
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True},
"Sales Team": {
"doctype": "Sales Team",
"field_map": {"incentives": "incentives"},

View File

@@ -670,6 +670,11 @@ frappe.ui.form.on("Asset", {
if (item.asset_location) {
frm.set_value("location", item.asset_location);
}
if (doctype === "Purchase Receipt") {
frm.set_value("purchase_receipt_item", item.name);
} else if (doctype === "Purchase Invoice") {
frm.set_value("purchase_invoice_item", item.name);
}
});
},

View File

@@ -33,14 +33,16 @@
"dimension_col_break",
"purchase_details_section",
"purchase_receipt",
"purchase_receipt_item",
"purchase_invoice",
"purchase_invoice_item",
"purchase_date",
"available_for_use_date",
"total_asset_cost",
"additional_asset_cost",
"column_break_23",
"gross_purchase_amount",
"asset_quantity",
"purchase_date",
"additional_asset_cost",
"total_asset_cost",
"section_break_23",
"calculate_depreciation",
"column_break_33",
@@ -536,6 +538,20 @@
"fieldname": "opening_number_of_booked_depreciations",
"fieldtype": "Int",
"label": "Opening Number of Booked Depreciations"
},
{
"fieldname": "purchase_receipt_item",
"fieldtype": "Link",
"hidden": 1,
"label": "Purchase Receipt Item",
"options": "Purchase Receipt Item"
},
{
"fieldname": "purchase_invoice_item",
"fieldtype": "Link",
"hidden": 1,
"label": "Purchase Invoice Item",
"options": "Purchase Invoice Item"
}
],
"idx": 72,
@@ -579,7 +595,7 @@
"link_fieldname": "target_asset"
}
],
"modified": "2024-08-01 16:39:09.340973",
"modified": "2024-08-26 23:28:29.095139",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",

View File

@@ -94,7 +94,9 @@ class Asset(AccountsController):
purchase_amount: DF.Currency
purchase_date: DF.Date | None
purchase_invoice: DF.Link | None
purchase_invoice_item: DF.Link | None
purchase_receipt: DF.Link | None
purchase_receipt_item: DF.Link | None
split_from: DF.Link | None
status: DF.Literal[
"Draft",

View File

@@ -31,6 +31,12 @@ class TestAssetCapitalization(unittest.TestCase):
def test_capitalization_with_perpetual_inventory(self):
company = "_Test Company with perpetual inventory"
set_depreciation_settings_in_company(company=company)
name = frappe.db.get_value(
"Asset Category Account",
filters={"parent": "Computers", "company_name": company},
fieldname=["name"],
)
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", "")
# Variables
consumed_asset_value = 100000
@@ -215,6 +221,12 @@ class TestAssetCapitalization(unittest.TestCase):
def test_capitalization_with_wip_composite_asset(self):
company = "_Test Company with perpetual inventory"
set_depreciation_settings_in_company(company=company)
name = frappe.db.get_value(
"Asset Category Account",
filters={"parent": "Computers", "company_name": company},
fieldname=["name"],
)
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", "")
stock_rate = 1000
stock_qty = 2

View File

@@ -5,7 +5,6 @@ frappe.provide("erpnext.accounts.dimensions");
frappe.ui.form.on("Asset Value Adjustment", {
setup: function (frm) {
frm.add_fetch("company", "cost_center", "cost_center");
frm.set_query("cost_center", function () {
return {
filters: {
@@ -22,6 +21,14 @@ frappe.ui.form.on("Asset Value Adjustment", {
},
};
});
frm.set_query("difference_account", function () {
return {
filters: {
company: frm.doc.company,
is_group: 0,
},
};
});
},
onload: function (frm) {
@@ -37,7 +44,7 @@ frappe.ui.form.on("Asset Value Adjustment", {
},
asset: function (frm) {
frm.trigger("set_current_asset_value");
frm.trigger("set_acc_dimension");
},
finance_book: function (frm) {
@@ -60,4 +67,15 @@ frappe.ui.form.on("Asset Value Adjustment", {
});
}
},
set_acc_dimension: function (frm) {
if (frm.doc.asset) {
frm.call({
method: "erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment.get_value_of_accounting_dimensions",
args: {
asset_name: frm.doc.asset,
},
});
}
},
});

View File

@@ -17,6 +17,7 @@
"new_asset_value",
"column_break_11",
"difference_amount",
"difference_account",
"journal_entry",
"accounting_dimensions_section",
"cost_center",
@@ -54,6 +55,7 @@
"fieldtype": "Link",
"label": "Journal Entry",
"options": "Journal Entry",
"no_copy": 1,
"read_only": 1
},
{
@@ -79,6 +81,7 @@
"fieldtype": "Currency",
"in_list_view": 1,
"label": "New Asset Value",
"no_copy": 1,
"reqd": 1
},
{
@@ -120,12 +123,20 @@
{
"fieldname": "column_break_11",
"fieldtype": "Column Break"
},
{
"fieldname": "difference_account",
"fieldtype": "Link",
"label": "Difference Account",
"no_copy": 1,
"options": "Account",
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2024-03-27 13:06:36.004049",
"modified": "2024-08-13 16:21:18.639208",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Value Adjustment",

View File

@@ -34,6 +34,7 @@ class AssetValueAdjustment(Document):
cost_center: DF.Link | None
current_asset_value: DF.Currency
date: DF.Date
difference_account: DF.Link
difference_amount: DF.Currency
finance_book: DF.Link | None
journal_entry: DF.Link | None
@@ -47,6 +48,7 @@ class AssetValueAdjustment(Document):
def on_submit(self):
self.make_depreciation_entry()
self.set_value_after_depreciation()
self.update_asset(self.new_asset_value)
add_asset_activity(
self.asset,
@@ -76,7 +78,10 @@ class AssetValueAdjustment(Document):
)
def set_difference_amount(self):
self.difference_amount = flt(self.current_asset_value - self.new_asset_value)
self.difference_amount = flt(self.new_asset_value - self.current_asset_value)
def set_value_after_depreciation(self):
frappe.db.set_value("Asset", self.asset, "value_after_depreciation", self.new_asset_value)
def set_current_asset_value(self):
if not self.current_asset_value and self.asset:
@@ -85,7 +90,7 @@ class AssetValueAdjustment(Document):
def make_depreciation_entry(self):
asset = frappe.get_doc("Asset", self.asset)
(
_,
fixed_asset_account,
accumulated_depreciation_account,
depreciation_expense_account,
) = get_depreciation_accounts(asset.asset_category, asset.company)
@@ -95,28 +100,41 @@ class AssetValueAdjustment(Document):
)
je = frappe.new_doc("Journal Entry")
je.voucher_type = "Depreciation Entry"
je.voucher_type = "Journal Entry"
je.naming_series = depreciation_series
je.posting_date = self.date
je.company = self.company
je.remark = f"Depreciation Entry against {self.asset} worth {self.difference_amount}"
je.remark = f"Revaluation Entry against {self.asset} worth {self.difference_amount}"
je.finance_book = self.finance_book
credit_entry = {
"account": accumulated_depreciation_account,
"credit_in_account_currency": self.difference_amount,
"cost_center": depreciation_cost_center or self.cost_center,
entry_template = {
"cost_center": self.cost_center or depreciation_cost_center,
"reference_type": "Asset",
"reference_name": self.asset,
"reference_name": asset.name,
}
debit_entry = {
"account": depreciation_expense_account,
"debit_in_account_currency": self.difference_amount,
"cost_center": depreciation_cost_center or self.cost_center,
"reference_type": "Asset",
"reference_name": self.asset,
}
if self.difference_amount < 0:
credit_entry = {
"account": fixed_asset_account,
"credit_in_account_currency": -self.difference_amount,
**entry_template,
}
debit_entry = {
"account": self.difference_account,
"debit_in_account_currency": -self.difference_amount,
**entry_template,
}
elif self.difference_amount > 0:
credit_entry = {
"account": self.difference_account,
"credit_in_account_currency": self.difference_amount,
**entry_template,
}
debit_entry = {
"account": fixed_asset_account,
"debit_in_account_currency": self.difference_amount,
**entry_template,
}
accounting_dimensions = get_checks_for_pl_and_bs_accounts()
@@ -179,3 +197,9 @@ class AssetValueAdjustment(Document):
)
asset.flags.ignore_validate_update_after_submit = True
asset.save()
@frappe.whitelist()
def get_value_of_accounting_dimensions(asset_name):
dimension_fields = [*frappe.get_list("Accounting Dimension", pluck="fieldname"), "cost_center"]
return frappe.db.get_value("Asset", asset_name, fieldname=dimension_fields, as_dict=True)

View File

@@ -93,8 +93,8 @@ class TestAssetValueAdjustment(unittest.TestCase):
self.assertEqual(first_asset_depr_schedule.status, "Cancelled")
expected_gle = (
("_Test Accumulated Depreciations - _TC", 0.0, 4625.29),
("_Test Depreciations - _TC", 4625.29, 0.0),
("_Test Difference Account - _TC", 4625.29, 0.0),
("_Test Fixed Asset - _TC", 0.0, 4625.29),
)
gle = frappe.db.sql(
@@ -177,8 +177,8 @@ class TestAssetValueAdjustment(unittest.TestCase):
# Test gl entry creted from asset value adjustemnet
expected_gle = (
("_Test Accumulated Depreciations - _TC", 0.0, 5625.29),
("_Test Depreciations - _TC", 5625.29, 0.0),
("_Test Difference Account - _TC", 5625.29, 0.0),
("_Test Fixed Asset - _TC", 0.0, 5625.29),
)
gle = frappe.db.sql(
@@ -259,6 +259,39 @@ class TestAssetValueAdjustment(unittest.TestCase):
self.assertEqual(schedules, expected_schedules)
def test_difference_amount(self):
pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=120000.0, location="Test Location")
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
asset_doc = frappe.get_doc("Asset", asset_name)
asset_doc.calculate_depreciation = 1
asset_doc.available_for_use_date = "2023-01-15"
asset_doc.purchase_date = "2023-01-15"
asset_doc.append(
"finance_books",
{
"expected_value_after_useful_life": 200,
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 12,
"frequency_of_depreciation": 1,
"depreciation_start_date": "2023-01-31",
},
)
asset_doc.submit()
adj_doc = make_asset_value_adjustment(
asset=asset_doc.name,
current_asset_value=54000,
new_asset_value=50000.0,
date="2023-08-21",
)
adj_doc.submit()
difference_amount = adj_doc.new_asset_value - adj_doc.current_asset_value
self.assertEqual(difference_amount, -4000)
asset_doc.load_from_db()
self.assertEqual(asset_doc.value_after_depreciation, 50000.0)
def make_asset_value_adjustment(**args):
args = frappe._dict(args)
@@ -272,7 +305,22 @@ def make_asset_value_adjustment(**args):
"new_asset_value": args.new_asset_value,
"current_asset_value": args.current_asset_value,
"cost_center": args.cost_center or "Main - _TC",
"difference_account": make_difference_account(),
}
).insert()
return doc
def make_difference_account(**args):
account = "_Test Difference Account - _TC"
if not frappe.db.exists("Account", account):
acc = frappe.new_doc("Account")
acc.account_name = "_Test Difference Account"
acc.parent_account = "Direct Income - _TC"
acc.company = "_Test Company"
acc.is_group = 0
acc.insert()
return acc.name
else:
return account

View File

@@ -735,7 +735,7 @@ def make_purchase_receipt(source_name, target_doc=None):
"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty)
and doc.delivered_by_supplier != 1,
},
"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "add_if_empty": True},
"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "reset_value": True},
},
target_doc,
set_missing_values,
@@ -811,7 +811,7 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions
"postprocess": update_item,
"condition": lambda doc: (doc.base_amount == 0 or abs(doc.billed_amt) < abs(doc.amount)),
},
"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "add_if_empty": True},
"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "reset_value": True},
}
doc = get_mapped_doc(

View File

@@ -824,6 +824,8 @@ class BuyingController(SubcontractingController):
"asset_quantity": asset_quantity,
"purchase_receipt": self.name if self.doctype == "Purchase Receipt" else None,
"purchase_invoice": self.name if self.doctype == "Purchase Invoice" else None,
"purchase_receipt_item": row.name if self.doctype == "Purchase Receipt" else None,
"purchase_invoice_item": row.name if self.doctype == "Purchase Invoice" else None,
}
)

View File

@@ -20,7 +20,7 @@ add_to_apps_screen = [
}
]
develop_version = "14.x.x-develop"
develop_version = "15.x.x-develop"
app_include_js = "erpnext.bundle.js"
app_include_css = "erpnext.bundle.css"
@@ -547,6 +547,8 @@ accounting_dimension_doctypes = [
"Payment Reconciliation",
"Payment Reconciliation Allocation",
"Payment Request",
"Asset Movement Item",
"Asset Depreciation Schedule",
]
get_matching_queries = (

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,18 +5,17 @@
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
"bom_and_work_order_tab",
"raw_materials_consumption_section",
"material_consumption",
"get_rm_cost_from_consumption_entry",
"column_break_3",
"backflush_raw_materials_based_on",
"capacity_planning",
"disable_capacity_planning",
"allow_overtime",
"allow_production_on_holidays",
"column_break_5",
"capacity_planning_for_days",
"mins_between_operations",
"validate_components_quantities_per_bom",
"bom_section",
"update_bom_costs_automatically",
"column_break_lhyt",
"manufacture_sub_assembly_in_operation",
"section_break_6",
"default_wip_warehouse",
"default_fg_warehouse",
@@ -30,8 +29,14 @@
"add_corrective_operation_cost_in_finished_good_valuation",
"column_break_24",
"job_card_excess_transfer",
"capacity_planning",
"disable_capacity_planning",
"allow_overtime",
"allow_production_on_holidays",
"column_break_5",
"capacity_planning_for_days",
"mins_between_operations",
"other_settings_section",
"update_bom_costs_automatically",
"set_op_cost_and_scrape_from_sub_assemblies",
"column_break_23",
"make_serial_no_batch_from_work_order"
@@ -149,7 +154,7 @@
{
"fieldname": "raw_materials_consumption_section",
"fieldtype": "Section Break",
"label": "Raw Materials Consumption"
"label": "Raw Materials Consumption "
},
{
"fieldname": "column_break_16",
@@ -183,8 +188,8 @@
},
{
"fieldname": "job_card_section",
"fieldtype": "Section Break",
"label": "Job Card"
"fieldtype": "Tab Break",
"label": "Job Card and Capacity Planning"
},
{
"fieldname": "column_break_24",
@@ -210,13 +215,41 @@
"fieldname": "get_rm_cost_from_consumption_entry",
"fieldtype": "Check",
"label": "Get Raw Materials Cost from Consumption Entry"
},
{
"fieldname": "bom_and_work_order_tab",
"fieldtype": "Tab Break",
"label": "BOM and Production"
},
{
"fieldname": "bom_section",
"fieldtype": "Section Break",
"label": "BOM"
},
{
"fieldname": "column_break_lhyt",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "If enabled then system will manufacture Sub-assembly against the Job Card (operation).",
"fieldname": "manufacture_sub_assembly_in_operation",
"fieldtype": "Check",
"label": "Manufacture Sub-assembly in Operation"
},
{
"default": "0",
"depends_on": "eval:doc.backflush_raw_materials_based_on == \"BOM\"",
"fieldname": "validate_components_quantities_per_bom",
"fieldtype": "Check",
"label": "Validate Components Quantities Per BOM"
}
],
"icon": "icon-wrench",
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2024-03-27 13:10:04.700433",
"modified": "2024-09-02 12:12:03.132567",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing Settings",

View File

@@ -29,15 +29,22 @@ class ManufacturingSettings(Document):
get_rm_cost_from_consumption_entry: DF.Check
job_card_excess_transfer: DF.Check
make_serial_no_batch_from_work_order: DF.Check
manufacture_sub_assembly_in_operation: DF.Check
material_consumption: DF.Check
mins_between_operations: DF.Int
overproduction_percentage_for_sales_order: DF.Percent
overproduction_percentage_for_work_order: DF.Percent
set_op_cost_and_scrape_from_sub_assemblies: DF.Check
update_bom_costs_automatically: DF.Check
validate_components_quantities_per_bom: DF.Check
# end: auto-generated types
pass
def before_save(self):
self.reset_values()
def reset_values(self):
if self.backflush_raw_materials_based_on != "BOM" and self.validate_components_quantities_per_bom:
self.validate_components_quantities_per_bom = 0
def get_mins_between_operations():

View File

@@ -2103,6 +2103,59 @@ class TestWorkOrder(FrappeTestCase):
stock_entry.submit()
def test_components_qty_for_bom_based_manufacture_entry(self):
frappe.db.set_single_value("Manufacturing Settings", "backflush_raw_materials_based_on", "BOM")
frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 1)
fg_item = "Test FG Item For Component Validation"
source_warehouse = "Stores - _TC"
raw_materials = ["Test Component Validation RM Item 1", "Test Component Validation RM Item 2"]
make_item(fg_item, {"is_stock_item": 1})
for item in raw_materials:
make_item(item, {"is_stock_item": 1})
test_stock_entry.make_stock_entry(
item_code=item,
target=source_warehouse,
qty=10,
basic_rate=100,
)
make_bom(item=fg_item, source_warehouse=source_warehouse, raw_materials=raw_materials)
wo = make_wo_order_test_record(
item=fg_item,
qty=10,
source_warehouse=source_warehouse,
)
transfer_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10))
transfer_entry.save()
for row in transfer_entry.items:
row.qty = 5
self.assertRaises(frappe.ValidationError, transfer_entry.save)
transfer_entry.reload()
for row in transfer_entry.items:
self.assertEqual(row.qty, 10)
transfer_entry.submit()
manufacture_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 10))
manufacture_entry.save()
for row in manufacture_entry.items:
if not row.s_warehouse:
continue
row.qty = 5
self.assertRaises(frappe.ValidationError, manufacture_entry.save)
manufacture_entry.reload()
manufacture_entry.submit()
frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 0)
def make_operation(**kwargs):
kwargs = frappe._dict(kwargs)

View File

@@ -378,3 +378,5 @@ erpnext.patches.v15_0.do_not_use_batchwise_valuation
erpnext.patches.v15_0.drop_index_posting_datetime_from_sle
erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1
erpnext.patches.v15_0.set_standard_stock_entry_type
erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment
erpnext.patches.v15_0.link_purchase_item_to_asset_doc

View File

@@ -0,0 +1,74 @@
import frappe
def execute():
if frappe.db.has_column("Asset", "purchase_invoice_item") and frappe.db.has_column(
"Asset", "purchase_receipt_item"
):
# Get all assets with their related Purchase Invoice and Purchase Receipt
assets = frappe.get_all(
"Asset",
filters={"docstatus": 0},
fields=[
"name",
"item_code",
"purchase_invoice",
"purchase_receipt",
"gross_purchase_amount",
"asset_quantity",
"purchase_invoice_item",
"purchase_receipt_item",
],
)
for asset in assets:
# Get Purchase Invoice Items
if asset.purchase_invoice and not asset.purchase_invoice_item:
purchase_invoice_item = get_linked_item(
"Purchase Invoice Item",
asset.purchase_invoice,
asset.item_code,
asset.gross_purchase_amount,
asset.asset_quantity,
)
frappe.db.set_value("Asset", asset.name, "purchase_invoice_item", purchase_invoice_item)
# Get Purchase Receipt Items
if asset.purchase_receipt and not asset.purchase_receipt_item:
purchase_receipt_item = get_linked_item(
"Purchase Receipt Item",
asset.purchase_receipt,
asset.item_code,
asset.gross_purchase_amount,
asset.asset_quantity,
)
frappe.db.set_value("Asset", asset.name, "purchase_receipt_item", purchase_receipt_item)
def get_linked_item(doctype, parent, item_code, amount, quantity):
items = frappe.get_all(
doctype,
filters={
"parenttype": doctype.replace(" Item", ""),
"parent": parent,
"item_code": item_code,
},
fields=["name", "rate", "amount", "qty", "landed_cost_voucher_amount"],
)
if len(items) == 1:
# If only one item exists, return it directly
return items[0].name
for item in items:
landed_cost = item.get("landed_cost_voucher_amount", 0)
# Check if the asset is grouped
if quantity > 1:
if item.amount + landed_cost == amount and item.qty == quantity:
return item.name
elif item.qty == quantity:
return item.name
else:
if item.rate + (landed_cost / item.qty) == amount:
return item.name
return items[0].name if items else None

View File

@@ -0,0 +1,10 @@
import frappe
def execute():
AssetValueAdjustment = frappe.qb.DocType("Asset Value Adjustment")
frappe.qb.update(AssetValueAdjustment).set(
AssetValueAdjustment.difference_amount,
AssetValueAdjustment.new_asset_value - AssetValueAdjustment.current_asset_value,
).where(AssetValueAdjustment.docstatus != 2).run()

View File

@@ -117,14 +117,17 @@ erpnext.financial_statements = {
erpnext.financial_statements.filters = get_filters();
let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
var filters = report.get_values();
frappe.model.with_doc("Fiscal Year", fiscal_year, function (r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
frappe.query_report.set_filter_value({
period_start_date: fy.year_start_date,
period_end_date: fy.year_end_date,
if (!filters.period_start_date || !filters.period_end_date) {
frappe.model.with_doc("Fiscal Year", fiscal_year, function (r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
frappe.query_report.set_filter_value({
period_start_date: fy.year_start_date,
period_end_date: fy.year_end_date,
});
});
});
}
if (report.page) {
const views_menu = report.page.add_custom_button_group(__("Financial Statements"));

View File

@@ -20,7 +20,11 @@ $.extend(erpnext, {
},
toggle_naming_series: function () {
if (cur_frm && cur_frm.fields_dict.naming_series) {
if (
cur_frm &&
cur_frm.fields_dict.naming_series &&
cur_frm.meta.naming_rule == 'By "Naming Series" field'
) {
cur_frm.toggle_display("naming_series", cur_frm.doc.__islocal ? true : false);
}
},

View File

@@ -368,8 +368,28 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
];
}
get_batch_qty(batch_no, callback) {
let warehouse = this.item.s_warehouse || this.item.t_warehouse || this.item.warehouse;
frappe.call({
method: "erpnext.stock.doctype.batch.batch.get_batch_qty",
args: {
batch_no: batch_no,
warehouse: warehouse,
item_code: this.item.item_code,
posting_date: this.frm.doc.posting_date,
posting_time: this.frm.doc.posting_time,
},
callback: (r) => {
if (r.message) {
callback(flt(r.message));
}
},
});
}
get_dialog_table_fields() {
let fields = [];
let me = this;
if (this.item.has_serial_no) {
fields.push({
@@ -395,6 +415,15 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
fieldname: "batch_no",
label: __("Batch No"),
in_list_view: 1,
change() {
let doc = this.doc;
if (!doc.qty && me.item.type_of_transaction === "Outward") {
me.get_batch_qty(doc.batch_no, (qty) => {
doc.qty = qty;
this.grid.set_value("qty", qty, doc);
});
}
},
get_query: () => {
let is_inward = false;
if (

View File

@@ -426,7 +426,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
"postprocess": update_item,
"condition": can_map_row,
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True},
"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},
"Payment Schedule": {"doctype": "Payment Schedule", "add_if_empty": True},
},
@@ -491,7 +491,7 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
"postprocess": update_item,
"condition": lambda row: not row.is_alternative,
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True},
"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},
},
target_doc,

View File

@@ -936,7 +936,7 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None):
mapper = {
"Sales Order": {"doctype": "Delivery Note", "validation": {"docstatus": ["=", 1]}},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True},
"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},
}
@@ -1136,7 +1136,10 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
"condition": lambda doc: doc.qty
and (doc.base_amount == 0 or abs(doc.billed_amt) < abs(doc.amount)),
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
"Sales Taxes and Charges": {
"doctype": "Sales Taxes and Charges",
"reset_value": True,
},
"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},
},
target_doc,

View File

@@ -20,7 +20,6 @@ frappe.ui.form.on("Company", {
},
setup: function (frm) {
frm.__rename_queue = "long";
erpnext.company.setup_queries(frm);
frm.set_query("parent_company", function () {
return {
@@ -81,6 +80,8 @@ frappe.ui.form.on("Company", {
},
refresh: function (frm) {
erpnext.company.setup_queries(frm);
frm.toggle_display("address_html", !frm.is_new());
if (!frm.is_new()) {

View File

@@ -1048,7 +1048,7 @@ def make_sales_invoice(source_name, target_doc=None, args=None):
},
"Sales Taxes and Charges": {
"doctype": "Sales Taxes and Charges",
"add_if_empty": True,
"reset_value": True,
"ignore": args.get("merge_taxes") if args else 0,
},
"Sales Team": {

View File

@@ -828,7 +828,11 @@ class PurchaseReceipt(BuyingController):
def update_assets(self, item, valuation_rate):
assets = frappe.db.get_all(
"Asset",
filters={"purchase_receipt": self.name, "item_code": item.item_code},
filters={
"purchase_receipt": self.name,
"item_code": item.item_code,
"purchase_receipt_item": ("in", [item.name, ""]),
},
fields=["name", "asset_quantity"],
)
@@ -1201,7 +1205,7 @@ def make_purchase_invoice(source_name, target_doc=None, args=None):
},
"Purchase Taxes and Charges": {
"doctype": "Purchase Taxes and Charges",
"add_if_empty": True,
"reset_value": True,
"ignore": args.get("merge_taxes") if args else 0,
},
},

View File

@@ -235,6 +235,7 @@ class StockEntry(StockController):
self.validate_serialized_batch()
self.calculate_rate_and_amount()
self.validate_putaway_capacity()
self.validate_component_quantities()
if self.get("purpose") != "Manufacture":
# ignore scrap item wh difference and empty source/target wh
@@ -764,6 +765,34 @@ class StockEntry(StockController):
title=_("Insufficient Stock"),
)
def validate_component_quantities(self):
if self.purpose not in ["Manufacture", "Material Transfer for Manufacture"]:
return
if not frappe.db.get_single_value("Manufacturing Settings", "validate_components_quantities_per_bom"):
return
if not self.fg_completed_qty:
return
raw_materials = self.get_bom_raw_materials(self.fg_completed_qty)
precision = frappe.get_precision("Stock Entry Detail", "qty")
for row in self.items:
if not row.s_warehouse:
continue
if details := raw_materials.get(row.item_code):
if flt(details.get("qty"), precision) != flt(row.qty, precision):
frappe.throw(
_("For the item {0}, the quantity should be {1} according to the BOM {2}.").format(
frappe.bold(row.item_code),
flt(details.get("qty"), precision),
get_link_to_form("BOM", self.bom_no),
),
title=_("Incorrect Component Quantity"),
)
@frappe.whitelist()
def get_stock_and_rate(self):
"""

View File

@@ -820,14 +820,10 @@ def get_price_list_rate(args, item_doc, out=None):
if price_list_rate is None or frappe.db.get_single_value(
"Stock Settings", "update_existing_price_list_rate"
):
if args.get("is_internal_supplier") or args.get("is_internal_customer"):
return out
insert_item_price(args)
if args.price_list and args.rate:
insert_item_price(args)
if not price_list_rate:
return out
if price_list_rate is None:
return out
out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) / flt(args.conversion_rate)
@@ -848,6 +844,14 @@ def get_price_list_rate(args, item_doc, out=None):
def insert_item_price(args):
"""Insert Item Price if Price List and Price List Rate are specified and currency is the same"""
if (
not args.price_list
or not args.rate
or args.get("is_internal_supplier")
or args.get("is_internal_customer")
):
return
if frappe.db.get_value("Price List", args.price_list, "currency", cache=True) == args.currency and cint(
frappe.db.get_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing")
):

View File

@@ -1533,7 +1533,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc
operator = "<="
voucher_condition = f"and creation < '{creation}'"
sle = frappe.db.sql(
sle = frappe.db.sql( # nosemgrep
f"""
select *, posting_datetime as "timestamp"
from `tabStock Ledger Entry`
@@ -1630,6 +1630,7 @@ def get_stock_ledger_entries(
if extra_cond:
conditions += f"{extra_cond}"
# nosemgrep
return frappe.db.sql(
"""
select *, posting_datetime as "timestamp"
@@ -1745,7 +1746,7 @@ def get_valuation_rate(
return batch_obj.get_incoming_rate()
# Get valuation rate from last sle for the same item and warehouse
if last_valuation_rate := frappe.db.sql(
if last_valuation_rate := frappe.db.sql( # nosemgrep
"""select valuation_rate
from `tabStock Ledger Entry` force index (item_warehouse)
where
@@ -1825,7 +1826,7 @@ def update_qty_in_future_sle(args, allow_negative_stock=False):
detail = next_stock_reco_detail[0]
datetime_limit_condition = get_datetime_limit_condition(detail)
frappe.db.sql(
frappe.db.sql( # nosemgrep
f"""
update `tabStock Ledger Entry`
set qty_after_transaction = qty_after_transaction + {qty_shift}
@@ -1991,8 +1992,8 @@ def is_negative_with_precision(neg_sle, is_batch=False):
return qty_deficit < 0 and abs(qty_deficit) > 0.0001
def get_future_sle_with_negative_qty(args):
return frappe.db.sql(
def get_future_sle_with_negative_qty(sle_args):
return frappe.db.sql( # nosemgrep
"""
select
qty_after_transaction, posting_date, posting_time,
@@ -2008,13 +2009,13 @@ def get_future_sle_with_negative_qty(args):
order by posting_date asc, posting_time asc
limit 1
""",
args,
sle_args,
as_dict=1,
)
def get_future_sle_with_negative_batch_qty(args):
return frappe.db.sql(
def get_future_sle_with_negative_batch_qty(sle_args):
return frappe.db.sql( # nosemgrep
"""
with batch_ledger as (
select
@@ -2034,7 +2035,7 @@ def get_future_sle_with_negative_batch_qty(args):
and posting_datetime >= %(posting_datetime)s
limit 1
""",
args,
sle_args,
as_dict=1,
)