Merge pull request #37032 from HarryPaulo/auth-rule-based-on-item-group
feat: created "based on" Item Group to specify a different percentage…
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 2c2efc0..8a894e2 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -98,7 +98,6 @@
if self.difference_amount:
frappe.throw(_("Difference Amount must be zero"))
self.make_gl_entries()
- self.make_advance_gl_entries()
self.update_outstanding_amounts()
self.update_advance_paid()
self.update_payment_schedule()
@@ -152,7 +151,6 @@
)
super(PaymentEntry, self).on_cancel()
self.make_gl_entries(cancel=1)
- self.make_advance_gl_entries(cancel=1)
self.update_outstanding_amounts()
self.update_advance_paid()
self.delink_advance_entry_references()
@@ -1060,6 +1058,8 @@
else:
self.make_exchange_gain_loss_journal()
+ self.make_advance_gl_entries(cancel=cancel)
+
def add_party_gl_entries(self, gl_entries):
if self.party_account:
if self.payment_type == "Receive":
@@ -1128,7 +1128,7 @@
if self.book_advance_payments_in_separate_party_account:
gl_entries = []
for d in self.get("references"):
- if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
+ if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Journal Entry"):
if not (against_voucher_type and against_voucher) or (
d.reference_doctype == against_voucher_type and d.reference_name == against_voucher
):
@@ -1164,6 +1164,13 @@
"voucher_detail_no": invoice.name,
}
+ posting_date = frappe.db.get_value(
+ invoice.reference_doctype, invoice.reference_name, "posting_date"
+ )
+
+ if getdate(posting_date) < getdate(self.posting_date):
+ posting_date = self.posting_date
+
dr_or_cr = "credit" if invoice.reference_doctype == "Sales Invoice" else "debit"
args_dict["account"] = invoice.account
args_dict[dr_or_cr] = invoice.allocated_amount
@@ -1172,6 +1179,7 @@
{
"against_voucher_type": invoice.reference_doctype,
"against_voucher": invoice.reference_name,
+ "posting_date": posting_date,
}
)
gle = self.get_gl_dict(
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index 7b7ce7a..d9f00be 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -24,7 +24,8 @@
filters: {
"company": this.frm.doc.company,
"is_group": 0,
- "account_type": frappe.boot.party_account_types[this.frm.doc.party_type]
+ "account_type": frappe.boot.party_account_types[this.frm.doc.party_type],
+ "root_type": this.frm.doc.party_type == 'Customer' ? "Asset" : "Liability"
}
};
});
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
index 6fdcf26..fd95c1f 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
@@ -15,9 +15,11 @@
},
"internal_links": {
"Sales Order": ["items", "sales_order"],
- "Delivery Note": ["items", "delivery_note"],
"Timesheet": ["timesheets", "time_sheet"],
},
+ "internal_and_external_links": {
+ "Delivery Note": ["items", "delivery_note"],
+ },
"transactions": [
{
"label": _("Payment"),
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index e05a4e7..ae24675 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -279,20 +279,19 @@
if match_conditions:
conditions.append(match_conditions)
- if filters.get("include_dimensions"):
- accounting_dimensions = get_accounting_dimensions(as_list=False)
+ accounting_dimensions = get_accounting_dimensions(as_list=False)
- if accounting_dimensions:
- for dimension in accounting_dimensions:
- if not dimension.disabled:
- if filters.get(dimension.fieldname):
- if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
- filters[dimension.fieldname] = get_dimension_with_children(
- dimension.document_type, filters.get(dimension.fieldname)
- )
- conditions.append("{0} in %({0})s".format(dimension.fieldname))
- else:
- conditions.append("{0} in %({0})s".format(dimension.fieldname))
+ if accounting_dimensions:
+ for dimension in accounting_dimensions:
+ if not dimension.disabled:
+ if filters.get(dimension.fieldname):
+ if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
+ filters[dimension.fieldname] = get_dimension_with_children(
+ dimension.document_type, filters.get(dimension.fieldname)
+ )
+ conditions.append("{0} in %({0})s".format(dimension.fieldname))
+ else:
+ conditions.append("{0} in %({0})s".format(dimension.fieldname))
return "and {}".format(" and ".join(conditions)) if conditions else ""
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index eed74a5..6a80f20 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -491,14 +491,13 @@
gl_map = doc.build_gl_map()
create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1)
- if voucher_type == "Payment Entry":
- doc.make_advance_gl_entries()
-
# Only update outstanding for newly linked vouchers
for entry in entries:
update_voucher_outstanding(
entry.against_voucher_type, entry.against_voucher, entry.account, entry.party_type, entry.party
)
+ if voucher_type == "Payment Entry":
+ doc.make_advance_gl_entries(entry.against_voucher_type, entry.against_voucher)
frappe.flags.ignore_party_validation = False
@@ -1163,8 +1162,13 @@
def parse_naming_series_variable(doc, variable):
if variable == "FY":
- date = doc.get("posting_date") or doc.get("transaction_date") or getdate()
- return get_fiscal_year(date=date, company=doc.get("company"))[0]
+ if doc:
+ date = doc.get("posting_date") or doc.get("transaction_date")
+ company = doc.get("company")
+ else:
+ date = getdate()
+ company = None
+ return get_fiscal_year(date=date, company=company)[0]
@frappe.whitelist()
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index b242108..5b5cc2b 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -1173,6 +1173,7 @@
"depends_on": "is_internal_supplier",
"fieldname": "set_from_warehouse",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Set From Warehouse",
"options": "Warehouse"
},
@@ -1273,7 +1274,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2023-06-03 16:19:45.710444",
+ "modified": "2023-09-13 16:21:07.361700",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 414f086..f79b622 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -878,6 +878,7 @@
"depends_on": "eval:parent.is_internal_supplier",
"fieldname": "from_warehouse",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "From Warehouse",
"options": "Warehouse"
},
@@ -902,7 +903,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2023-08-17 10:17:40.893393",
+ "modified": "2023-09-13 16:22:40.825092",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",
diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js
index 372ca56..08dc44c 100644
--- a/erpnext/buying/doctype/supplier/supplier.js
+++ b/erpnext/buying/doctype/supplier/supplier.js
@@ -12,6 +12,7 @@
return {
filters: {
'account_type': 'Payable',
+ 'root_type': 'Liability',
'company': d.company,
"is_group": 0
}
diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py
index 7be1d83..ee2ada3 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.py
+++ b/erpnext/buying/doctype/supplier/test_supplier.py
@@ -206,6 +206,7 @@
{
"doctype": "Supplier",
"supplier_name": args.supplier_name,
+ "default_currency": args.default_currency,
"supplier_group": args.supplier_group or "Services",
"supplier_type": args.supplier_type or "Company",
"tax_withholding_category": args.tax_withholding_category,
diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.js b/erpnext/manufacturing/doctype/bom_creator/bom_creator.js
index 01dc89b..0cf2b51 100644
--- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.js
+++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.js
@@ -134,6 +134,19 @@
frm.trigger("build_tree");
});
}
+
+ if (frm.doc.docstatus === 1 && frm.doc.status !== "Completed") {
+ frm.add_custom_button(__("Create Multi-level BOM"), () => {
+ frm.trigger("create_multi_level_bom");
+ });
+ }
+ },
+
+ create_multi_level_bom(frm) {
+ frm.call({
+ method: "enqueue_create_boms",
+ doc: frm.doc,
+ })
}
});
diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py
index 999d610..058caa3 100644
--- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py
+++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py
@@ -161,6 +161,7 @@
def on_submit(self):
self.enqueue_create_boms()
+ @frappe.whitelist()
def enqueue_create_boms(self):
frappe.enqueue(
self.create_boms,
@@ -220,6 +221,18 @@
frappe.msgprint(_("BOMs creation failed"))
def create_bom(self, row, production_item_wise_rm):
+ bom_creator_item = row.name if row.name != self.name else ""
+ if frappe.db.exists(
+ "BOM",
+ {
+ "bom_creator": self.name,
+ "item": row.item_code,
+ "bom_creator_item": bom_creator_item,
+ "docstatus": 1,
+ },
+ ):
+ return
+
bom = frappe.new_doc("BOM")
bom.update(
{
@@ -228,7 +241,7 @@
"quantity": row.qty,
"allow_alternative_item": 1,
"bom_creator": self.name,
- "bom_creator_item": row.name if row.name != self.name else "",
+ "bom_creator_item": bom_creator_item,
"rm_cost_as_per": "Manual",
}
)
diff --git a/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py b/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py
index d239d58..ee5886c 100644
--- a/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py
+++ b/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py
@@ -180,6 +180,73 @@
self.assertEqual(doc.raw_material_cost, fg_valuation_rate)
+ def test_make_boms_from_bom_creator(self):
+ final_product = "Bicycle Test"
+ make_item(
+ final_product,
+ {
+ "item_group": "Raw Material",
+ "stock_uom": "Nos",
+ },
+ )
+
+ doc = make_bom_creator(
+ name="Bicycle BOM Test",
+ company="_Test Company",
+ item_code=final_product,
+ qty=1,
+ rm_cosy_as_per="Valuation Rate",
+ currency="INR",
+ plc_conversion_rate=1,
+ conversion_rate=1,
+ )
+
+ add_item(
+ parent=doc.name,
+ fg_item=final_product,
+ fg_reference_id=doc.name,
+ item_code="Pedal Assembly",
+ qty=2,
+ )
+
+ doc.reload()
+ self.assertEqual(doc.items[0].is_expandable, 0)
+
+ add_sub_assembly(
+ convert_to_sub_assembly=1,
+ parent=doc.name,
+ fg_item=final_product,
+ fg_reference_id=doc.items[0].name,
+ bom_item={
+ "item_code": "Pedal Assembly",
+ "qty": 2,
+ "items": [
+ {
+ "item_code": "Pedal Body",
+ "qty": 2,
+ },
+ {
+ "item_code": "Pedal Axle",
+ "qty": 2,
+ },
+ ],
+ },
+ )
+
+ doc.reload()
+ self.assertEqual(doc.items[0].is_expandable, 1)
+
+ doc.submit()
+ doc.create_boms()
+ doc.reload()
+
+ data = frappe.get_all("BOM", filters={"bom_creator": doc.name, "docstatus": 1})
+ self.assertEqual(len(data), 2)
+
+ doc.create_boms()
+ data = frappe.get_all("BOM", filters={"bom_creator": doc.name, "docstatus": 1})
+ self.assertEqual(len(data), 2)
+
def create_items():
raw_materials = [
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
index 09bf1d8..d07bf0f 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
@@ -10,22 +10,25 @@
"warehouse",
"item_name",
"material_request_type",
- "actual_qty",
- "ordered_qty",
+ "quantity",
"required_bom_qty",
"column_break_4",
- "quantity",
+ "schedule_date",
"uom",
"conversion_factor",
- "projected_qty",
- "reserved_qty_for_production",
- "safety_stock",
"item_details",
"description",
"min_order_qty",
"section_break_8",
"sales_order",
- "requested_qty"
+ "bin_qty_section",
+ "actual_qty",
+ "requested_qty",
+ "reserved_qty_for_production",
+ "column_break_yhelv",
+ "ordered_qty",
+ "projected_qty",
+ "safety_stock"
],
"fields": [
{
@@ -65,7 +68,7 @@
"fieldtype": "Column Break"
},
{
- "columns": 1,
+ "columns": 2,
"fieldname": "quantity",
"fieldtype": "Float",
"in_list_view": 1,
@@ -80,12 +83,12 @@
"read_only": 1
},
{
- "columns": 2,
+ "columns": 1,
"default": "0",
"fieldname": "actual_qty",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Available Qty",
+ "label": "Qty In Stock",
"no_copy": 1,
"read_only": 1
},
@@ -176,11 +179,27 @@
"fieldtype": "Float",
"label": "Conversion Factor",
"read_only": 1
+ },
+ {
+ "columns": 1,
+ "fieldname": "schedule_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Required By"
+ },
+ {
+ "fieldname": "bin_qty_section",
+ "fieldtype": "Section Break",
+ "label": "BIN Qty"
+ },
+ {
+ "fieldname": "column_break_yhelv",
+ "fieldtype": "Column Break"
}
],
"istable": 1,
"links": [],
- "modified": "2023-05-03 12:43:29.895754",
+ "modified": "2023-09-12 12:09:08.358326",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Material Request Plan Item",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index b7a2489..7bde29f 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -726,7 +726,7 @@
# key for Sales Order:Material Request Type:Customer
key = "{}:{}:{}".format(item.sales_order, material_request_type, item_doc.customer or "")
- schedule_date = add_days(nowdate(), cint(item_doc.lead_time_days))
+ schedule_date = item.schedule_date or add_days(nowdate(), cint(item_doc.lead_time_days))
if not key in material_request_map:
# make a new MR for the combination
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index dccb903..6ed7506 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -2,7 +2,7 @@
# See license.txt
import frappe
from frappe.tests.utils import FrappeTestCase
-from frappe.utils import add_to_date, flt, now_datetime, nowdate
+from frappe.utils import add_to_date, flt, getdate, now_datetime, nowdate
from erpnext.controllers.item_variant import create_variant
from erpnext.manufacturing.doctype.production_plan.production_plan import (
@@ -58,6 +58,9 @@
pln = create_production_plan(item_code="Test Production Item 1")
self.assertTrue(len(pln.mr_items), 2)
+ for row in pln.mr_items:
+ row.schedule_date = add_to_date(nowdate(), days=10)
+
pln.make_material_request()
pln.reload()
self.assertTrue(pln.status, "Material Requested")
@@ -71,6 +74,13 @@
self.assertTrue(len(material_requests), 2)
+ for row in material_requests:
+ mr_schedule_date = getdate(frappe.db.get_value("Material Request", row[0], "schedule_date"))
+
+ expected_date = getdate(add_to_date(nowdate(), days=10))
+
+ self.assertEqual(mr_schedule_date, expected_date)
+
pln.make_work_order()
work_orders = frappe.get_all(
"Work Order", fields=["name"], filters={"production_plan": pln.name}, as_list=1
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index c2ed579..a66b549 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -87,6 +87,7 @@
issue=task_details.issue,
is_group=task_details.is_group,
color=task_details.color,
+ template_task=task_details.name,
)
).insert()
@@ -106,9 +107,13 @@
return date
def dependency_mapping(self, template_tasks, project_tasks):
- for template_task in template_tasks:
- project_task = list(filter(lambda x: x.subject == template_task.subject, project_tasks))[0]
- project_task = frappe.get_doc("Task", project_task.name)
+ for project_task in project_tasks:
+ if project_task.get("template_task"):
+ template_task = frappe.get_doc("Task", project_task.template_task)
+ else:
+ template_task = list(filter(lambda x: x.subject == project_task.subject, template_tasks))[0]
+ template_task = frappe.get_doc("Task", template_task.name)
+
self.check_depends_on_value(template_task, project_task, project_tasks)
self.check_for_parent_tasks(template_task, project_task, project_tasks)
@@ -120,6 +125,7 @@
filter(lambda x: x.subject == child_task_subject, project_tasks)
)
if len(corresponding_project_task):
+ project_task.reload() # reload, as it might have been updated in the previous iteration
project_task.append("depends_on", {"task": corresponding_project_task[0].name})
project_task.save()
diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py
index 8a599ce..e49fecd 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.py
@@ -1,9 +1,8 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-import unittest
-
import frappe
+from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, getdate, nowdate
from erpnext.projects.doctype.project_template.test_project_template import make_project_template
@@ -15,7 +14,7 @@
test_ignore = ["Sales Order"]
-class TestProject(unittest.TestCase):
+class TestProject(FrappeTestCase):
def test_project_with_template_having_no_parent_and_depend_tasks(self):
project_name = "Test Project with Template - No Parent and Dependend Tasks"
frappe.db.sql(""" delete from tabTask where project = %s """, project_name)
@@ -155,6 +154,50 @@
so.reload()
self.assertFalse(so.project)
+ def test_project_with_template_tasks_having_common_name(self):
+ # Step - 1: Create Template Parent Tasks
+ template_parent_task1 = create_task(subject="Parent Task - 1", is_template=1, is_group=1)
+ template_parent_task2 = create_task(subject="Parent Task - 2", is_template=1, is_group=1)
+ template_parent_task3 = create_task(subject="Parent Task - 1", is_template=1, is_group=1)
+
+ # Step - 2: Create Template Child Tasks
+ template_task1 = create_task(
+ subject="Task - 1", is_template=1, parent_task=template_parent_task1.name
+ )
+ template_task2 = create_task(
+ subject="Task - 2", is_template=1, parent_task=template_parent_task2.name
+ )
+ template_task3 = create_task(
+ subject="Task - 1", is_template=1, parent_task=template_parent_task3.name
+ )
+
+ # Step - 3: Create Project Template
+ template_tasks = [
+ template_parent_task1,
+ template_task1,
+ template_parent_task2,
+ template_task2,
+ template_parent_task3,
+ template_task3,
+ ]
+ project_template = make_project_template(
+ "Project template with common Task Subject", template_tasks
+ )
+
+ # Step - 4: Create Project against the Project Template
+ project = get_project("Project with common Task Subject", project_template)
+ project_tasks = frappe.get_all(
+ "Task", {"project": project.name}, ["subject", "parent_task", "is_group"]
+ )
+
+ # Test - 1: No. of Project Tasks should be equal to No. of Template Tasks
+ self.assertEquals(len(project_tasks), len(template_tasks))
+
+ # Test - 2: All child Project Tasks should have Parent Task linked
+ for pt in project_tasks:
+ if not pt.is_group:
+ self.assertIsNotNone(pt.parent_task)
+
def get_project(name, template):
diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json
index 62ec9e0..05a70c3 100644
--- a/erpnext/projects/doctype/task/task.json
+++ b/erpnext/projects/doctype/task/task.json
@@ -52,13 +52,15 @@
"company",
"lft",
"rgt",
- "old_parent"
+ "old_parent",
+ "template_task"
],
"fields": [
{
"fieldname": "subject",
"fieldtype": "Data",
"in_global_search": 1,
+ "in_list_view": 1,
"in_standard_filter": 1,
"label": "Subject",
"reqd": 1,
@@ -138,6 +140,7 @@
"fieldname": "parent_task",
"fieldtype": "Link",
"ignore_user_permissions": 1,
+ "in_list_view": 1,
"label": "Parent Task",
"options": "Task",
"search_index": 1
@@ -382,6 +385,12 @@
"fieldtype": "Date",
"label": "Completed On",
"mandatory_depends_on": "eval: doc.status == \"Completed\""
+ },
+ {
+ "fieldname": "template_task",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Template Task"
}
],
"icon": "fa fa-check",
@@ -389,7 +398,7 @@
"is_tree": 1,
"links": [],
"max_attachments": 5,
- "modified": "2023-04-17 21:06:50.174418",
+ "modified": "2023-09-06 13:52:05.861175",
"modified_by": "Administrator",
"module": "Projects",
"name": "Task",
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index 60f0941..e274a52 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -23,6 +23,7 @@
let d = locals[cdt][cdn];
let filters = {
'account_type': 'Receivable',
+ 'root_type': 'Asset',
'company': d.company,
"is_group": 0
};
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index ed270be..83689a2 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1889,6 +1889,61 @@
self.assertEqual(len(dn.packed_items), 1)
self.assertEqual(dn.items[0].item_code, "_Test Product Bundle Item Partial 2")
+ @change_settings("Selling Settings", {"editable_bundle_item_rates": 1})
+ def test_expired_rate_for_packed_item(self):
+ bundle = "_Test Product Bundle 1"
+ packed_item = "_Packed Item 1"
+
+ # test Update Items with product bundle
+ for product_bundle in [bundle]:
+ if not frappe.db.exists("Item", product_bundle):
+ bundle_item = make_item(product_bundle, {"is_stock_item": 0})
+ bundle_item.append(
+ "item_defaults", {"company": "_Test Company", "default_warehouse": "_Test Warehouse - _TC"}
+ )
+ bundle_item.save(ignore_permissions=True)
+
+ for product_bundle in [packed_item]:
+ if not frappe.db.exists("Item", product_bundle):
+ make_item(product_bundle, {"is_stock_item": 0, "stock_uom": "Nos"})
+
+ make_product_bundle(bundle, [packed_item], 1)
+
+ for scenario in [
+ {"valid_upto": add_days(nowdate(), -1), "expected_rate": 0.0},
+ {"valid_upto": add_days(nowdate(), 1), "expected_rate": 111.0},
+ ]:
+ with self.subTest(scenario=scenario):
+ frappe.get_doc(
+ {
+ "doctype": "Item Price",
+ "item_code": packed_item,
+ "selling": 1,
+ "price_list": "_Test Price List",
+ "valid_from": add_days(nowdate(), -1),
+ "valid_upto": scenario.get("valid_upto"),
+ "price_list_rate": 111,
+ }
+ ).save()
+
+ so = frappe.new_doc("Sales Order")
+ so.transaction_date = nowdate()
+ so.delivery_date = nowdate()
+ so.set_warehouse = ""
+ so.company = "_Test Company"
+ so.customer = "_Test Customer"
+ so.currency = "INR"
+ so.selling_price_list = "_Test Price List"
+ so.append("items", {"item_code": bundle, "qty": 1})
+ so.save()
+
+ self.assertEqual(len(so.items), 1)
+ self.assertEqual(len(so.packed_items), 1)
+ self.assertEqual(so.items[0].item_code, bundle)
+ self.assertEqual(so.packed_items[0].item_code, packed_item)
+ self.assertEqual(so.items[0].rate, scenario.get("expected_rate"))
+ self.assertEqual(so.packed_items[0].rate, scenario.get("expected_rate"))
+
def automatically_fetch_payment_terms(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
diff --git a/erpnext/setup/doctype/authorization_rule/authorization_rule.py b/erpnext/setup/doctype/authorization_rule/authorization_rule.py
index b7823a1..9e64e55 100644
--- a/erpnext/setup/doctype/authorization_rule/authorization_rule.py
+++ b/erpnext/setup/doctype/authorization_rule/authorization_rule.py
@@ -56,8 +56,6 @@
frappe.throw(_("Discount must be less than 100"))
elif self.based_on == "Customerwise Discount" and not self.master_name:
frappe.throw(_("Customer required for 'Customerwise Discount'"))
- else:
- self.based_on = "Not Applicable"
def validate(self):
self.check_duplicate_entry()
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index fa207ec..4973dab 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -200,8 +200,8 @@
$.each([
["default_bank_account", {"account_type": "Bank"}],
["default_cash_account", {"account_type": "Cash"}],
- ["default_receivable_account", {"account_type": "Receivable"}],
- ["default_payable_account", {"account_type": "Payable"}],
+ ["default_receivable_account", { "root_type": "Asset", "account_type": "Receivable" }],
+ ["default_payable_account", { "root_type": "Liability", "account_type": "Payable" }],
["default_expense_account", {"root_type": "Expense"}],
["default_income_account", {"root_type": "Income"}],
["round_off_account", {"root_type": "Expense"}],
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index ed2852e..24d7da4 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -33,22 +33,17 @@
"fax",
"website",
"address_html",
+ "registration_info",
+ "registration_details",
+ "lft",
+ "rgt",
+ "old_parent",
+ "accounts_tab",
"section_break_28",
"create_chart_of_accounts_based_on",
"existing_company",
"column_break_26",
"chart_of_accounts",
- "charts_section",
- "sales_settings",
- "default_buying_terms",
- "sales_monthly_history",
- "monthly_sales_target",
- "total_monthly_sales",
- "column_break_goals",
- "default_selling_terms",
- "default_warehouse_for_sales_return",
- "credit_limit",
- "transactions_annual_history",
"default_settings",
"default_bank_account",
"default_cash_account",
@@ -75,16 +70,12 @@
"column_break_fwcf",
"default_advance_received_account",
"default_advance_paid_account",
- "auto_accounting_for_stock_settings",
- "enable_perpetual_inventory",
- "enable_provisional_accounting_for_non_stock_items",
- "default_inventory_account",
- "stock_adjustment_account",
- "default_in_transit_warehouse",
- "column_break_32",
- "stock_received_but_not_billed",
- "default_provisional_account",
- "expenses_included_in_valuation",
+ "exchange_rate_revaluation_settings_section",
+ "auto_exchange_rate_revaluation",
+ "auto_err_frequency",
+ "submit_err_jv",
+ "budget_detail",
+ "exception_budget_approver_role",
"fixed_asset_defaults",
"accumulated_depreciation_account",
"depreciation_expense_account",
@@ -95,17 +86,29 @@
"depreciation_cost_center",
"capital_work_in_progress_account",
"asset_received_but_not_billed",
- "exchange_rate_revaluation_settings_section",
- "auto_exchange_rate_revaluation",
- "auto_err_frequency",
- "submit_err_jv",
- "budget_detail",
- "exception_budget_approver_role",
- "registration_info",
- "registration_details",
- "lft",
- "rgt",
- "old_parent"
+ "buying_and_selling_tab",
+ "sales_settings",
+ "default_buying_terms",
+ "sales_monthly_history",
+ "monthly_sales_target",
+ "total_monthly_sales",
+ "column_break_goals",
+ "default_selling_terms",
+ "default_warehouse_for_sales_return",
+ "credit_limit",
+ "transactions_annual_history",
+ "stock_tab",
+ "auto_accounting_for_stock_settings",
+ "enable_perpetual_inventory",
+ "enable_provisional_accounting_for_non_stock_items",
+ "default_inventory_account",
+ "stock_adjustment_account",
+ "default_in_transit_warehouse",
+ "column_break_32",
+ "stock_received_but_not_billed",
+ "default_provisional_account",
+ "expenses_included_in_valuation",
+ "dashboard_tab"
],
"fields": [
{
@@ -212,11 +215,6 @@
"read_only": 1
},
{
- "fieldname": "charts_section",
- "fieldtype": "Section Break",
- "label": "Default Values"
- },
- {
"fieldname": "default_currency",
"fieldtype": "Link",
"ignore_user_permissions": 1,
@@ -535,7 +533,6 @@
"options": "Account"
},
{
- "collapsible": 1,
"fieldname": "budget_detail",
"fieldtype": "Section Break",
"label": "Budget Detail"
@@ -680,7 +677,6 @@
"fieldtype": "Column Break"
},
{
- "collapsible": 1,
"fieldname": "fixed_asset_defaults",
"fieldtype": "Section Break",
"label": "Fixed Asset Defaults"
@@ -758,6 +754,27 @@
"fieldname": "submit_err_jv",
"fieldtype": "Check",
"label": "Submit ERR Journals?"
+ },
+ {
+ "fieldname": "accounts_tab",
+ "fieldtype": "Tab Break",
+ "label": "Accounts"
+ },
+ {
+ "fieldname": "buying_and_selling_tab",
+ "fieldtype": "Tab Break",
+ "label": "Buying and Selling"
+ },
+ {
+ "fieldname": "stock_tab",
+ "fieldtype": "Tab Break",
+ "label": "Stock"
+ },
+ {
+ "fieldname": "dashboard_tab",
+ "fieldtype": "Tab Break",
+ "label": "Dashboard",
+ "show_dashboard": 1
}
],
"icon": "fa fa-building",
@@ -765,7 +782,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
- "modified": "2023-07-07 05:41:41.537256",
+ "modified": "2023-09-10 21:53:13.860791",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js
index 49a90f9..3c81b02 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.js
+++ b/erpnext/setup/doctype/customer_group/customer_group.js
@@ -30,6 +30,7 @@
frm.set_query('account', 'accounts', function (doc, cdt, cdn) {
return {
filters: {
+ 'root_type': 'Asset',
"account_type": 'Receivable',
"company": locals[cdt][cdn].company,
"is_group": 0
diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js
index b2acfd7..3362929 100644
--- a/erpnext/setup/doctype/supplier_group/supplier_group.js
+++ b/erpnext/setup/doctype/supplier_group/supplier_group.js
@@ -30,6 +30,7 @@
frm.set_query('account', 'accounts', function (doc, cdt, cdn) {
return {
filters: {
+ 'root_type': 'Liability',
'account_type': 'Payable',
'company': locals[cdt][cdn].company,
"is_group": 0
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 6a9e241..e0d4919 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -1253,6 +1253,7 @@
"depends_on": "eval: doc.is_internal_customer",
"fieldname": "set_target_warehouse",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"in_standard_filter": 1,
"label": "Set Target Warehouse",
"no_copy": 1,
@@ -1400,7 +1401,7 @@
"idx": 146,
"is_submittable": 1,
"links": [],
- "modified": "2023-06-16 14:58:55.066602",
+ "modified": "2023-09-04 14:15:28.363184",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
index e66c233..d4a574d 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
@@ -11,10 +11,12 @@
},
"internal_links": {
"Sales Order": ["items", "against_sales_order"],
- "Sales Invoice": ["items", "against_sales_invoice"],
"Material Request": ["items", "material_request"],
"Purchase Order": ["items", "purchase_order"],
},
+ "internal_and_external_links": {
+ "Sales Invoice": ["items", "against_sales_invoice"],
+ },
"transactions": [
{"label": _("Related"), "items": ["Sales Invoice", "Packing Slip", "Delivery Trip"]},
{"label": _("Reference"), "items": ["Sales Order", "Shipment", "Quality Inspection"]},
diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py
index dbd8de4..a9e9ad1 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -207,6 +207,9 @@
"conversion_rate": doc.get("conversion_rate"),
}
)
+ if not row_data.get("transaction_date"):
+ row_data.update({"transaction_date": doc.get("transaction_date")})
+
rate = get_price_list_rate(row_data, item_doc).get("price_list_rate")
pi_row.rate = rate or item_data.get("valuation_rate") or 0.0
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 60aefdd..04eff54 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -603,7 +603,7 @@
account=provisional_account,
cost_center=item.cost_center,
debit=0.0,
- credit=multiplication_factor * item.amount,
+ credit=multiplication_factor * item.base_amount,
remarks=remarks,
against_account=expense_account,
account_currency=credit_currency,
@@ -617,7 +617,7 @@
gl_entries=gl_entries,
account=expense_account,
cost_center=item.cost_center,
- debit=multiplication_factor * item.amount,
+ debit=multiplication_factor * item.base_amount,
credit=0.0,
remarks=remarks,
against_account=provisional_account,
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 6134bfa..b7712ee 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -2017,6 +2017,49 @@
ste7.reload()
self.assertEqual(ste7.items[0].valuation_rate, valuation_rate)
+ def test_purchase_receipt_provisional_accounting(self):
+ # Step - 1: Create Supplier with Default Currency as USD
+ from erpnext.buying.doctype.supplier.test_supplier import create_supplier
+
+ supplier = create_supplier(default_currency="USD")
+
+ # Step - 2: Setup Company for Provisional Accounting
+ from erpnext.accounts.doctype.account.test_account import create_account
+
+ provisional_account = create_account(
+ account_name="Provision Account",
+ parent_account="Current Liabilities - _TC",
+ company="_Test Company",
+ )
+ company = frappe.get_doc("Company", "_Test Company")
+ company.enable_provisional_accounting_for_non_stock_items = 1
+ company.default_provisional_account = provisional_account
+ company.save()
+
+ # Step - 3: Create Non-Stock Item
+ item = make_item(properties={"is_stock_item": 0})
+
+ # Step - 4: Create Purchase Receipt
+ pr = make_purchase_receipt(
+ qty=2,
+ item_code=item.name,
+ company=company.name,
+ supplier=supplier.name,
+ currency=supplier.default_currency,
+ )
+
+ # Test - 1: Total and Base Total should not be the same as the currency is different
+ self.assertNotEqual(flt(pr.total, 2), flt(pr.base_total, 2))
+ self.assertEqual(flt(pr.total * pr.conversion_rate, 2), flt(pr.base_total, 2))
+
+ # Test - 2: Sum of Debit or Credit should be equal to Purchase Receipt Base Total
+ amount = frappe.db.get_value("GL Entry", {"docstatus": 1, "voucher_no": pr.name}, ["sum(debit)"])
+ expected_amount = pr.base_total
+ self.assertEqual(amount, expected_amount)
+
+ company.enable_provisional_accounting_for_non_stock_items = 0
+ company.save()
+
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier