Merge pull request #32622 from deepeshgarg007/ignore_pi_reference
fix: Ignore linked purchase invoice on cancel
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
index dff883a..a0ea433 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -202,6 +202,7 @@
"fieldname": "reference_type",
"fieldtype": "Select",
"label": "Reference Type",
+ "no_copy": 1,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement"
},
{
@@ -209,13 +210,15 @@
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Reference Name",
+ "no_copy": 1,
"options": "reference_type"
},
{
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])",
"fieldname": "reference_due_date",
"fieldtype": "Select",
- "label": "Reference Due Date"
+ "label": "Reference Due Date",
+ "no_copy": 1
},
{
"fieldname": "project",
@@ -274,19 +277,22 @@
"fieldname": "reference_detail_no",
"fieldtype": "Data",
"hidden": 1,
- "label": "Reference Detail No"
+ "label": "Reference Detail No",
+ "no_copy": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-08-30 21:27:32.200299",
+ "modified": "2022-10-13 17:07:17.999191",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 95ba3d8..6667454 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -460,10 +460,6 @@
frappe.flags.ignore_party_validation = False
- if entry.voucher_type in ("Payment Entry", "Journal Entry"):
- if hasattr(doc, "update_expense_claim"):
- doc.update_expense_claim()
-
def check_if_advance_entry_modified(args):
"""
@@ -1171,6 +1167,10 @@
where voucher_type=%s and voucher_no=%s""",
(voucher_type, voucher_no),
)
+ ple = qb.DocType("Payment Ledger Entry")
+ qb.from_(ple).delete().where(
+ (ple.voucher_type == voucher_type) & (ple.voucher_no == voucher_no)
+ ).run()
def sort_stock_vouchers_by_posting_date(
@@ -1368,9 +1368,8 @@
frappe.delete_doc("Desktop Icon", icon)
-def create_payment_ledger_entry(
- gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0
-):
+def get_payment_ledger_entries(gl_entries, cancel=0):
+ ple_map = []
if gl_entries:
ple = None
@@ -1410,44 +1409,57 @@
dr_or_cr *= -1
dr_or_cr_account_currency *= -1
- ple = frappe.get_doc(
- {
- "doctype": "Payment Ledger Entry",
- "posting_date": gle.posting_date,
- "company": gle.company,
- "account_type": account_type,
- "account": gle.account,
- "party_type": gle.party_type,
- "party": gle.party,
- "cost_center": gle.cost_center,
- "finance_book": gle.finance_book,
- "due_date": gle.due_date,
- "voucher_type": gle.voucher_type,
- "voucher_no": gle.voucher_no,
- "against_voucher_type": gle.against_voucher_type
- if gle.against_voucher_type
- else gle.voucher_type,
- "against_voucher_no": gle.against_voucher if gle.against_voucher else gle.voucher_no,
- "account_currency": gle.account_currency,
- "amount": dr_or_cr,
- "amount_in_account_currency": dr_or_cr_account_currency,
- "delinked": True if cancel else False,
- "remarks": gle.remarks,
- }
+ ple = frappe._dict(
+ doctype="Payment Ledger Entry",
+ posting_date=gle.posting_date,
+ company=gle.company,
+ account_type=account_type,
+ account=gle.account,
+ party_type=gle.party_type,
+ party=gle.party,
+ cost_center=gle.cost_center,
+ finance_book=gle.finance_book,
+ due_date=gle.due_date,
+ voucher_type=gle.voucher_type,
+ voucher_no=gle.voucher_no,
+ against_voucher_type=gle.against_voucher_type
+ if gle.against_voucher_type
+ else gle.voucher_type,
+ against_voucher_no=gle.against_voucher if gle.against_voucher else gle.voucher_no,
+ account_currency=gle.account_currency,
+ amount=dr_or_cr,
+ amount_in_account_currency=dr_or_cr_account_currency,
+ delinked=True if cancel else False,
+ remarks=gle.remarks,
)
dimensions_and_defaults = get_dimensions()
if dimensions_and_defaults:
for dimension in dimensions_and_defaults[0]:
- ple.set(dimension.fieldname, gle.get(dimension.fieldname))
+ ple[dimension.fieldname] = gle.get(dimension.fieldname)
- if cancel:
- delink_original_entry(ple)
- ple.flags.ignore_permissions = 1
- ple.flags.adv_adj = adv_adj
- ple.flags.from_repost = from_repost
- ple.flags.update_outstanding = update_outstanding
- ple.submit()
+ ple_map.append(ple)
+ return ple_map
+
+
+def create_payment_ledger_entry(
+ gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0
+):
+ if gl_entries:
+ ple_map = get_payment_ledger_entries(gl_entries, cancel=cancel)
+
+ for entry in ple_map:
+
+ ple = frappe.get_doc(entry)
+
+ if cancel:
+ delink_original_entry(ple)
+
+ ple.flags.ignore_permissions = 1
+ ple.flags.adv_adj = adv_adj
+ ple.flags.from_repost = from_repost
+ ple.flags.update_outstanding = update_outstanding
+ ple.submit()
def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, party):
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index bcedd4d..c224b61 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -18,7 +18,7 @@
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
get_party_tax_withholding_details,
)
-from erpnext.accounts.party import get_party_account_currency
+from erpnext.accounts.party import get_party_account, get_party_account_currency
from erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items
from erpnext.controllers.buying_controller import BuyingController
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
@@ -558,6 +558,7 @@
target.set_advances()
target.set_payment_schedule()
+ target.credit_to = get_party_account("Supplier", source.supplier, source.company)
def update_item(obj, target, source_parent):
target.amount = flt(obj.amount) - flt(obj.billed_amt)
diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json
index 99c00ad..8f8a086 100644
--- a/erpnext/crm/doctype/lead/lead.json
+++ b/erpnext/crm/doctype/lead/lead.json
@@ -375,7 +375,7 @@
"depends_on": "eval:!doc.__islocal",
"fieldname": "notes_tab",
"fieldtype": "Tab Break",
- "label": "Notes"
+ "label": "Comments"
},
{
"collapsible": 1,
@@ -506,7 +506,7 @@
{
"fieldname": "dashboard_tab",
"fieldtype": "Tab Break",
- "label": "Dashboard",
+ "label": "Connections",
"show_dashboard": 1
}
],
@@ -514,7 +514,7 @@
"idx": 5,
"image_field": "image",
"links": [],
- "modified": "2022-08-09 18:26:17.101521",
+ "modified": "2022-10-13 12:42:04.277879",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json
index fed0c7c..07641d2 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.json
+++ b/erpnext/crm/doctype/opportunity/opportunity.json
@@ -544,14 +544,14 @@
"depends_on": "eval:!doc.__islocal",
"fieldname": "dashboard_tab",
"fieldtype": "Tab Break",
- "label": "Dashboard",
+ "label": "Connections",
"show_dashboard": 1
},
{
"depends_on": "eval:!doc.__islocal",
"fieldname": "notes_tab",
"fieldtype": "Tab Break",
- "label": "Notes"
+ "label": "Comments"
},
{
"fieldname": "notes_html",
@@ -622,7 +622,7 @@
"icon": "fa fa-info-sign",
"idx": 195,
"links": [],
- "modified": "2022-08-09 18:26:37.235964",
+ "modified": "2022-10-13 12:42:21.545636",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",
diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json
index 820a6c7..d32311b 100644
--- a/erpnext/crm/doctype/prospect/prospect.json
+++ b/erpnext/crm/doctype/prospect/prospect.json
@@ -128,7 +128,7 @@
"depends_on": "eval:!doc.__islocal",
"fieldname": "notes_section",
"fieldtype": "Tab Break",
- "label": "Notes"
+ "label": "Comments"
},
{
"depends_on": "eval: !doc.__islocal",
@@ -218,7 +218,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2022-08-09 18:26:56.950185",
+ "modified": "2022-10-13 12:29:33.674561",
"modified_by": "Administrator",
"module": "CRM",
"name": "Prospect",
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index b8f51f8..6bc17a3 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -274,8 +274,6 @@
"Timesheet": "erpnext.controllers.website_list_for_contact.has_website_permission",
}
-dump_report_map = "erpnext.startup.report_data_map.data_map"
-
before_tests = "erpnext.setup.utils.before_tests"
standard_queries = {
diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json
index 1253649..4bdb1db 100644
--- a/erpnext/projects/workspace/projects/projects.json
+++ b/erpnext/projects/workspace/projects/projects.json
@@ -5,7 +5,7 @@
"label": "Open Projects"
}
],
- "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
+ "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"creation": "2020-03-02 15:46:04.874669",
"docstatus": 0,
"doctype": "Workspace",
@@ -170,9 +170,27 @@
"link_type": "Report",
"onboard": 0,
"type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Settings",
+ "link_count": 1,
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Projects Settings",
+ "link_count": 0,
+ "link_to": "Projects Settings",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
}
],
- "modified": "2022-06-28 12:31:30.167740",
+ "modified": "2022-10-11 22:39:10.436311",
"modified_by": "Administrator",
"module": "Projects",
"name": "Projects",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 25806d6..f0e9e4b 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -18,6 +18,7 @@
update_linked_doc,
validate_inter_company_party,
)
+from erpnext.accounts.party import get_party_account
from erpnext.controllers.selling_controller import SellingController
from erpnext.manufacturing.doctype.production_plan.production_plan import (
get_items_for_material_requests,
@@ -727,6 +728,8 @@
if source.loyalty_points and source.order_type == "Shopping Cart":
target.redeem_loyalty_points = 1
+ target.debit_to = get_party_account("Customer", source.customer, source.company)
+
def update_item(source, target, source_parent):
target.amount = flt(source.amount) - flt(source.billed_amt)
target.base_amount = target.amount * flt(source_parent.conversion_rate)
diff --git a/erpnext/startup/report_data_map.py b/erpnext/startup/report_data_map.py
deleted file mode 100644
index f8c1b6c..0000000
--- a/erpnext/startup/report_data_map.py
+++ /dev/null
@@ -1,327 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-# mappings for table dumps
-# "remember to add indexes!"
-
-data_map = {
- "Company": {"columns": ["name"], "conditions": ["docstatus < 2"]},
- "Fiscal Year": {
- "columns": ["name", "year_start_date", "year_end_date"],
- "conditions": ["docstatus < 2"],
- },
- # Accounts
- "Account": {
- "columns": ["name", "parent_account", "lft", "rgt", "report_type", "company", "is_group"],
- "conditions": ["docstatus < 2"],
- "order_by": "lft",
- "links": {
- "company": ["Company", "name"],
- },
- },
- "Cost Center": {
- "columns": ["name", "lft", "rgt"],
- "conditions": ["docstatus < 2"],
- "order_by": "lft",
- },
- "GL Entry": {
- "columns": [
- "name",
- "account",
- "posting_date",
- "cost_center",
- "debit",
- "credit",
- "is_opening",
- "company",
- "voucher_type",
- "voucher_no",
- "remarks",
- ],
- "order_by": "posting_date, account",
- "links": {
- "account": ["Account", "name"],
- "company": ["Company", "name"],
- "cost_center": ["Cost Center", "name"],
- },
- },
- # Stock
- "Item": {
- "columns": [
- "name",
- "if(item_name=name, '', item_name) as item_name",
- "description",
- "item_group as parent_item_group",
- "stock_uom",
- "brand",
- "valuation_method",
- ],
- # "conditions": ["docstatus < 2"],
- "order_by": "name",
- "links": {"parent_item_group": ["Item Group", "name"], "brand": ["Brand", "name"]},
- },
- "Item Group": {
- "columns": ["name", "parent_item_group"],
- # "conditions": ["docstatus < 2"],
- "order_by": "lft",
- },
- "Brand": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"},
- "Project": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"},
- "Warehouse": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"},
- "Stock Ledger Entry": {
- "columns": [
- "name",
- "posting_date",
- "posting_time",
- "item_code",
- "warehouse",
- "actual_qty as qty",
- "voucher_type",
- "voucher_no",
- "project",
- "incoming_rate as incoming_rate",
- "stock_uom",
- "serial_no",
- "qty_after_transaction",
- "valuation_rate",
- ],
- "order_by": "posting_date, posting_time, creation",
- "links": {
- "item_code": ["Item", "name"],
- "warehouse": ["Warehouse", "name"],
- "project": ["Project", "name"],
- },
- "force_index": "posting_sort_index",
- },
- "Serial No": {
- "columns": ["name", "purchase_rate as incoming_rate"],
- "conditions": ["docstatus < 2"],
- "order_by": "name",
- },
- "Stock Entry": {
- "columns": ["name", "purpose"],
- "conditions": ["docstatus=1"],
- "order_by": "posting_date, posting_time, name",
- },
- "Material Request Item": {
- "columns": ["item.name as name", "item_code", "warehouse", "(qty - ordered_qty) as qty"],
- "from": "`tabMaterial Request Item` item, `tabMaterial Request` main",
- "conditions": [
- "item.parent = main.name",
- "main.docstatus=1",
- "main.status != 'Stopped'",
- "ifnull(warehouse, '')!=''",
- "qty > ordered_qty",
- ],
- "links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]},
- },
- "Purchase Order Item": {
- "columns": [
- "item.name as name",
- "item_code",
- "warehouse",
- "(qty - received_qty)*conversion_factor as qty",
- ],
- "from": "`tabPurchase Order Item` item, `tabPurchase Order` main",
- "conditions": [
- "item.parent = main.name",
- "main.docstatus=1",
- "main.status != 'Stopped'",
- "ifnull(warehouse, '')!=''",
- "qty > received_qty",
- ],
- "links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]},
- },
- "Sales Order Item": {
- "columns": [
- "item.name as name",
- "item_code",
- "(qty - delivered_qty)*conversion_factor as qty",
- "warehouse",
- ],
- "from": "`tabSales Order Item` item, `tabSales Order` main",
- "conditions": [
- "item.parent = main.name",
- "main.docstatus=1",
- "main.status != 'Stopped'",
- "ifnull(warehouse, '')!=''",
- "qty > delivered_qty",
- ],
- "links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]},
- },
- # Sales
- "Customer": {
- "columns": [
- "name",
- "if(customer_name=name, '', customer_name) as customer_name",
- "customer_group as parent_customer_group",
- "territory as parent_territory",
- ],
- "conditions": ["docstatus < 2"],
- "order_by": "name",
- "links": {
- "parent_customer_group": ["Customer Group", "name"],
- "parent_territory": ["Territory", "name"],
- },
- },
- "Customer Group": {
- "columns": ["name", "parent_customer_group"],
- "conditions": ["docstatus < 2"],
- "order_by": "lft",
- },
- "Territory": {
- "columns": ["name", "parent_territory"],
- "conditions": ["docstatus < 2"],
- "order_by": "lft",
- },
- "Sales Invoice": {
- "columns": ["name", "customer", "posting_date", "company"],
- "conditions": ["docstatus=1"],
- "order_by": "posting_date",
- "links": {"customer": ["Customer", "name"], "company": ["Company", "name"]},
- },
- "Sales Invoice Item": {
- "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
- "conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
- "order_by": "parent",
- "links": {"parent": ["Sales Invoice", "name"], "item_code": ["Item", "name"]},
- },
- "Sales Order": {
- "columns": ["name", "customer", "transaction_date as posting_date", "company"],
- "conditions": ["docstatus=1"],
- "order_by": "transaction_date",
- "links": {"customer": ["Customer", "name"], "company": ["Company", "name"]},
- },
- "Sales Order Item[Sales Analytics]": {
- "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
- "conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
- "order_by": "parent",
- "links": {"parent": ["Sales Order", "name"], "item_code": ["Item", "name"]},
- },
- "Delivery Note": {
- "columns": ["name", "customer", "posting_date", "company"],
- "conditions": ["docstatus=1"],
- "order_by": "posting_date",
- "links": {"customer": ["Customer", "name"], "company": ["Company", "name"]},
- },
- "Delivery Note Item[Sales Analytics]": {
- "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
- "conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
- "order_by": "parent",
- "links": {"parent": ["Delivery Note", "name"], "item_code": ["Item", "name"]},
- },
- "Supplier": {
- "columns": [
- "name",
- "if(supplier_name=name, '', supplier_name) as supplier_name",
- "supplier_group as parent_supplier_group",
- ],
- "conditions": ["docstatus < 2"],
- "order_by": "name",
- "links": {
- "parent_supplier_group": ["Supplier Group", "name"],
- },
- },
- "Supplier Group": {
- "columns": ["name", "parent_supplier_group"],
- "conditions": ["docstatus < 2"],
- "order_by": "name",
- },
- "Purchase Invoice": {
- "columns": ["name", "supplier", "posting_date", "company"],
- "conditions": ["docstatus=1"],
- "order_by": "posting_date",
- "links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]},
- },
- "Purchase Invoice Item": {
- "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
- "conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
- "order_by": "parent",
- "links": {"parent": ["Purchase Invoice", "name"], "item_code": ["Item", "name"]},
- },
- "Purchase Order": {
- "columns": ["name", "supplier", "transaction_date as posting_date", "company"],
- "conditions": ["docstatus=1"],
- "order_by": "posting_date",
- "links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]},
- },
- "Purchase Order Item[Purchase Analytics]": {
- "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
- "conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
- "order_by": "parent",
- "links": {"parent": ["Purchase Order", "name"], "item_code": ["Item", "name"]},
- },
- "Purchase Receipt": {
- "columns": ["name", "supplier", "posting_date", "company"],
- "conditions": ["docstatus=1"],
- "order_by": "posting_date",
- "links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]},
- },
- "Purchase Receipt Item[Purchase Analytics]": {
- "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
- "conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
- "order_by": "parent",
- "links": {"parent": ["Purchase Receipt", "name"], "item_code": ["Item", "name"]},
- },
- # Support
- "Issue": {
- "columns": ["name", "status", "creation", "resolution_date", "first_responded_on"],
- "conditions": ["docstatus < 2"],
- "order_by": "creation",
- },
- # Manufacturing
- "Work Order": {
- "columns": [
- "name",
- "status",
- "creation",
- "planned_start_date",
- "planned_end_date",
- "status",
- "actual_start_date",
- "actual_end_date",
- "modified",
- ],
- "conditions": ["docstatus = 1"],
- "order_by": "creation",
- },
- # Medical
- "Patient": {
- "columns": [
- "name",
- "creation",
- "owner",
- "if(patient_name=name, '', patient_name) as patient_name",
- ],
- "conditions": ["docstatus < 2"],
- "order_by": "name",
- "links": {"owner": ["User", "name"]},
- },
- "Patient Appointment": {
- "columns": [
- "name",
- "appointment_type",
- "patient",
- "practitioner",
- "appointment_date",
- "department",
- "status",
- "company",
- ],
- "order_by": "name",
- "links": {
- "practitioner": ["Healthcare Practitioner", "name"],
- "appointment_type": ["Appointment Type", "name"],
- },
- },
- "Healthcare Practitioner": {
- "columns": ["name", "department"],
- "order_by": "name",
- "links": {
- "department": ["Department", "name"],
- },
- },
- "Appointment Type": {"columns": ["name"], "order_by": "name"},
- "Medical Department": {"columns": ["name"], "order_by": "name"},
-}
diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
index edd2553..e0f2479 100644
--- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
@@ -9,6 +9,7 @@
from frappe.utils import nowdate
from frappe.utils.data import add_to_date, today
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.utils import repost_gle_for_stock_vouchers
from erpnext.controllers.stock_controller import create_item_wise_repost_entries
from erpnext.stock.doctype.item.test_item import make_item
@@ -272,3 +273,57 @@
[{"credit": 50, "debit": 0}],
gle_filters={"account": "Stock In Hand - TCP1"},
)
+
+ def test_duplicate_ple_on_repost(self):
+ from erpnext.accounts import utils
+
+ # lower numbers to simplify test
+ orig_chunk_size = utils.GL_REPOSTING_CHUNK
+ utils.GL_REPOSTING_CHUNK = 2
+ self.addCleanup(setattr, utils, "GL_REPOSTING_CHUNK", orig_chunk_size)
+
+ rate = 100
+ item = self.make_item()
+ item.valuation_rate = 90
+ item.allow_negative_stock = 1
+ item.save()
+
+ company = "_Test Company with perpetual inventory"
+
+ # consume non-existing stock
+ sinv = create_sales_invoice(
+ company=company,
+ posting_date=today(),
+ debit_to="Debtors - TCP1",
+ income_account="Sales - TCP1",
+ expense_account="Cost of Goods Sold - TCP1",
+ warehouse="Stores - TCP1",
+ update_stock=1,
+ currency="INR",
+ item_code=item.name,
+ cost_center="Main - TCP1",
+ qty=1,
+ rate=rate,
+ )
+
+ # backdated receipt triggers repost
+ make_stock_entry(
+ item=item.name,
+ company=company,
+ qty=5,
+ rate=rate,
+ target="Stores - TCP1",
+ posting_date=add_to_date(today(), days=-1),
+ )
+
+ ple_entries = frappe.db.get_list(
+ "Payment Ledger Entry",
+ filters={"voucher_type": sinv.doctype, "voucher_no": sinv.name, "delinked": 0},
+ )
+
+ # assert successful deduplication on PLE
+ self.assertEqual(len(ple_entries), 1)
+
+ # outstanding should not be affected
+ sinv.reload()
+ self.assertEqual(sinv.outstanding_amount, 100)
diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py
index ab784ca..6e06d23 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.py
+++ b/erpnext/stock/doctype/warehouse/warehouse.py
@@ -9,6 +9,7 @@
from frappe.contacts.address_and_contact import load_address_and_contact
from frappe.utils import cint, flt
from frappe.utils.nestedset import NestedSet
+from pypika.terms import ExistsCriterion
from erpnext.stock import get_warehouse_account
@@ -266,3 +267,23 @@
frappe.throw(_("Warehouse not found against the account {0}").format(account))
return warehouses
+
+
+# Will be use for frappe.qb
+def apply_warehouse_filter(query, sle, filters):
+ if warehouse := filters.get("warehouse"):
+ warehouse_table = frappe.qb.DocType("Warehouse")
+
+ lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
+ chilren_subquery = (
+ frappe.qb.from_(warehouse_table)
+ .select(warehouse_table.name)
+ .where(
+ (warehouse_table.lft >= lft)
+ & (warehouse_table.rgt <= rgt)
+ & (warehouse_table.name == sle.warehouse)
+ )
+ )
+ query = query.where(ExistsCriterion(chilren_subquery))
+
+ return query
diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
index 8a13300..291c6b5 100644
--- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
+++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
@@ -5,6 +5,9 @@
import frappe
from frappe import _
from frappe.utils import cint, flt, getdate
+from pypika import functions as fn
+
+from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter
def execute(filters=None):
@@ -64,36 +67,42 @@
return columns
-def get_conditions(filters):
- conditions = ""
+# get all details
+def get_stock_ledger_entries(filters):
if not filters.get("from_date"):
frappe.throw(_("'From Date' is required"))
- if filters.get("to_date"):
- conditions += " and posting_date <= '%s'" % filters["to_date"]
+ sle = frappe.qb.DocType("Stock Ledger Entry")
+ query = (
+ frappe.qb.from_(sle)
+ .select(
+ sle.item_code,
+ sle.warehouse,
+ sle.batch_no,
+ sle.posting_date,
+ fn.Sum(sle.actual_qty).as_("actual_qty"),
+ )
+ .where(
+ (sle.docstatus < 2)
+ & (sle.is_cancelled == 0)
+ & (sle.batch_no.isnotnull())
+ & (sle.batch_no != "")
+ )
+ .groupby(sle.voucher_no, sle.batch_no, sle.item_code, sle.warehouse)
+ .orderby(sle.item_code, sle.warehouse)
+ )
+
+ if to_date := filters.get("to_date"):
+ query = query.where(sle.posting_date <= to_date)
else:
frappe.throw(_("'To Date' is required"))
- for field in ["item_code", "warehouse", "batch_no", "company"]:
+ query = apply_warehouse_filter(query, sle, filters)
+ for field in ["item_code", "batch_no", "company"]:
if filters.get(field):
- conditions += " and {0} = {1}".format(field, frappe.db.escape(filters.get(field)))
+ query = query.where(sle[field] == filters.get(field))
- return conditions
-
-
-# get all details
-def get_stock_ledger_entries(filters):
- conditions = get_conditions(filters)
- return frappe.db.sql(
- """
- select item_code, batch_no, warehouse, posting_date, sum(actual_qty) as actual_qty
- from `tabStock Ledger Entry`
- where is_cancelled = 0 and docstatus < 2 and ifnull(batch_no, '') != '' %s
- group by voucher_no, batch_no, item_code, warehouse
- order by item_code, warehouse"""
- % conditions,
- as_dict=1,
- )
+ return query.run(as_dict=True)
def get_item_warehouse_batch_map(filters, float_precision):
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index 679d234..0fc642e 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -10,10 +10,10 @@
from frappe.query_builder.functions import CombineDatetime
from frappe.utils import cint, date_diff, flt, getdate
from frappe.utils.nestedset import get_descendants_of
-from pypika.terms import ExistsCriterion
import erpnext
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
+from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter
from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age
from erpnext.stock.utils import add_additional_uom_columns, is_reposting_item_valuation_in_progress
@@ -270,18 +270,8 @@
if company := filters.get("company"):
query = query.where(sle.company == company)
- if warehouse := filters.get("warehouse"):
- lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
- chilren_subquery = (
- frappe.qb.from_(warehouse_table)
- .select(warehouse_table.name)
- .where(
- (warehouse_table.lft >= lft)
- & (warehouse_table.rgt <= rgt)
- & (warehouse_table.name == sle.warehouse)
- )
- )
- query = query.where(ExistsCriterion(chilren_subquery))
+ if filters.get("warehouse"):
+ query = apply_warehouse_filter(query, sle, filters)
elif warehouse_type := filters.get("warehouse_type"):
query = (
query.join(warehouse_table)
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index e18d4c7..a951197 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -6,11 +6,11 @@
from frappe import _
from frappe.query_builder.functions import CombineDatetime
from frappe.utils import cint, flt
-from pypika.terms import ExistsCriterion
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import get_stock_balance_for
+from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter
from erpnext.stock.utils import (
is_reposting_item_valuation_in_progress,
update_included_uom_in_report,
@@ -295,20 +295,7 @@
if filters.get(field):
query = query.where(sle[field] == filters.get(field))
- if warehouse := filters.get("warehouse"):
- lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
-
- warehouse_table = frappe.qb.DocType("Warehouse")
- chilren_subquery = (
- frappe.qb.from_(warehouse_table)
- .select(warehouse_table.name)
- .where(
- (warehouse_table.lft >= lft)
- & (warehouse_table.rgt <= rgt)
- & (warehouse_table.name == sle.warehouse)
- )
- )
- query = query.where(ExistsCriterion(chilren_subquery))
+ query = apply_warehouse_filter(query, sle, filters)
return query.run(as_dict=True)
diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
index 4e00138..472f6bc 100644
--- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
@@ -15,8 +15,30 @@
class TestServiceLevelAgreement(unittest.TestCase):
def setUp(self):
+ self.create_company()
frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
- frappe.db.sql("delete from `tabLead`")
+ lead = frappe.qb.DocType("Lead")
+ frappe.qb.from_(lead).delete().where(lead.company == self.company).run()
+
+ def create_company(self):
+ name = "_Test Support SLA"
+ company = None
+ if frappe.db.exists("Company", name):
+ company = frappe.get_doc("Company", name)
+ else:
+ company = frappe.get_doc(
+ {
+ "doctype": "Company",
+ "company_name": name,
+ "country": "India",
+ "default_currency": "INR",
+ "create_chart_of_accounts_based_on": "Standard Template",
+ "chart_of_accounts": "Standard",
+ }
+ )
+ company = company.save()
+
+ self.company = company.name
def test_service_level_agreement(self):
# Default Service Level Agreement
@@ -205,7 +227,7 @@
# make lead with default SLA
creation = datetime.datetime(2019, 3, 4, 12, 0)
- lead = make_lead(creation=creation, index=1)
+ lead = make_lead(creation=creation, index=1, company=self.company)
self.assertEqual(lead.service_level_agreement, lead_sla.name)
self.assertEqual(lead.response_by, datetime.datetime(2019, 3, 4, 16, 0))
@@ -233,7 +255,7 @@
)
creation = datetime.datetime(2020, 3, 4, 4, 0)
- lead = make_lead(creation, index=2)
+ lead = make_lead(creation, index=2, company=self.company)
frappe.flags.current_time = datetime.datetime(2020, 3, 4, 4, 15)
lead.reload()
@@ -267,7 +289,7 @@
)
creation = datetime.datetime(2019, 3, 4, 12, 0)
- lead = make_lead(creation=creation, index=1)
+ lead = make_lead(creation=creation, index=1, company=self.company)
self.assertEqual(lead.response_by, datetime.datetime(2019, 3, 4, 16, 0))
# failed with response time only
@@ -294,7 +316,7 @@
# fulfilled with response time only
creation = datetime.datetime(2019, 3, 4, 12, 0)
- lead = make_lead(creation=creation, index=2)
+ lead = make_lead(creation=creation, index=2, company=self.company)
self.assertEqual(lead.service_level_agreement, lead_sla.name)
self.assertEqual(lead.response_by, datetime.datetime(2019, 3, 4, 16, 0))
@@ -321,7 +343,7 @@
apply_sla_for_resolution=0,
)
creation = datetime.datetime(2019, 3, 4, 12, 0)
- lead = make_lead(creation=creation, index=4)
+ lead = make_lead(creation=creation, index=4, company=self.company)
applied_sla = frappe.db.get_value("Lead", lead.name, "service_level_agreement")
self.assertFalse(applied_sla)
@@ -611,7 +633,7 @@
return frappe.get_doc("DocType", "Test SLA on Custom Dt")
-def make_lead(creation=None, index=0):
+def make_lead(creation=None, index=0, company=None):
return frappe.get_doc(
{
"doctype": "Lead",
@@ -621,5 +643,6 @@
"creation": creation,
"service_level_agreement_creation": creation,
"priority": "Medium",
+ "company": company,
}
).insert(ignore_permissions=True)