mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-02 05:06:58 +00:00
Compare commits
64 Commits
chore/test
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cab1b129c0 | ||
|
|
171f12c2eb | ||
|
|
9cea43b006 | ||
|
|
15adc92e76 | ||
|
|
ba1e8f0005 | ||
|
|
3eaea74a51 | ||
|
|
d84eb9a97b | ||
|
|
e5569f681a | ||
|
|
e99be23b57 | ||
|
|
5cc866e840 | ||
|
|
9e5b492db1 | ||
|
|
4cc2902b99 | ||
|
|
5e1296a0b9 | ||
|
|
7ccd729cc5 | ||
|
|
0aed70153b | ||
|
|
bdd6a63556 | ||
|
|
e51ffb1bf1 | ||
|
|
52d5085360 | ||
|
|
1969c9ca47 | ||
|
|
83278d6f3b | ||
|
|
d4da9a3d7d | ||
|
|
99152b8300 | ||
|
|
4afbd4d3d9 | ||
|
|
e3e62a2211 | ||
|
|
304a247dc8 | ||
|
|
8abef22a49 | ||
|
|
222842b7d1 | ||
|
|
e179100afd | ||
|
|
44aa01b115 | ||
|
|
1a8ef852f1 | ||
|
|
bb184f90a7 | ||
|
|
b2ad93be81 | ||
|
|
65cb89cc40 | ||
|
|
26a646aae5 | ||
|
|
7d5efaf124 | ||
|
|
028cc2cf49 | ||
|
|
249d519d02 | ||
|
|
f4088d48a1 | ||
|
|
02460b4684 | ||
|
|
ee8e6e806f | ||
|
|
196482348d | ||
|
|
237605889f | ||
|
|
38ebfd7bd6 | ||
|
|
9ec2945e6e | ||
|
|
cb9ea22b6f | ||
|
|
8aec16376e | ||
|
|
7bc121e308 | ||
|
|
e4d6c0854b | ||
|
|
beb2974317 | ||
|
|
fad904d68b | ||
|
|
d072909451 | ||
|
|
a1daad8d4f | ||
|
|
27d5165755 | ||
|
|
088b8ff69b | ||
|
|
3b25878d71 | ||
|
|
55afd95b20 | ||
|
|
5a32866b93 | ||
|
|
dead28e50e | ||
|
|
8446be6518 | ||
|
|
e61d299e63 | ||
|
|
31ee3f1923 | ||
|
|
851dfb16be | ||
|
|
047014f2b5 | ||
|
|
90aba582ec |
@@ -471,6 +471,25 @@ def on_doctype_update():
|
||||
frappe.db.add_index("GL Entry", ["posting_date", "company"])
|
||||
frappe.db.add_index("GL Entry", ["party_type", "party"])
|
||||
|
||||
if frappe.db.db_type == "postgres":
|
||||
# Postgres-only partial/covering indexes for the financial reports (General Ledger, Trial
|
||||
# Balance, Balance Sheet, P&L), which always filter `is_cancelled = 0` and scope by company.
|
||||
# `where`/`include` are no-ops on MariaDB and its optimizer ignores these anyway, so they are
|
||||
# added only on postgres to avoid dead write overhead on this insert-hot table.
|
||||
frappe.db.add_index(
|
||||
"GL Entry",
|
||||
["company", "posting_date", "account"],
|
||||
index_name="gle_active_detail",
|
||||
where="is_cancelled = 0",
|
||||
)
|
||||
frappe.db.add_index(
|
||||
"GL Entry",
|
||||
["company", "account", "posting_date"],
|
||||
index_name="gle_active_cover",
|
||||
where="is_cancelled = 0",
|
||||
include=["debit", "credit"],
|
||||
)
|
||||
|
||||
|
||||
def rename_gle_sle_docs():
|
||||
for doctype in ["GL Entry", "Stock Ledger Entry"]:
|
||||
|
||||
@@ -360,12 +360,15 @@ class TestPeriodClosingVoucher(ERPNextTestSuite):
|
||||
|
||||
self.make_period_closing_voucher(posting_date="2021-03-31")
|
||||
|
||||
# Passed posting_date is after PCV end date, so cancellation should not fail.
|
||||
make_reverse_gl_entries(
|
||||
voucher_type="Journal Entry",
|
||||
voucher_no=jv.name,
|
||||
posting_date="2022-01-01",
|
||||
)
|
||||
frappe.db.set_value("Company", "Test PCV Company", "accounts_frozen_till_date", "2021-12-31")
|
||||
|
||||
try:
|
||||
make_reverse_gl_entries(
|
||||
voucher_type="Journal Entry",
|
||||
voucher_no=jv.name,
|
||||
)
|
||||
finally:
|
||||
frappe.db.set_value("Company", "Test PCV Company", "accounts_frozen_till_date", None)
|
||||
|
||||
totals_after_cancel = frappe.get_all(
|
||||
"GL Entry",
|
||||
|
||||
@@ -640,13 +640,15 @@ def make_reverse_gl_entries(
|
||||
partial_cancel=partial_cancel,
|
||||
)
|
||||
validate_accounting_period(gl_entries)
|
||||
check_freezing_date(gl_entries[0]["posting_date"], gl_entries[0]["company"], adv_adj)
|
||||
|
||||
is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
|
||||
|
||||
# For reverse entries, use the posting_date parameter if provided and valid
|
||||
# Otherwise fall back to original posting_date
|
||||
validation_date = posting_date if posting_date else gl_entries[0]["posting_date"]
|
||||
if immutable_ledger_enabled:
|
||||
validation_date = posting_date or frappe.form_dict.get("posting_date") or getdate()
|
||||
else:
|
||||
validation_date = posting_date if posting_date else gl_entries[0]["posting_date"]
|
||||
|
||||
check_freezing_date(validation_date, gl_entries[0]["company"], adv_adj)
|
||||
validate_against_pcv(is_opening, validation_date, gl_entries[0]["company"])
|
||||
|
||||
if partial_cancel:
|
||||
@@ -715,7 +717,7 @@ def make_reverse_gl_entries(
|
||||
|
||||
if immutable_ledger_enabled:
|
||||
new_gle["is_cancelled"] = 0
|
||||
new_gle["posting_date"] = frappe.form_dict.get("posting_date") or getdate()
|
||||
new_gle["posting_date"] = posting_date or frappe.form_dict.get("posting_date") or getdate()
|
||||
elif posting_date:
|
||||
new_gle["posting_date"] = posting_date
|
||||
|
||||
|
||||
@@ -582,12 +582,7 @@ def prepare_data(accounts, start_date, end_date, balance_must_be, companies, com
|
||||
total += flt(row[company])
|
||||
|
||||
row["has_value"] = has_value
|
||||
# when accumulating into the group company, that company's column already consolidates its
|
||||
# descendants, so summing every company column would double-count; use the group total directly.
|
||||
if filters.get("accumulated_in_group_company"):
|
||||
row["total"] = flt(row.get(filters.company, 0.0), 3)
|
||||
else:
|
||||
row["total"] = total
|
||||
row["total"] = total
|
||||
|
||||
data.append(row)
|
||||
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.utils import flt, today
|
||||
|
||||
from erpnext.accounts.report.consolidated_financial_statement.consolidated_financial_statement import (
|
||||
execute,
|
||||
)
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
PARENT_COMPANY = "Parent Group Company India"
|
||||
CHILD_COMPANY = "Child Company India"
|
||||
|
||||
|
||||
class TestConsolidatedFinancialStatement(ERPNextTestSuite):
|
||||
"""Consolidation is exercised via the bootstrap group of companies
|
||||
(`Parent Group Company India` with child `Child Company India`). Income and
|
||||
expense posted in the child company must surface in the report that is run
|
||||
for the parent (group) company."""
|
||||
|
||||
def setUp(self):
|
||||
self.fiscal_year = get_fiscal_year(today(), company=PARENT_COMPANY)[0]
|
||||
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"company": PARENT_COMPANY,
|
||||
"filter_based_on": "Fiscal Year",
|
||||
"from_fiscal_year": self.fiscal_year,
|
||||
"to_fiscal_year": self.fiscal_year,
|
||||
"periodicity": "Yearly",
|
||||
"include_default_book_entries": 1,
|
||||
}
|
||||
)
|
||||
filters.update(extra)
|
||||
return execute(filters)[1]
|
||||
|
||||
def post_journal_entry(self, debit_account, credit_account, amount):
|
||||
je = frappe.new_doc("Journal Entry")
|
||||
je.posting_date = today()
|
||||
je.company = CHILD_COMPANY
|
||||
je.set(
|
||||
"accounts",
|
||||
[
|
||||
{"account": debit_account, "debit_in_account_currency": amount},
|
||||
{"account": credit_account, "credit_in_account_currency": amount},
|
||||
],
|
||||
)
|
||||
je.save()
|
||||
je.submit()
|
||||
return je
|
||||
|
||||
def get_row(self, data, account_name_fragment):
|
||||
for row in data:
|
||||
if account_name_fragment in str(row.get("account_name") or ""):
|
||||
return row
|
||||
return None
|
||||
|
||||
def test_profit_and_loss_reflects_child_company_income(self):
|
||||
amount = 7000
|
||||
self.post_journal_entry("Cash - CCI", "Sales - CCI", amount)
|
||||
|
||||
data = self.run_report(report="Profit and Loss Statement", accumulated_in_group_company=0)
|
||||
|
||||
self.assertTrue(data, "Report returned no rows")
|
||||
|
||||
# child's Sales account is mapped onto the parent chart (Sales - PGCI)
|
||||
sales_row = self.get_row(data, "Sales")
|
||||
self.assertIsNotNone(sales_row, "Sales row missing from consolidated P&L")
|
||||
self.assertEqual(flt(sales_row.get(CHILD_COMPANY)), amount)
|
||||
|
||||
total_income_row = self.get_row(data, "Total Income (Credit)")
|
||||
self.assertIsNotNone(total_income_row, "Total Income row missing")
|
||||
self.assertGreaterEqual(flt(total_income_row.get("total")), amount)
|
||||
|
||||
def test_profit_and_loss_reflects_child_company_expense(self):
|
||||
amount = 3000
|
||||
self.post_journal_entry("Marketing Expenses - CCI", "Cash - CCI", amount)
|
||||
|
||||
data = self.run_report(report="Profit and Loss Statement", accumulated_in_group_company=0)
|
||||
|
||||
expense_row = self.get_row(data, "Marketing Expenses")
|
||||
self.assertIsNotNone(expense_row, "Marketing Expenses row missing from consolidated P&L")
|
||||
self.assertEqual(flt(expense_row.get(CHILD_COMPANY)), amount)
|
||||
|
||||
total_expense_row = self.get_row(data, "Total Expense (Debit)")
|
||||
self.assertIsNotNone(total_expense_row, "Total Expense row missing")
|
||||
self.assertGreaterEqual(flt(total_expense_row.get("total")), amount)
|
||||
|
||||
def test_accumulated_in_group_company_rolls_up_to_parent(self):
|
||||
"""With `accumulated_in_group_company`, the child's amount is also
|
||||
accumulated into the parent company column."""
|
||||
amount = 5000
|
||||
self.post_journal_entry("Cash - CCI", "Sales - CCI", amount)
|
||||
|
||||
data = self.run_report(report="Profit and Loss Statement", accumulated_in_group_company=1)
|
||||
|
||||
sales_row = self.get_row(data, "Sales")
|
||||
self.assertIsNotNone(sales_row)
|
||||
self.assertEqual(flt(sales_row.get(CHILD_COMPANY)), amount)
|
||||
# parent column picks up the child value when accumulated
|
||||
self.assertEqual(flt(sales_row.get(PARENT_COMPANY)), amount)
|
||||
# the total must equal the consolidated (group) value, not the sum of parent + child columns
|
||||
self.assertEqual(flt(sales_row.get("total")), amount)
|
||||
|
||||
def test_balance_sheet_executes_and_returns_rows(self):
|
||||
# posting income leaves a balancing entry in the child's Cash (Asset) account
|
||||
amount = 4000
|
||||
self.post_journal_entry("Cash - CCI", "Sales - CCI", amount)
|
||||
|
||||
data = self.run_report(report="Balance Sheet", accumulated_in_group_company=0)
|
||||
|
||||
self.assertTrue(data, "Balance Sheet returned no rows")
|
||||
cash_row = self.get_row(data, "Cash")
|
||||
self.assertIsNotNone(cash_row, "Cash asset row missing from consolidated Balance Sheet")
|
||||
self.assertGreaterEqual(flt(cash_row.get(CHILD_COMPANY)), amount)
|
||||
@@ -0,0 +1,89 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.report.delivered_items_to_be_billed.delivered_items_to_be_billed import execute
|
||||
from erpnext.stock.doctype.delivery_note.mapper import make_sales_invoice
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestDeliveredItemsToBeBilled(ERPNextTestSuite):
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"posting_date": "2026-06-30",
|
||||
}
|
||||
)
|
||||
filters.update(extra)
|
||||
return execute(filters)[1]
|
||||
|
||||
def stock_up_item(self):
|
||||
make_stock_entry(
|
||||
item_code="_Test Item",
|
||||
target="Stores - _TC",
|
||||
qty=20,
|
||||
basic_rate=100,
|
||||
posting_date="2026-05-25",
|
||||
)
|
||||
|
||||
def test_unbilled_delivery_note_appears(self):
|
||||
self.stock_up_item()
|
||||
dn = create_delivery_note(
|
||||
item_code="_Test Item",
|
||||
warehouse="Stores - _TC",
|
||||
qty=5,
|
||||
rate=300,
|
||||
customer="_Test Customer",
|
||||
posting_date="2026-06-01",
|
||||
)
|
||||
|
||||
rows = self.run_report(delivery_note=dn.name)
|
||||
self.assertEqual(len(rows), 1)
|
||||
|
||||
row = rows[0]
|
||||
self.assertEqual(row.name, dn.name)
|
||||
self.assertEqual(row.customer, "_Test Customer")
|
||||
self.assertEqual(row.item_code, "_Test Item")
|
||||
self.assertEqual(row.amount, 1500)
|
||||
self.assertEqual(row.billed_amount, 0)
|
||||
self.assertEqual(row.returned_amount, 0)
|
||||
self.assertEqual(row.pending_amount, 1500)
|
||||
|
||||
def test_fully_billed_delivery_note_drops_out(self):
|
||||
self.stock_up_item()
|
||||
dn = create_delivery_note(
|
||||
item_code="_Test Item",
|
||||
warehouse="Stores - _TC",
|
||||
qty=5,
|
||||
rate=300,
|
||||
customer="_Test Customer",
|
||||
posting_date="2026-06-01",
|
||||
)
|
||||
|
||||
self.assertEqual(len(self.run_report(delivery_note=dn.name)), 1)
|
||||
|
||||
si = make_sales_invoice(dn.name)
|
||||
si.posting_date = "2026-06-02"
|
||||
si.set_posting_time = 1
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
self.assertEqual(self.run_report(delivery_note=dn.name), [])
|
||||
|
||||
def test_date_filter_excludes_later_delivery_notes(self):
|
||||
self.stock_up_item()
|
||||
dn = create_delivery_note(
|
||||
item_code="_Test Item",
|
||||
warehouse="Stores - _TC",
|
||||
qty=5,
|
||||
rate=300,
|
||||
customer="_Test Customer",
|
||||
posting_date="2026-07-15",
|
||||
)
|
||||
|
||||
rows = self.run_report(delivery_note=dn.name, posting_date="2026-06-30")
|
||||
self.assertEqual(rows, [])
|
||||
@@ -84,7 +84,8 @@ def build_query_filters(filters: dict | None = None) -> list:
|
||||
qb_filters = []
|
||||
if filters:
|
||||
if filters.account:
|
||||
qb_filters.append(qb.Field("account").isin(filters.account))
|
||||
accounts = filters.account if isinstance(filters.account, list | tuple) else [filters.account]
|
||||
qb_filters.append(qb.Field("account").isin(accounts))
|
||||
|
||||
if filters.voucher_no:
|
||||
qb_filters.append(qb.Field("voucher_no").eq(filters.voucher_no))
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import qb
|
||||
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||
from erpnext.accounts.report.invalid_ledger_entries.invalid_ledger_entries import execute
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestInvalidLedgerEntries(ERPNextTestSuite):
|
||||
"""Tests for the Invalid Ledger Entries integrity report.
|
||||
|
||||
The report flags vouchers that still have *active* ledger entries
|
||||
(GL Entry with is_cancelled=0 or Payment Ledger Entry with delinked=0)
|
||||
in the given period, but whose source voucher document is no longer
|
||||
submitted (docstatus != 1). Such orphaned ledgers indicate corruption.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.company = "_Test Company"
|
||||
self.debit_account = "_Test Bank - _TC"
|
||||
self.credit_account = "_Test Cash - _TC"
|
||||
self.from_date = "2026-01-01"
|
||||
self.to_date = "2026-12-31"
|
||||
self.posting_date = "2026-06-01"
|
||||
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"company": self.company,
|
||||
"from_date": self.from_date,
|
||||
"to_date": self.to_date,
|
||||
}
|
||||
)
|
||||
filters.update(extra)
|
||||
return execute(filters)[1]
|
||||
|
||||
def make_submitted_jv(self):
|
||||
return make_journal_entry(
|
||||
self.debit_account,
|
||||
self.credit_account,
|
||||
amount=500,
|
||||
posting_date=self.posting_date,
|
||||
company=self.company,
|
||||
submit=True,
|
||||
)
|
||||
|
||||
def test_healthy_voucher_not_flagged(self):
|
||||
"""A normal balanced, submitted Journal Entry must NOT be flagged."""
|
||||
jv = self.make_submitted_jv()
|
||||
|
||||
# It genuinely posted active GL entries, so it is in scope of the scan.
|
||||
self.assertTrue(
|
||||
frappe.db.exists(
|
||||
"GL Entry",
|
||||
{"voucher_no": jv.name, "is_cancelled": 0, "company": self.company},
|
||||
)
|
||||
)
|
||||
|
||||
flagged = {row.get("voucher_no") for row in self.run_report()}
|
||||
self.assertNotIn(jv.name, flagged)
|
||||
|
||||
def test_orphaned_gl_entries_flagged(self):
|
||||
"""A voucher whose document was set non-submitted while its GL entries
|
||||
remain active (is_cancelled=0) must be flagged as invalid."""
|
||||
jv = self.make_submitted_jv()
|
||||
|
||||
# Corrupt the state: mark the source document as cancelled (docstatus=2)
|
||||
# without cancelling/removing its GL Entries. This is the exact orphaned
|
||||
# ledger condition the report detects.
|
||||
frappe.db.set_value("Journal Entry", jv.name, "docstatus", 2, update_modified=False)
|
||||
|
||||
data = self.run_report()
|
||||
|
||||
matching = [
|
||||
row
|
||||
for row in data
|
||||
if row.get("voucher_no") == jv.name and row.get("voucher_type") == "Journal Entry"
|
||||
]
|
||||
self.assertEqual(len(matching), 1, "Orphaned voucher should be flagged exactly once")
|
||||
self.assertEqual(matching[0]["voucher_type"], "Journal Entry")
|
||||
self.assertEqual(matching[0]["voucher_no"], jv.name)
|
||||
|
||||
def test_voucher_no_filter_scopes_scan(self):
|
||||
"""The voucher_no filter must restrict the scan to that voucher only."""
|
||||
orphan = self.make_submitted_jv()
|
||||
other = self.make_submitted_jv()
|
||||
frappe.db.set_value("Journal Entry", orphan.name, "docstatus", 2, update_modified=False)
|
||||
frappe.db.set_value("Journal Entry", other.name, "docstatus", 2, update_modified=False)
|
||||
|
||||
flagged = {row.get("voucher_no") for row in self.run_report(voucher_no=orphan.name)}
|
||||
self.assertIn(orphan.name, flagged)
|
||||
self.assertNotIn(other.name, flagged)
|
||||
|
||||
def test_account_filter_scopes_scan(self):
|
||||
"""The account filter (a MultiSelectList, so a list) must restrict the
|
||||
scan to vouchers touching one of the given accounts."""
|
||||
orphan = self.make_submitted_jv()
|
||||
frappe.db.set_value("Journal Entry", orphan.name, "docstatus", 2, update_modified=False)
|
||||
|
||||
# Filtering on an account the voucher touches -> flagged.
|
||||
flagged = {row.get("voucher_no") for row in self.run_report(account=[self.debit_account])}
|
||||
self.assertIn(orphan.name, flagged)
|
||||
|
||||
# Filtering on an unrelated account -> not in scope.
|
||||
unrelated = "Creditors - _TC"
|
||||
flagged = {row.get("voucher_no") for row in self.run_report(account=[unrelated])}
|
||||
self.assertNotIn(orphan.name, flagged)
|
||||
|
||||
def test_account_filter_accepts_a_scalar(self):
|
||||
"""A scalar (non-list) account filter must not crash the query."""
|
||||
orphan = self.make_submitted_jv()
|
||||
frappe.db.set_value("Journal Entry", orphan.name, "docstatus", 2, update_modified=False)
|
||||
|
||||
flagged = {row.get("voucher_no") for row in self.run_report(account=self.debit_account)}
|
||||
self.assertIn(orphan.name, flagged)
|
||||
|
||||
def test_period_filter_excludes_out_of_range(self):
|
||||
"""Vouchers posted outside the from/to window must not be scanned."""
|
||||
orphan = self.make_submitted_jv()
|
||||
frappe.db.set_value("Journal Entry", orphan.name, "docstatus", 2, update_modified=False)
|
||||
|
||||
flagged = {
|
||||
row.get("voucher_no") for row in self.run_report(from_date="2025-01-01", to_date="2025-12-31")
|
||||
}
|
||||
self.assertNotIn(orphan.name, flagged)
|
||||
|
||||
def test_cancelled_gl_entries_not_flagged(self):
|
||||
"""If the ledger entries are properly cancelled (is_cancelled=1), the
|
||||
voucher is out of scope even when its document is non-submitted."""
|
||||
jv = self.make_submitted_jv()
|
||||
|
||||
gle = qb.DocType("GL Entry")
|
||||
qb.update(gle).set(gle.is_cancelled, 1).where(gle.voucher_no == jv.name).run()
|
||||
frappe.db.set_value("Journal Entry", jv.name, "docstatus", 2, update_modified=False)
|
||||
|
||||
flagged = {row.get("voucher_no") for row in self.run_report()}
|
||||
self.assertNotIn(jv.name, flagged)
|
||||
|
||||
def test_missing_filters_raises(self):
|
||||
"""validate_filters must guard mandatory inputs."""
|
||||
self.assertRaises(frappe.ValidationError, execute, None)
|
||||
|
||||
bad = frappe._dict({"from_date": self.from_date, "to_date": self.to_date})
|
||||
self.assertRaises(frappe.ValidationError, execute, bad)
|
||||
|
||||
reversed_dates = frappe._dict(
|
||||
{"company": self.company, "from_date": self.to_date, "to_date": self.from_date}
|
||||
)
|
||||
self.assertRaises(frappe.ValidationError, execute, reversed_dates)
|
||||
@@ -21,6 +21,8 @@ def execute(filters=None):
|
||||
entries = get_entries(filters)
|
||||
invoice_details = get_invoice_posting_date_map(filters)
|
||||
|
||||
report = ReceivablePayableReport(filters)
|
||||
|
||||
data = []
|
||||
for d in entries:
|
||||
invoice = invoice_details.get(d.against_voucher_no) or frappe._dict()
|
||||
@@ -29,7 +31,9 @@ def execute(filters=None):
|
||||
d.update({"range1": 0, "range2": 0, "range3": 0, "range4": 0, "outstanding": payment_amount})
|
||||
|
||||
if d.against_voucher_no:
|
||||
ReceivablePayableReport(filters).get_ageing_data(invoice.posting_date, d)
|
||||
# age the payment by how long after the invoice it was made (payment date - invoice date)
|
||||
report.age_as_on = getdate(d.posting_date)
|
||||
report.get_ageing_data(invoice.posting_date, d)
|
||||
|
||||
row = [
|
||||
d.voucher_type,
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.utils import getdate
|
||||
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.accounts.report.payment_period_based_on_invoice_date.payment_period_based_on_invoice_date import (
|
||||
execute,
|
||||
)
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestPaymentPeriodBasedOnInvoiceDate(ERPNextTestSuite):
|
||||
"""Depth tests for the Payment Period Based On Invoice Date report.
|
||||
|
||||
The report lists Payment Ledger Entries against invoices and buckets the paid
|
||||
amount by the payment period -- how long after the invoice the payment was made
|
||||
(payment date - invoice date) -- into ranges: range1 (0-30), range2 (30-60),
|
||||
range3 (60-90), range4 (90 Above).
|
||||
"""
|
||||
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"payment_type": "Incoming",
|
||||
"party_type": "Customer",
|
||||
"from_date": "2026-01-01",
|
||||
"to_date": "2026-12-31",
|
||||
}
|
||||
)
|
||||
filters.update(extra)
|
||||
return execute(filters)
|
||||
|
||||
def find_payment_row(self, data, payment_name):
|
||||
# Row shape (positional): payment_document, payment_entry(voucher_no),
|
||||
# party_type, party, posting_date, invoice(against_voucher_no),
|
||||
# invoice_posting_date, due_date, amount, remarks, age,
|
||||
# range1, range2, range3, range4, [delay_in_payment]
|
||||
for row in data:
|
||||
if row[1] == payment_name:
|
||||
return row
|
||||
return None
|
||||
|
||||
def pay_invoice(self, invoice, payment_date):
|
||||
pe = get_payment_entry("Sales Invoice", invoice.name)
|
||||
pe.posting_date = payment_date
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = payment_date
|
||||
pe.submit()
|
||||
return pe
|
||||
|
||||
def test_paid_amount_lands_in_0_30_bucket(self):
|
||||
# invoice 2026-06-01, paid 2026-06-20 -> 19 days after -> 0-30 bucket
|
||||
invoice = create_sales_invoice(customer="_Test Customer", rate=1000, posting_date="2026-06-01")
|
||||
payment = self.pay_invoice(invoice, "2026-06-20")
|
||||
|
||||
columns, data = self.run_report()
|
||||
|
||||
row = self.find_payment_row(data, payment.name)
|
||||
self.assertIsNotNone(row, "Payment row not found in report output")
|
||||
|
||||
# Positional assertions on the row shape.
|
||||
self.assertEqual(row[2], "Customer")
|
||||
self.assertEqual(row[4], getdate("2026-06-20")) # payment posting date
|
||||
self.assertEqual(row[5], invoice.name) # against invoice
|
||||
self.assertEqual(row[6], getdate("2026-06-01")) # invoice posting date
|
||||
self.assertEqual(row[8], 1000) # amount
|
||||
self.assertEqual(row[10], 19) # age = payment date - invoice date
|
||||
|
||||
# Buckets: 0-30 filled, others empty.
|
||||
self.assertEqual(row[11], 1000) # range1 (0-30)
|
||||
self.assertEqual(row[12], 0) # range2 (30-60)
|
||||
self.assertEqual(row[13], 0) # range3 (60-90)
|
||||
self.assertEqual(row[14], 0) # range4 (90 Above)
|
||||
|
||||
def test_paid_amount_lands_in_30_60_bucket(self):
|
||||
# invoice 2026-06-01, paid 2026-07-16 -> 45 days after -> 30-60 bucket
|
||||
invoice = create_sales_invoice(customer="_Test Customer 1", rate=1000, posting_date="2026-06-01")
|
||||
payment = self.pay_invoice(invoice, "2026-07-16")
|
||||
|
||||
columns, data = self.run_report()
|
||||
|
||||
row = self.find_payment_row(data, payment.name)
|
||||
self.assertIsNotNone(row, "Payment row not found in report output")
|
||||
|
||||
self.assertEqual(row[8], 1000) # amount
|
||||
self.assertEqual(row[10], 45) # age = payment date - invoice date
|
||||
# Buckets: 30-60 filled, others empty.
|
||||
self.assertEqual(row[11], 0) # range1 (0-30)
|
||||
self.assertEqual(row[12], 1000) # range2 (30-60)
|
||||
self.assertEqual(row[13], 0) # range3 (60-90)
|
||||
self.assertEqual(row[14], 0) # range4 (90 Above)
|
||||
|
||||
def test_columns_expose_expected_age_buckets(self):
|
||||
columns, _data = self.run_report()
|
||||
labels_by_fieldname = {c["fieldname"]: c["label"] for c in columns}
|
||||
self.assertEqual(labels_by_fieldname["range1"], "0-30")
|
||||
self.assertEqual(labels_by_fieldname["range2"], "30-60")
|
||||
self.assertEqual(labels_by_fieldname["range3"], "60-90")
|
||||
self.assertEqual(labels_by_fieldname["range4"], "90 Above")
|
||||
# Sales Invoice link for Incoming payments.
|
||||
invoice_col = next(c for c in columns if c["fieldname"] == "invoice")
|
||||
self.assertEqual(invoice_col["options"], "Sales Invoice")
|
||||
|
||||
def test_invalid_payment_type_party_type_combo_throws(self):
|
||||
# Incoming + Supplier is invalid.
|
||||
self.assertRaises(
|
||||
frappe.ValidationError,
|
||||
self.run_report,
|
||||
payment_type="Incoming",
|
||||
party_type="Supplier",
|
||||
)
|
||||
# Outgoing + Customer is invalid.
|
||||
self.assertRaises(
|
||||
frappe.ValidationError,
|
||||
self.run_report,
|
||||
payment_type="Outgoing",
|
||||
party_type="Customer",
|
||||
)
|
||||
@@ -0,0 +1,171 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.accounts.report.purchase_invoice_trends.purchase_invoice_trends import execute
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
FISCAL_YEAR = "_Test Fiscal Year 2026"
|
||||
COMPANY = "_Test Company"
|
||||
SUPPLIER = "_Test Supplier"
|
||||
ITEM = "_Test Item"
|
||||
POSTING_DATE = "2026-06-01"
|
||||
|
||||
|
||||
def make_dated_purchase_invoice(qty, rate):
|
||||
# make_purchase_invoice ignores posting_date unless posting time is explicitly set, so build the
|
||||
# invoice unsubmitted, pin the posting date, then submit to land it in the intended period bucket.
|
||||
pi = make_purchase_invoice(
|
||||
supplier=SUPPLIER, item_code=ITEM, qty=qty, rate=rate, posting_date=POSTING_DATE, do_not_submit=1
|
||||
)
|
||||
pi.set_posting_time = 1
|
||||
pi.posting_date = POSTING_DATE
|
||||
pi.submit()
|
||||
return pi
|
||||
|
||||
|
||||
class TestPurchaseInvoiceTrends(ERPNextTestSuite):
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"company": COMPANY,
|
||||
"fiscal_year": FISCAL_YEAR,
|
||||
"period": "Yearly",
|
||||
"based_on": "Item",
|
||||
}
|
||||
)
|
||||
filters.update(extra)
|
||||
columns, data = execute(filters)
|
||||
labels = [c.split(":")[0] if isinstance(c, str) else c.get("label") for c in columns]
|
||||
return labels, data
|
||||
|
||||
@staticmethod
|
||||
def _cell(labels, row, label):
|
||||
return row[labels.index(label)]
|
||||
|
||||
def _find_row(self, data, key):
|
||||
for row in data:
|
||||
if row and row[0] == key:
|
||||
return row
|
||||
return None
|
||||
|
||||
def test_yearly_item_qty_and_amount(self):
|
||||
labels_before, data_before = self.run_report()
|
||||
before = self._find_row(data_before, ITEM)
|
||||
|
||||
qty, rate = 4, 250
|
||||
make_dated_purchase_invoice(qty, rate)
|
||||
|
||||
labels, data = self.run_report()
|
||||
self.assertIn("Item", labels)
|
||||
self.assertIn("Item Name", labels)
|
||||
self.assertIn("Currency", labels)
|
||||
self.assertIn("Total(Qty)", labels)
|
||||
self.assertIn("Total(Amt)", labels)
|
||||
# Yearly period bucket uses the fiscal year name as the label prefix
|
||||
self.assertIn(f"{FISCAL_YEAR} (Qty)", labels)
|
||||
self.assertIn(f"{FISCAL_YEAR} (Amt)", labels)
|
||||
|
||||
row = self._find_row(data, ITEM)
|
||||
self.assertIsNotNone(row)
|
||||
|
||||
before_qty = self._cell(labels_before, before, f"{FISCAL_YEAR} (Qty)") if before else 0
|
||||
before_amt = self._cell(labels_before, before, f"{FISCAL_YEAR} (Amt)") if before else 0
|
||||
before_tqty = self._cell(labels_before, before, "Total(Qty)") if before else 0
|
||||
before_tamt = self._cell(labels_before, before, "Total(Amt)") if before else 0
|
||||
|
||||
self.assertEqual(self._cell(labels, row, f"{FISCAL_YEAR} (Qty)") - before_qty, qty)
|
||||
self.assertEqual(self._cell(labels, row, f"{FISCAL_YEAR} (Amt)") - before_amt, qty * rate)
|
||||
self.assertEqual(self._cell(labels, row, "Total(Qty)") - before_tqty, qty)
|
||||
self.assertEqual(self._cell(labels, row, "Total(Amt)") - before_tamt, qty * rate)
|
||||
|
||||
def test_monthly_bucket(self):
|
||||
labels_before, data_before = self.run_report(period="Monthly")
|
||||
before = self._find_row(data_before, ITEM)
|
||||
|
||||
qty, rate = 3, 100
|
||||
make_dated_purchase_invoice(qty, rate)
|
||||
|
||||
labels, data = self.run_report(period="Monthly")
|
||||
# posting_date 2026-06-01 -> June bucket
|
||||
self.assertIn("Jun (Qty)", labels)
|
||||
self.assertIn("Jun (Amt)", labels)
|
||||
|
||||
row = self._find_row(data, ITEM)
|
||||
before_qty = self._cell(labels_before, before, "Jun (Qty)") if before else 0
|
||||
before_tamt = self._cell(labels_before, before, "Total(Amt)") if before else 0
|
||||
|
||||
self.assertEqual(self._cell(labels, row, "Jun (Qty)") - before_qty, qty)
|
||||
self.assertEqual(self._cell(labels, row, "Total(Amt)") - before_tamt, qty * rate)
|
||||
|
||||
def test_quarterly_bucket(self):
|
||||
labels_before, data_before = self.run_report(period="Quarterly")
|
||||
before = self._find_row(data_before, ITEM)
|
||||
|
||||
qty, rate = 2, 150
|
||||
make_dated_purchase_invoice(qty, rate)
|
||||
|
||||
labels, data = self.run_report(period="Quarterly")
|
||||
# 2026-06-01 falls in the Apr-Jun quarter
|
||||
self.assertIn("Apr-Jun (Qty)", labels)
|
||||
self.assertIn("Apr-Jun (Amt)", labels)
|
||||
|
||||
row = self._find_row(data, ITEM)
|
||||
before_qty = self._cell(labels_before, before, "Apr-Jun (Qty)") if before else 0
|
||||
before_amt = self._cell(labels_before, before, "Apr-Jun (Amt)") if before else 0
|
||||
|
||||
self.assertEqual(self._cell(labels, row, "Apr-Jun (Qty)") - before_qty, qty)
|
||||
self.assertEqual(self._cell(labels, row, "Apr-Jun (Amt)") - before_amt, qty * rate)
|
||||
|
||||
def test_based_on_supplier(self):
|
||||
labels_before, data_before = self.run_report(based_on="Supplier")
|
||||
before = self._find_row(data_before, SUPPLIER)
|
||||
|
||||
qty, rate = 5, 200
|
||||
make_dated_purchase_invoice(qty, rate)
|
||||
|
||||
labels, data = self.run_report(based_on="Supplier")
|
||||
self.assertIn("Supplier", labels)
|
||||
self.assertIn("Supplier Name", labels)
|
||||
self.assertIn("Supplier Group", labels)
|
||||
|
||||
row = self._find_row(data, SUPPLIER)
|
||||
self.assertIsNotNone(row)
|
||||
|
||||
before_tqty = self._cell(labels_before, before, "Total(Qty)") if before else 0
|
||||
before_tamt = self._cell(labels_before, before, "Total(Amt)") if before else 0
|
||||
|
||||
self.assertEqual(self._cell(labels, row, "Total(Qty)") - before_tqty, qty)
|
||||
self.assertEqual(self._cell(labels, row, "Total(Amt)") - before_tamt, qty * rate)
|
||||
|
||||
def test_group_by_item_under_supplier(self):
|
||||
labels_before, data_before = self.run_report(based_on="Supplier", group_by="Item")
|
||||
# group_by inserts an "Item" column; the item breakdown row carries the item key there
|
||||
item_idx = labels_before.index("Item")
|
||||
before = None
|
||||
for r in data_before:
|
||||
if r and r[0] != SUPPLIER and r[item_idx] == ITEM:
|
||||
before = r
|
||||
break
|
||||
|
||||
qty, rate = 6, 300
|
||||
make_dated_purchase_invoice(qty, rate)
|
||||
|
||||
labels, data = self.run_report(based_on="Supplier", group_by="Item")
|
||||
self.assertIn("Item", labels)
|
||||
|
||||
item_idx = labels.index("Item")
|
||||
row = None
|
||||
for r in data:
|
||||
if r and r[0] != SUPPLIER and r[0] != "'Total'" and r[item_idx] == ITEM:
|
||||
row = r
|
||||
break
|
||||
self.assertIsNotNone(row)
|
||||
|
||||
before_tqty = self._cell(labels_before, before, "Total(Qty)") if before else 0
|
||||
before_tamt = self._cell(labels_before, before, "Total(Amt)") if before else 0
|
||||
|
||||
self.assertEqual(self._cell(labels, row, "Total(Qty)") - before_tqty, qty)
|
||||
self.assertEqual(self._cell(labels, row, "Total(Amt)") - before_tamt, qty * rate)
|
||||
@@ -0,0 +1,101 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.report.received_items_to_be_billed.received_items_to_be_billed import execute
|
||||
from erpnext.stock.doctype.purchase_receipt.mapper import make_purchase_invoice as make_pi_from_pr
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestReceivedItemsToBeBilled(ERPNextTestSuite):
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"posting_date": "2026-06-30",
|
||||
}
|
||||
)
|
||||
filters.update(extra)
|
||||
return execute(filters)[1]
|
||||
|
||||
def get_row(self, data, purchase_receipt):
|
||||
matches = [row for row in data if row.get("name") == purchase_receipt]
|
||||
return matches[0] if matches else None
|
||||
|
||||
def test_unbilled_receipt_appears_with_pending_amount(self):
|
||||
pr = make_purchase_receipt(
|
||||
item_code="_Test Item",
|
||||
qty=5,
|
||||
rate=200,
|
||||
supplier="_Test Supplier",
|
||||
posting_date="2026-06-01",
|
||||
)
|
||||
|
||||
row = self.get_row(self.run_report(), pr.name)
|
||||
|
||||
self.assertIsNotNone(row, "Unbilled Purchase Receipt should appear in the report")
|
||||
self.assertEqual(row.get("supplier"), "_Test Supplier")
|
||||
self.assertEqual(row.get("item_code"), "_Test Item")
|
||||
self.assertEqual(row.get("amount"), 1000.0)
|
||||
self.assertEqual(row.get("billed_amount"), 0.0)
|
||||
self.assertEqual(row.get("returned_amount"), 0.0)
|
||||
self.assertEqual(row.get("pending_amount"), 1000.0)
|
||||
|
||||
def test_billed_receipt_drops_out_of_report(self):
|
||||
pr = make_purchase_receipt(
|
||||
item_code="_Test Item",
|
||||
qty=5,
|
||||
rate=200,
|
||||
supplier="_Test Supplier",
|
||||
posting_date="2026-06-01",
|
||||
)
|
||||
|
||||
self.assertIsNotNone(self.get_row(self.run_report(), pr.name))
|
||||
|
||||
pi = make_pi_from_pr(pr.name)
|
||||
pi.set_posting_time = 1
|
||||
pi.posting_date = "2026-06-02"
|
||||
pi.submit()
|
||||
|
||||
self.assertIsNone(
|
||||
self.get_row(self.run_report(), pr.name),
|
||||
"Fully billed Purchase Receipt should no longer appear in the report",
|
||||
)
|
||||
|
||||
def test_reference_field_filter_limits_to_single_receipt(self):
|
||||
first_pr = make_purchase_receipt(
|
||||
item_code="_Test Item",
|
||||
qty=5,
|
||||
rate=200,
|
||||
supplier="_Test Supplier",
|
||||
posting_date="2026-06-01",
|
||||
)
|
||||
second_pr = make_purchase_receipt(
|
||||
item_code="_Test Item",
|
||||
qty=3,
|
||||
rate=100,
|
||||
supplier="_Test Supplier",
|
||||
posting_date="2026-06-01",
|
||||
)
|
||||
|
||||
data = self.run_report(purchase_receipt=first_pr.name)
|
||||
|
||||
self.assertIsNotNone(self.get_row(data, first_pr.name))
|
||||
self.assertIsNone(self.get_row(data, second_pr.name))
|
||||
|
||||
def test_posting_date_cutoff_excludes_later_receipts(self):
|
||||
pr = make_purchase_receipt(
|
||||
item_code="_Test Item",
|
||||
qty=5,
|
||||
rate=200,
|
||||
supplier="_Test Supplier",
|
||||
posting_date="2026-06-15",
|
||||
)
|
||||
|
||||
self.assertIsNone(
|
||||
self.get_row(self.run_report(posting_date="2026-06-01"), pr.name),
|
||||
"Receipt dated after the cutoff should be excluded",
|
||||
)
|
||||
self.assertIsNotNone(self.get_row(self.run_report(posting_date="2026-06-30"), pr.name))
|
||||
@@ -0,0 +1,118 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.accounts.report.sales_invoice_trends.sales_invoice_trends import execute
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
FISCAL_YEAR = "_Test Fiscal Year 2026"
|
||||
POSTING_DATE = "2026-06-01"
|
||||
|
||||
|
||||
class TestSalesInvoiceTrends(ERPNextTestSuite):
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"fiscal_year": FISCAL_YEAR,
|
||||
"based_on": "Item",
|
||||
"period": "Yearly",
|
||||
}
|
||||
)
|
||||
filters.update(extra)
|
||||
columns, data = execute(filters)
|
||||
labels = [c.split(":")[0] if isinstance(c, str) else c.get("label") for c in columns]
|
||||
return labels, data
|
||||
|
||||
def _cell(self, data, key_label, key_value, col_label, labels):
|
||||
"""Return the value at column `col_label` for the row whose first-column
|
||||
value equals `key_value`, or 0 if that row does not exist yet."""
|
||||
key_idx = labels.index(key_label)
|
||||
col_idx = labels.index(col_label)
|
||||
for row in data:
|
||||
if row[key_idx] == key_value:
|
||||
return row[col_idx] or 0
|
||||
return 0
|
||||
|
||||
def test_yearly_item_amount_and_total(self):
|
||||
# Yearly period => a single "<FY> (Qty)"/"(Amt)" bucket, plus Total(Qty)/Total(Amt).
|
||||
labels, before = self.run_report()
|
||||
qty_col = f"{FISCAL_YEAR} (Qty)"
|
||||
amt_col = f"{FISCAL_YEAR} (Amt)"
|
||||
before_qty = self._cell(before, "Item", "_Test Item", qty_col, labels)
|
||||
before_amt = self._cell(before, "Item", "_Test Item", amt_col, labels)
|
||||
before_tot_qty = self._cell(before, "Item", "_Test Item", "Total(Qty)", labels)
|
||||
before_tot_amt = self._cell(before, "Item", "_Test Item", "Total(Amt)", labels)
|
||||
|
||||
create_sales_invoice(item="_Test Item", qty=4, rate=200, posting_date=POSTING_DATE)
|
||||
|
||||
labels, after = self.run_report()
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", qty_col, labels) - before_qty, 4)
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", amt_col, labels) - before_amt, 800)
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", "Total(Qty)", labels) - before_tot_qty, 4)
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", "Total(Amt)", labels) - before_tot_amt, 800)
|
||||
|
||||
def test_monthly_lands_in_june_bucket(self):
|
||||
# Monthly period => one bucket per month; a 2026-06-01 invoice hits "Jun (Qty)"/"(Amt)".
|
||||
labels, before = self.run_report(period="Monthly")
|
||||
before_qty = self._cell(before, "Item", "_Test Item", "Jun (Qty)", labels)
|
||||
before_amt = self._cell(before, "Item", "_Test Item", "Jun (Amt)", labels)
|
||||
before_tot = self._cell(before, "Item", "_Test Item", "Total(Amt)", labels)
|
||||
|
||||
create_sales_invoice(item="_Test Item", qty=3, rate=100, posting_date=POSTING_DATE)
|
||||
|
||||
labels, after = self.run_report(period="Monthly")
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", "Jun (Qty)", labels) - before_qty, 3)
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", "Jun (Amt)", labels) - before_amt, 300)
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", "Total(Amt)", labels) - before_tot, 300)
|
||||
# Nothing should leak into an unrelated month.
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", "Jan (Amt)", labels), 0)
|
||||
|
||||
def test_quarterly_lands_in_apr_jun_bucket(self):
|
||||
# Quarterly period over a Jan-Dec fiscal year => Apr-Jun is the 2nd quarter; June lands there.
|
||||
labels, before = self.run_report(period="Quarterly")
|
||||
before_qty = self._cell(before, "Item", "_Test Item", "Apr-Jun (Qty)", labels)
|
||||
before_amt = self._cell(before, "Item", "_Test Item", "Apr-Jun (Amt)", labels)
|
||||
|
||||
create_sales_invoice(item="_Test Item", qty=5, rate=50, posting_date=POSTING_DATE)
|
||||
|
||||
labels, after = self.run_report(period="Quarterly")
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", "Apr-Jun (Qty)", labels) - before_qty, 5)
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", "Apr-Jun (Amt)", labels) - before_amt, 250)
|
||||
# Jan-Mar quarter must stay untouched.
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", "Jan-Mar (Amt)", labels), 0)
|
||||
|
||||
def test_based_on_customer_total(self):
|
||||
# based_on=Customer => first column is "Customer"; the customer's Total(Amt) reflects the sale.
|
||||
labels, before = self.run_report(based_on="Customer")
|
||||
before_tot_qty = self._cell(before, "Customer", "_Test Customer", "Total(Qty)", labels)
|
||||
before_tot_amt = self._cell(before, "Customer", "_Test Customer", "Total(Amt)", labels)
|
||||
|
||||
create_sales_invoice(
|
||||
customer="_Test Customer", item="_Test Item", qty=2, rate=300, posting_date=POSTING_DATE
|
||||
)
|
||||
|
||||
labels, after = self.run_report(based_on="Customer")
|
||||
self.assertEqual(
|
||||
self._cell(after, "Customer", "_Test Customer", "Total(Qty)", labels) - before_tot_qty, 2
|
||||
)
|
||||
self.assertEqual(
|
||||
self._cell(after, "Customer", "_Test Customer", "Total(Amt)", labels) - before_tot_amt, 600
|
||||
)
|
||||
|
||||
def test_group_by_item_under_customer(self):
|
||||
# based_on=Customer + group_by=Item inserts an "Item" breakdown column before the period
|
||||
# buckets; the per-item detail row carries the item key and the amount for that customer/item.
|
||||
labels, before = self.run_report(based_on="Customer", group_by="Item")
|
||||
# In group_by mode the detail rows key off the group_by column ("Item"), so snapshot by item.
|
||||
before_amt = self._cell(before, "Item", "_Test Item", "Total(Amt)", labels)
|
||||
|
||||
create_sales_invoice(
|
||||
customer="_Test Customer", item="_Test Item", qty=6, rate=100, posting_date=POSTING_DATE
|
||||
)
|
||||
|
||||
labels, after = self.run_report(based_on="Customer", group_by="Item")
|
||||
self.assertIn("Item", labels)
|
||||
self.assertEqual(self._cell(after, "Item", "_Test Item", "Total(Amt)", labels) - before_amt, 600)
|
||||
@@ -15,8 +15,6 @@ def execute(filters=None):
|
||||
|
||||
columns = get_columns(filters)
|
||||
|
||||
filters.get("date")
|
||||
|
||||
data = []
|
||||
|
||||
if not filters.get("shareholder"):
|
||||
@@ -24,7 +22,7 @@ def execute(filters=None):
|
||||
else:
|
||||
share_type, no_of_shares, rate, amount = 1, 2, 3, 4
|
||||
|
||||
all_shares = get_all_shares(filters.get("shareholder"))
|
||||
all_shares = get_all_shares(filters.get("shareholder"), filters.get("date"))
|
||||
for share_entry in all_shares:
|
||||
row = False
|
||||
for datum in data:
|
||||
@@ -63,5 +61,28 @@ def get_columns(filters):
|
||||
return columns
|
||||
|
||||
|
||||
def get_all_shares(shareholder):
|
||||
return frappe.get_doc("Shareholder", shareholder).share_balance
|
||||
def get_all_shares(shareholder, date):
|
||||
"""Share movements for the shareholder up to (and including) `date`, signed by direction:
|
||||
shares received are positive, shares transferred/sold out are negative."""
|
||||
transfers = frappe.get_all(
|
||||
"Share Transfer",
|
||||
filters={"docstatus": 1, "date": ("<=", date)},
|
||||
fields=["share_type", "no_of_shares", "rate", "amount", "from_shareholder", "to_shareholder"],
|
||||
order_by="date",
|
||||
)
|
||||
|
||||
shares = []
|
||||
for transfer in transfers:
|
||||
if transfer.to_shareholder == shareholder:
|
||||
shares.append(transfer)
|
||||
elif transfer.from_shareholder == shareholder:
|
||||
shares.append(
|
||||
frappe._dict(
|
||||
share_type=transfer.share_type,
|
||||
no_of_shares=-transfer.no_of_shares,
|
||||
rate=transfer.rate,
|
||||
amount=-transfer.amount,
|
||||
)
|
||||
)
|
||||
|
||||
return shares
|
||||
|
||||
177
erpnext/accounts/report/share_balance/test_share_balance.py
Normal file
177
erpnext/accounts/report/share_balance/test_share_balance.py
Normal file
@@ -0,0 +1,177 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.report.share_balance.share_balance import execute
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
COMPANY = "_Test Company"
|
||||
|
||||
|
||||
class TestShareBalanceReport(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
self.share_type = create_share_type("_Test Share Balance Equity")
|
||||
self.shareholder = create_shareholder("_Test Share Balance Holder", COMPANY)
|
||||
|
||||
def test_date_filter_is_mandatory(self):
|
||||
self.assertRaises(frappe.ValidationError, execute, frappe._dict({"shareholder": self.shareholder}))
|
||||
|
||||
def test_no_shareholder_returns_empty_data(self):
|
||||
# `shareholder` is optional; without it the report yields no rows.
|
||||
columns, data = execute(frappe._dict({"date": "2026-06-01", "company": COMPANY}))
|
||||
self.assertEqual(data, [])
|
||||
self.assertEqual(len(columns), 5)
|
||||
|
||||
def test_balance_after_issue(self):
|
||||
create_share_transfer(
|
||||
transfer_type="Issue",
|
||||
to_shareholder=self.shareholder,
|
||||
share_type=self.share_type,
|
||||
from_no=1,
|
||||
to_no=100,
|
||||
no_of_shares=100,
|
||||
rate=10,
|
||||
date="2026-06-01",
|
||||
)
|
||||
|
||||
row = self.get_row(date="2026-06-05")
|
||||
self.assertEqual(row[0], self.shareholder)
|
||||
self.assertEqual(row[1], self.share_type)
|
||||
self.assertEqual(row[2], 100) # no_of_shares
|
||||
self.assertEqual(row[3], 10) # average rate
|
||||
self.assertEqual(row[4], 1000) # amount = 100 * 10
|
||||
|
||||
def test_balance_increases_on_second_issue(self):
|
||||
create_share_transfer(
|
||||
transfer_type="Issue",
|
||||
to_shareholder=self.shareholder,
|
||||
share_type=self.share_type,
|
||||
from_no=1,
|
||||
to_no=100,
|
||||
no_of_shares=100,
|
||||
rate=10,
|
||||
date="2026-06-01",
|
||||
)
|
||||
create_share_transfer(
|
||||
transfer_type="Issue",
|
||||
to_shareholder=self.shareholder,
|
||||
share_type=self.share_type,
|
||||
from_no=101,
|
||||
to_no=200,
|
||||
no_of_shares=100,
|
||||
rate=20,
|
||||
date="2026-06-10",
|
||||
)
|
||||
|
||||
# The report groups by share type, summing shares and amount and
|
||||
# recomputing the average rate: (1000 + 2000) / 200 = 15.
|
||||
row = self.get_row(date="2026-06-15")
|
||||
self.assertEqual(row[2], 200)
|
||||
self.assertEqual(row[3], 15)
|
||||
self.assertEqual(row[4], 3000)
|
||||
|
||||
def test_balance_reduces_after_transfer_out(self):
|
||||
other_holder = create_shareholder("_Test Share Balance Holder 2", COMPANY)
|
||||
create_share_transfer(
|
||||
transfer_type="Issue",
|
||||
to_shareholder=self.shareholder,
|
||||
share_type=self.share_type,
|
||||
from_no=1,
|
||||
to_no=100,
|
||||
no_of_shares=100,
|
||||
rate=10,
|
||||
date="2026-06-01",
|
||||
)
|
||||
create_share_transfer(
|
||||
transfer_type="Transfer",
|
||||
from_shareholder=self.shareholder,
|
||||
to_shareholder=other_holder,
|
||||
share_type=self.share_type,
|
||||
from_no=1,
|
||||
to_no=40,
|
||||
no_of_shares=40,
|
||||
rate=10,
|
||||
date="2026-06-10",
|
||||
)
|
||||
|
||||
row = self.get_row(date="2026-06-15")
|
||||
self.assertEqual(row[2], 60) # 100 issued - 40 transferred out
|
||||
self.assertEqual(row[4], 600)
|
||||
|
||||
other_row = self.get_row(date="2026-06-15", shareholder=other_holder)
|
||||
self.assertEqual(other_row[2], 40)
|
||||
self.assertEqual(other_row[4], 400)
|
||||
|
||||
def test_as_on_date_before_issue_shows_no_holding(self):
|
||||
# the report is as-on `date`: before any share transfer, the shareholder holds nothing
|
||||
create_share_transfer(
|
||||
transfer_type="Issue",
|
||||
to_shareholder=self.shareholder,
|
||||
share_type=self.share_type,
|
||||
from_no=1,
|
||||
to_no=100,
|
||||
no_of_shares=100,
|
||||
rate=10,
|
||||
date="2026-06-01",
|
||||
)
|
||||
|
||||
data = execute(
|
||||
frappe._dict({"date": "2026-05-01", "company": COMPANY, "shareholder": self.shareholder})
|
||||
)[1]
|
||||
self.assertEqual(data, [])
|
||||
|
||||
def test_as_on_date_reflects_holding_up_to_that_date(self):
|
||||
# two issues on different dates; an as-on date between them sees only the first
|
||||
create_share_transfer(
|
||||
transfer_type="Issue",
|
||||
to_shareholder=self.shareholder,
|
||||
share_type=self.share_type,
|
||||
from_no=1,
|
||||
to_no=100,
|
||||
no_of_shares=100,
|
||||
rate=10,
|
||||
date="2026-06-01",
|
||||
)
|
||||
create_share_transfer(
|
||||
transfer_type="Issue",
|
||||
to_shareholder=self.shareholder,
|
||||
share_type=self.share_type,
|
||||
from_no=101,
|
||||
to_no=200,
|
||||
no_of_shares=100,
|
||||
rate=20,
|
||||
date="2026-06-10",
|
||||
)
|
||||
|
||||
self.assertEqual(self.get_row(date="2026-06-05")[2], 100) # only the first issue
|
||||
self.assertEqual(self.get_row(date="2026-06-15")[2], 200) # both issues
|
||||
|
||||
def get_row(self, date, shareholder=None):
|
||||
filters = frappe._dict(
|
||||
{"date": date, "company": COMPANY, "shareholder": shareholder or self.shareholder}
|
||||
)
|
||||
data = execute(filters)[1]
|
||||
holdings = [r for r in data if r[1] == self.share_type]
|
||||
self.assertEqual(len(holdings), 1, f"Expected one row for share type, got: {data}")
|
||||
return holdings[0]
|
||||
|
||||
|
||||
def create_share_type(title):
|
||||
if not frappe.db.exists("Share Type", title):
|
||||
frappe.get_doc({"doctype": "Share Type", "title": title}).insert()
|
||||
return title
|
||||
|
||||
|
||||
def create_shareholder(title, company):
|
||||
shareholder = frappe.get_doc({"doctype": "Shareholder", "title": title, "company": company}).insert()
|
||||
return shareholder.name
|
||||
|
||||
|
||||
def create_share_transfer(**kwargs):
|
||||
kwargs.setdefault("company", COMPANY)
|
||||
kwargs.setdefault("asset_account", "Cash - _TC")
|
||||
kwargs.setdefault("equity_or_liability_account", "Creditors - _TC")
|
||||
transfer = frappe.get_doc({"doctype": "Share Transfer", **kwargs})
|
||||
transfer.submit()
|
||||
return transfer
|
||||
@@ -0,0 +1,63 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||
from erpnext.accounts.report.voucher_wise_balance.voucher_wise_balance import execute
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestVoucherWiseBalance(ERPNextTestSuite):
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"from_date": "2026-01-01",
|
||||
"to_date": "2026-12-31",
|
||||
}
|
||||
)
|
||||
filters.update(extra)
|
||||
return execute(filters)[1]
|
||||
|
||||
def find_row(self, data, voucher_no):
|
||||
for row in data:
|
||||
if row.get("voucher_no") == voucher_no:
|
||||
return row
|
||||
return None
|
||||
|
||||
def test_balanced_voucher_not_flagged(self):
|
||||
jv = make_journal_entry(
|
||||
"Sales - _TC", "_Test Bank - _TC", 1000, submit=True, posting_date="2026-06-01"
|
||||
)
|
||||
|
||||
data = self.run_report()
|
||||
self.assertIsNone(
|
||||
self.find_row(data, jv.name),
|
||||
msg="A balanced voucher (debit == credit) must not be flagged.",
|
||||
)
|
||||
|
||||
def test_imbalanced_voucher_flagged(self):
|
||||
jv = make_journal_entry(
|
||||
"Sales - _TC", "_Test Bank - _TC", 1000, submit=True, posting_date="2026-06-01"
|
||||
)
|
||||
|
||||
# Tamper one GL Entry: drop the debit side so debit != credit for this voucher.
|
||||
gle_name = frappe.db.get_value(
|
||||
"GL Entry",
|
||||
{"voucher_no": jv.name, "is_cancelled": 0, "debit": [">", 0]},
|
||||
"name",
|
||||
)
|
||||
self.assertIsNotNone(gle_name, msg="Expected a debit GL Entry for the journal entry.")
|
||||
frappe.db.set_value("GL Entry", gle_name, {"debit": 400, "debit_in_account_currency": 400})
|
||||
|
||||
data = self.run_report()
|
||||
row = self.find_row(data, jv.name)
|
||||
self.assertIsNotNone(row, msg="An imbalanced voucher must be flagged by the report.")
|
||||
|
||||
self.assertEqual(row.get("voucher_type"), "Journal Entry")
|
||||
self.assertEqual(row.get("credit"), 1000)
|
||||
self.assertEqual(row.get("debit"), 400)
|
||||
self.assertNotEqual(
|
||||
row.get("debit"), row.get("credit"), msg="Flagged rows must have debit != credit."
|
||||
)
|
||||
329
erpnext/accounts/workspace/accounts_setup/accounts_setup.json
Normal file
329
erpnext/accounts/workspace/accounts_setup/accounts_setup.json
Normal file
@@ -0,0 +1,329 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-14 12:44:31.994274",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "database",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Accounts Setup",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-06-14 13:43:50.138704",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module_onboarding": "Accounting Onboarding",
|
||||
"name": "Accounts Setup",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 55.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Chart of Accounts",
|
||||
"link_to": "Account",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Chart of Cost Centers",
|
||||
"link_to": "Cost Center",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Account Category",
|
||||
"link_to": "Account Category",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounting Dimension",
|
||||
"link_to": "Accounting Dimension",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Currency",
|
||||
"link_to": "Currency",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Currency Exchange",
|
||||
"link_to": "Currency Exchange",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Finance Book",
|
||||
"link_to": "Finance Book",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Mode of Payment",
|
||||
"link_to": "Mode of Payment",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Payment Term",
|
||||
"link_to": "Payment Term",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Journal Entry Template",
|
||||
"link_to": "Journal Entry Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Terms and Conditions",
|
||||
"link_to": "Terms and Conditions",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Company",
|
||||
"link_to": "Company",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Fiscal Year",
|
||||
"link_to": "Fiscal Year",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Taxes",
|
||||
"link_to": "Sales Taxes and Charges Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "lock-keyhole-open",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Opening & Closing",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "COA Importer",
|
||||
"link_to": "Chart of Accounts Importer",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Opening Invoice Tool",
|
||||
"link_to": "Opening Invoice Creation Tool",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounting Period",
|
||||
"link_to": "Accounting Period",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "FX Revaluation",
|
||||
"link_to": "Exchange Rate Revaluation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Period Closing Voucher",
|
||||
"link_to": "Period Closing Voucher",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "settings",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounts Settings",
|
||||
"link_to": "Accounts Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Currency Exchange Settings",
|
||||
"link_to": "Currency Exchange Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Accounts Setup",
|
||||
"type": "Workspace"
|
||||
}
|
||||
222
erpnext/accounts/workspace/banking/banking.json
Normal file
222
erpnext/accounts/workspace/banking/banking.json
Normal file
@@ -0,0 +1,222 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-11 11:51:22.767176",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "circle-dollar-sign",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Banking",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-06-14 13:43:50.924019",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Banking",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 49.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "book-open-check",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank Clearance",
|
||||
"link_to": "Bank Clearance",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "tool",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank Reconciliation",
|
||||
"link_to": "Bank Reconciliation Tool",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "clipboard-check",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Reconciliation Statement",
|
||||
"link_to": "Bank Reconciliation Statement",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "split",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Unreconcile Payment",
|
||||
"link_to": "Unreconcile Payment",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "link",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Process Payment Reconciliation",
|
||||
"link_to": "Process Payment Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank",
|
||||
"link_to": "Bank",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank Account",
|
||||
"link_to": "Bank Account",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank Account Type",
|
||||
"link_to": "Bank Account Type",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank Account Subtype",
|
||||
"link_to": "Bank Account Subtype",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank Guarantee",
|
||||
"link_to": "Bank Guarantee",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Plaid Settings",
|
||||
"link_to": "Plaid Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "scroll-text",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Dunning",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dunning",
|
||||
"link_to": "Dunning",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dunning Type",
|
||||
"link_to": "Dunning Type",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Banking",
|
||||
"type": "Workspace"
|
||||
}
|
||||
104
erpnext/accounts/workspace/budgeting/budgeting.json
Normal file
104
erpnext/accounts/workspace/budgeting/budgeting.json
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-14 14:38:20.315394",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "accounting",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Budgeting",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-07-02 04:24:48.116724",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Budgeting",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 57.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "briefcase-business",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Budget",
|
||||
"link_to": "Budget",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "badge-cent",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Cost Center",
|
||||
"link_to": "Cost Center",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "accounting",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounting Dimension",
|
||||
"link_to": "Accounting Dimension",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "notepad-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Cost Center Allocation",
|
||||
"link_to": "Cost Center Allocation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "sheet",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Budget Variance",
|
||||
"link_to": "Budget Variance Report",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Budgeting",
|
||||
"type": "Workspace"
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "table",
|
||||
"icon": "sheet",
|
||||
"idx": 1,
|
||||
"indicator_color": "",
|
||||
"is_hidden": 0,
|
||||
@@ -266,9 +266,10 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-05-18 09:49:45.138296",
|
||||
"modified": "2026-06-14 13:44:08.095321",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module_onboarding": "Accounting Onboarding",
|
||||
"name": "Financial Reports",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
@@ -279,6 +280,417 @@
|
||||
"roles": [],
|
||||
"sequence_id": 5.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "accounting",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Financial Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Balance Sheet",
|
||||
"link_to": "Balance Sheet",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Profit and Loss",
|
||||
"link_to": "Profit and Loss Statement",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Cash Flow",
|
||||
"link_to": "Cash Flow",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Trial Balance",
|
||||
"link_to": "Trial Balance",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Consolidated Report",
|
||||
"link_to": "Consolidated Financial Statement",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Custom Financial Statement",
|
||||
"link_to": "Custom Financial Statement",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Financial Report Template",
|
||||
"link_to": "Financial Report Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "book-text",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Ledgers",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "General Ledger",
|
||||
"link_to": "General Ledger",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer Ledger",
|
||||
"link_to": "Customer Ledger Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier Ledger",
|
||||
"link_to": "Supplier Ledger Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Registers",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounts Receivable",
|
||||
"link_to": "Accounts Receivable",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounts Payable",
|
||||
"link_to": "Accounts Payable",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "AR Summary",
|
||||
"link_to": "Accounts Receivable Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "AP Summary",
|
||||
"link_to": "Accounts Payable Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Register",
|
||||
"link_to": "Sales Register",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Register",
|
||||
"link_to": "Purchase Register",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item-wise sales Register",
|
||||
"link_to": "Item-wise Sales Register",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item-wise Purchase Register",
|
||||
"link_to": "Item-wise Purchase Register",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "dollar-sign",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Profitability",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Gross Profit",
|
||||
"link_to": "Gross Profit",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Profitability Analysis",
|
||||
"link_to": "Profitability Analysis",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Invoice Trends",
|
||||
"link_to": "Sales Invoice Trends",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Invoice Trends",
|
||||
"link_to": "Purchase Invoice Trends",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "scroll-text",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Other Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Trial Balance for Party",
|
||||
"link_to": "Trial Balance for Party",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Payment Period Based On Invoice Date",
|
||||
"link_to": "Payment Period Based On Invoice Date",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Partners Commission",
|
||||
"link_to": "Sales Partners Commission",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer Credit Balance",
|
||||
"link_to": "Customer Credit Balance",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Payment Summary",
|
||||
"link_to": "Sales Payment Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Address And Contacts",
|
||||
"link_to": "Address And Contacts",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "UAE VAT 201",
|
||||
"link_to": "UAE VAT 201",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Financial Reports",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -587,9 +587,10 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-01-23 11:05:47.246213",
|
||||
"modified": "2026-06-14 13:44:08.471142",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module_onboarding": "Accounting Onboarding",
|
||||
"name": "Invoicing",
|
||||
"number_cards": [
|
||||
{
|
||||
@@ -617,6 +618,354 @@
|
||||
"roles": [],
|
||||
"sequence_id": 2.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "home",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Invoicing",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "chart",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dashboard",
|
||||
"link_to": "Accounts",
|
||||
"link_type": "Dashboard",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "list-tree",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Chart of Accounts",
|
||||
"link_to": "Account",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "arrow-left-to-line",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Receivables",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer",
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Invoice",
|
||||
"link_to": "Sales Invoice",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Credit Note",
|
||||
"link_to": "Sales Invoice",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"route_options": "{\"is_return\": 1}",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounts Receivable",
|
||||
"link_to": "Accounts Receivable",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "arrow-right-from-line",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Payables",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier",
|
||||
"link_to": "Supplier",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Invoice",
|
||||
"link_to": "Purchase Invoice",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Debit Note",
|
||||
"link_to": "Purchase Invoice",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"route_options": "{\"is_return\": 1}",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounts Payable",
|
||||
"link_to": "Accounts Payable",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "money-coins-1",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Payments",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Payment Entry",
|
||||
"link_to": "Payment Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Journal Entry",
|
||||
"link_to": "Journal Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Payment Request",
|
||||
"link_to": "Payment Request",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Payment Order",
|
||||
"link_to": "Payment Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Payment Reconciliation",
|
||||
"link_to": "Payment Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Unreconcile Payment",
|
||||
"link_to": "Unreconcile Payment",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Process Payment Reconciliation",
|
||||
"link_to": "Process Payment Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Repost Accounting Ledger",
|
||||
"link_to": "Repost Accounting Ledger",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Repost Payment Ledger",
|
||||
"link_to": "Repost Payment Ledger",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sheet",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "General Ledger",
|
||||
"link_to": "General Ledger",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Trial Balance",
|
||||
"link_to": "Trial Balance",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Financial Reports",
|
||||
"link_to": "Financial Reports",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "settings",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Settings",
|
||||
"link_to": "Accounts Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Invoicing",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
240
erpnext/accounts/workspace/payments/payments.json
Normal file
240
erpnext/accounts/workspace/payments/payments.json
Normal file
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-11 11:51:21.886461",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "receipt-text",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Payments",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-06-14 13:43:50.184761",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module_onboarding": "Accounting Onboarding",
|
||||
"name": "Payments",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 47.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "chart",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dashboard",
|
||||
"link_to": "Payments",
|
||||
"link_type": "Dashboard",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "money-coins-1",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Payments",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Payment Entry",
|
||||
"link_to": "Payment Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Journal Entry",
|
||||
"link_to": "Journal Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Payment Request",
|
||||
"link_to": "Payment Request",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Payment Order",
|
||||
"link_to": "Payment Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Payment Reconciliation",
|
||||
"link_to": "Payment Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Unreconcile Payment",
|
||||
"link_to": "Unreconcile Payment",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Process Payment Reconciliation",
|
||||
"link_to": "Process Payment Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Repost Accounting Ledger",
|
||||
"link_to": "Repost Accounting Ledger",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Repost Payment Ledger",
|
||||
"link_to": "Repost Payment Ledger",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sheet",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounts Receivable",
|
||||
"link_to": "Accounts Receivable",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounts Payable",
|
||||
"link_to": "Accounts Payable",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "General Ledger",
|
||||
"link_to": "General Ledger",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Trial Balance",
|
||||
"link_to": "Trial Balance",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Financial Reports",
|
||||
"link_to": "Financial Reports",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Payments",
|
||||
"type": "Workspace"
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-11 11:51:22.831729",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "money-coins-1",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Share Management",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-06-14 13:43:51.040978",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Share Management",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 50.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "customer",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Shareholder",
|
||||
"link_to": "Shareholder",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "move-horizontal",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Share Transfer",
|
||||
"link_to": "Share Transfer",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "list",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Share Ledger",
|
||||
"link_to": "Share Ledger",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "notepad-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Share Balance",
|
||||
"link_to": "Share Balance",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Share Management",
|
||||
"type": "Workspace"
|
||||
}
|
||||
121
erpnext/accounts/workspace/subscriptions/subscriptions.json
Normal file
121
erpnext/accounts/workspace/subscriptions/subscriptions.json
Normal file
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-14 14:08:36.817393",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "accounting",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Subscriptions",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-06-14 14:08:36.999272",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Subscriptions",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 56.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "circle-dollar-sign",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Subscription",
|
||||
"link_to": "Subscription",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "receipt-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Subscription Plan",
|
||||
"link_to": "Subscription Plan",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "settings",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Subscription Settings",
|
||||
"link_to": "Subscription Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer",
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier",
|
||||
"link_to": "Supplier",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Subscriptions",
|
||||
"type": "Workspace"
|
||||
}
|
||||
188
erpnext/accounts/workspace/taxes/taxes.json
Normal file
188
erpnext/accounts/workspace/taxes/taxes.json
Normal file
@@ -0,0 +1,188 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-11 11:51:22.649582",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "money-coins-1",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Taxes",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-06-14 13:43:50.894825",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module_onboarding": "Accounting Onboarding",
|
||||
"name": "Taxes",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 48.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "panel-bottom-close",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Tax Template",
|
||||
"link_to": "Sales Taxes and Charges Template",
|
||||
"link_type": "DocType",
|
||||
"navigate_to_tab": "",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "panel-top-close",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Tax Template",
|
||||
"link_to": "Purchase Taxes and Charges Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "stock",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Tax Template",
|
||||
"link_to": "Item Tax Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "triangle",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Tax Category",
|
||||
"link_to": "Tax Category",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "book-open-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Tax Rule",
|
||||
"link_to": "Tax Rule",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "book-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Tax Withholding Category",
|
||||
"link_to": "Tax Withholding Category",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Tax Withholding Group",
|
||||
"link_to": "Tax Withholding Group",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "notebook-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Deduction Certificate",
|
||||
"link_to": "Lower Deduction Certificate",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sheet",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_to": "",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "TDS Computation Summary",
|
||||
"link_to": "TDS Computation Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Tax Withholding Details",
|
||||
"link_to": "Tax Withholding Details",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Taxes",
|
||||
"type": "Workspace"
|
||||
}
|
||||
@@ -199,9 +199,10 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2025-12-31 16:22:38.132729",
|
||||
"modified": "2026-06-14 13:44:08.417956",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"module_onboarding": "Asset Onboarding",
|
||||
"name": "Assets",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
@@ -212,6 +213,294 @@
|
||||
"roles": [],
|
||||
"sequence_id": 7.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "home",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Assets",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "chart",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dashboard",
|
||||
"link_to": "Asset",
|
||||
"link_type": "Dashboard",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "laptop",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset",
|
||||
"link_to": "Asset",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "trending-down",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Depreciation Schedule",
|
||||
"link_to": "Asset Depreciation Schedule",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sprout",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Capitalization",
|
||||
"link_to": "Asset Capitalization",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "move-horizontal",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Movement",
|
||||
"link_to": "Asset Movement",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "getting-started",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Maintenance",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Maintenance Team",
|
||||
"link_to": "Asset Maintenance Team",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Maintenance",
|
||||
"link_to": "Asset Maintenance",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Maintenance Log",
|
||||
"link_to": "Asset Maintenance Log",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Value Adjustment",
|
||||
"link_to": "Asset Value Adjustment",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Repair",
|
||||
"link_to": "Asset Repair",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sheet",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Fixed Asset Register",
|
||||
"link_to": "Fixed Asset Register",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Depreciation Ledger",
|
||||
"link_to": "Asset Depreciation Ledger",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Depreciations and Balances",
|
||||
"link_to": "Asset Depreciations and Balances",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Maintenance",
|
||||
"link_to": "Asset Maintenance",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Activity",
|
||||
"link_to": "Asset Activity",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Asset Category",
|
||||
"link_to": "Asset Category",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Location",
|
||||
"link_to": "Location",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "settings",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Settings",
|
||||
"link_to": "Accounts Settings",
|
||||
"link_type": "DocType",
|
||||
"navigate_to_tab": "assets_tab",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Assets",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -341,17 +341,6 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Item Wise Consumption",
|
||||
"link_count": 0,
|
||||
"link_to": "Item Wise Consumption",
|
||||
"link_type": "Report",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -512,9 +501,10 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-01-02 14:55:59.078773",
|
||||
"modified": "2026-06-14 13:43:50.509039",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"module_onboarding": "Buying Onboarding",
|
||||
"name": "Buying",
|
||||
"number_cards": [
|
||||
{
|
||||
@@ -538,6 +528,403 @@
|
||||
"roles": [],
|
||||
"sequence_id": 5.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "home",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Buying",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "chart",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dashboard",
|
||||
"link_to": "Buying",
|
||||
"link_type": "Dashboard",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "notepad-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Material Request",
|
||||
"link_to": "Material Request",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "git-pull-request-arrow",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Request for Quotation",
|
||||
"link_to": "Request for Quotation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "book-open-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier Quotation",
|
||||
"link_to": "Supplier Quotation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "receipt-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Order",
|
||||
"link_to": "Purchase Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "liabilities",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Invoice",
|
||||
"link_to": "Purchase Invoice",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier",
|
||||
"link_to": "Supplier",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier Group",
|
||||
"link_to": "Supplier Group",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Price List",
|
||||
"link_to": "Price List",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Address",
|
||||
"link_to": "Address",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Contacts",
|
||||
"link_to": "Contact",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier Scorecard",
|
||||
"link_to": "Supplier Scorecard",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier Scorecard Criteria",
|
||||
"link_to": "Supplier Scorecard Criteria",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier Scorecard Variable",
|
||||
"link_to": "Supplier Scorecard Variable",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier Scorecard Standing",
|
||||
"link_to": "Supplier Scorecard Standing",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sheet",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Analytics",
|
||||
"link_to": "Purchase Analytics",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Order Analysis",
|
||||
"link_to": "Purchase Order Analysis",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Requested Items to Order and Receive",
|
||||
"link_to": "Requested Items to Order and Receive",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Items To Be Requested",
|
||||
"link_to": "Items To Be Requested",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item-wise Purchase History",
|
||||
"link_to": "Item-wise Purchase History",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Receipt Trends ",
|
||||
"link_to": "Purchase Receipt Trends",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Invoice Trends",
|
||||
"link_to": "Purchase Invoice Trends",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Order Trends",
|
||||
"link_to": "Purchase Order Trends",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Procurement Tracker",
|
||||
"link_to": "Procurement Tracker",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Wise Consumption",
|
||||
"link_to": "Item Wise Consumption",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier Quotation Comparison",
|
||||
"link_to": "Supplier Quotation Comparison",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier Addresses And Contacts",
|
||||
"link_to": "Address And Contacts",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "settings",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Settings",
|
||||
"link_to": "Buying Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Buying",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -167,7 +167,8 @@ status_map = {
|
||||
"Pick List": [
|
||||
["Draft", None],
|
||||
["Open", "eval:self.docstatus == 1"],
|
||||
["Completed", "stock_entry_exists"],
|
||||
["Completed", "is_fully_transferred"],
|
||||
["Partially Transferred", "is_partially_transferred"],
|
||||
[
|
||||
"Partly Delivered",
|
||||
"eval:self.purpose == 'Delivery' and self.delivery_status == 'Partly Delivered'",
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
"app": "erpnext",
|
||||
"charts": [
|
||||
{
|
||||
"chart_name": "Won Opportunities",
|
||||
"label": "Won Opportunities"
|
||||
"chart_name": "Territory Wise Sales",
|
||||
"label": "Territory Wise Sales"
|
||||
}
|
||||
],
|
||||
"content": "[{\"id\":\"4jhDsfZ7EP\",\"type\":\"header\",\"data\":{\"text\":\"This module is scheduled for deprecation and will be completely removed in version 17, please use <a href=\\\"https://frappe.io/crm\\\">Frappe CRM</a> instead.\",\"col\":12}},{\"id\":\"-bzBQ_IbL9\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Won Opportunities\",\"col\":12}},{\"id\":\"LdM1QgUnqU\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"New Lead (Last 1 Month)\",\"col\":4}},{\"id\":\"X23-SXBcYG\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"New Opportunity (Last 1 Month)\",\"col\":4}},{\"id\":\"3rm7fH52M-\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Won Opportunity (Last 1 Month)\",\"col\":4}},{\"id\":\"K6a2Kh5Zav\",\"type\":\"spacer\",\"data\":{\"col\":12}}]",
|
||||
"content": "[{\"id\":\"4jhDsfZ7EP\",\"type\":\"header\",\"data\":{\"text\":\"This module is scheduled for deprecation and will be completely removed in version 17, please use <a href=\\\"http://frappe.io/crm\\\">Frappe CRM</a> instead.\",\"col\":12}},{\"id\":\"Cj2TyhgiWy\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Territory Wise Sales\",\"col\":12}},{\"id\":\"LAKRmpYMRA\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"69RN0XsiJK\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Lead\",\"col\":3}},{\"id\":\"t6PQ0vY-Iw\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Opportunity\",\"col\":3}},{\"id\":\"VOFE0hqXRD\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":3}},{\"id\":\"0ik53fuemG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"id\":\"wdROEmB_XG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"-I9HhcgUKE\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"ttpROKW9vk\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"-76QPdbBHy\",\"type\":\"card\",\"data\":{\"card_name\":\"Sales Pipeline\",\"col\":4}},{\"id\":\"_YmGwzVWRr\",\"type\":\"card\",\"data\":{\"card_name\":\"Masters\",\"col\":4}},{\"id\":\"Bma1PxoXk3\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"80viA0R83a\",\"type\":\"card\",\"data\":{\"card_name\":\"Campaign\",\"col\":4}},{\"id\":\"Buo5HtKRFN\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"sLS_x4FMK2\",\"type\":\"card\",\"data\":{\"card_name\":\"Maintenance\",\"col\":4}}]",
|
||||
"creation": "2020-01-23 14:48:30.183272",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
@@ -18,6 +18,14 @@
|
||||
"is_hidden": 0,
|
||||
"label": "CRM",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Reports",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "Lead",
|
||||
"hidden": 0,
|
||||
@@ -115,6 +123,14 @@
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Maintenance",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -148,6 +164,183 @@
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Sales Pipeline",
|
||||
"link_count": 7,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Lead",
|
||||
"link_count": 0,
|
||||
"link_to": "Lead",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Opportunity",
|
||||
"link_count": 0,
|
||||
"link_to": "Opportunity",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Customer",
|
||||
"link_count": 0,
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Contract",
|
||||
"link_count": 0,
|
||||
"link_to": "Contract",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Appointment",
|
||||
"link_count": 0,
|
||||
"link_to": "Appointment",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Newsletter",
|
||||
"link_count": 0,
|
||||
"link_to": "Newsletter",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Communication",
|
||||
"link_count": 0,
|
||||
"link_to": "Communication",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Settings",
|
||||
"link_count": 2,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "CRM Settings",
|
||||
"link_count": 0,
|
||||
"link_to": "CRM Settings",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "SMS Settings",
|
||||
"link_count": 0,
|
||||
"link_to": "SMS Settings",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Campaign",
|
||||
"link_count": 5,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Campaign",
|
||||
"link_count": 0,
|
||||
"link_to": "Campaign",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Email Campaign",
|
||||
"link_count": 0,
|
||||
"link_to": "Email Campaign",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "SMS Center",
|
||||
"link_count": 0,
|
||||
"link_to": "SMS Center",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "SMS Log",
|
||||
"link_count": 0,
|
||||
"link_to": "SMS Log",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Email Group",
|
||||
"link_count": 0,
|
||||
"link_to": "Email Group",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
@@ -228,24 +421,11 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-01-03 15:05:23.983099",
|
||||
"modified": "2026-06-14 13:44:08.297053",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "CRM",
|
||||
"number_cards": [
|
||||
{
|
||||
"label": "New Lead (Last 1 Month)",
|
||||
"number_card_name": "New Lead (Last 1 Month)"
|
||||
},
|
||||
{
|
||||
"label": "New Opportunity (Last 1 Month)",
|
||||
"number_card_name": "New Opportunity (Last 1 Month)"
|
||||
},
|
||||
{
|
||||
"label": "Won Opportunity (Last 1 Month)",
|
||||
"number_card_name": "Won Opportunity (Last 1 Month)"
|
||||
}
|
||||
],
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "",
|
||||
"public": 1,
|
||||
@@ -253,7 +433,552 @@
|
||||
"restrict_to_domain": "",
|
||||
"roles": [],
|
||||
"sequence_id": 17.0,
|
||||
"shortcuts": [],
|
||||
"shortcuts": [
|
||||
{
|
||||
"color": "Blue",
|
||||
"format": "{} Open",
|
||||
"label": "Lead",
|
||||
"link_to": "Lead",
|
||||
"stats_filter": "{\"status\":\"Open\"}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "Blue",
|
||||
"format": "{} Assigned",
|
||||
"label": "Opportunity",
|
||||
"link_to": "Opportunity",
|
||||
"stats_filter": "{\"_assign\": [\"like\", '%' + frappe.session.user + '%']}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Customer",
|
||||
"link_to": "Customer",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Sales Analytics",
|
||||
"link_to": "Sales Analytics",
|
||||
"report_ref_doctype": "Sales Order",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "Dashboard",
|
||||
"link_to": "CRM",
|
||||
"type": "Dashboard"
|
||||
}
|
||||
],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "chart",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "CRM",
|
||||
"link_type": "Dashboard",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "users-round",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Lead",
|
||||
"link_to": "Lead",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "lightbulb",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Opportunity",
|
||||
"link_to": "Opportunity",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "customer",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer",
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sheet",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Analytics",
|
||||
"link_to": "Sales Analytics",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Lead Details",
|
||||
"link_to": "Lead Details",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Pipeline Analytics",
|
||||
"link_to": "Sales Pipeline Analytics",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Opportunity Summary by Sales Stage",
|
||||
"link_to": "Opportunity Summary by Sales Stage",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Funnel",
|
||||
"link_to": "sales-funnel",
|
||||
"link_type": "Page",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Prospects Engaged But Not Converted",
|
||||
"link_to": "Prospects Engaged But Not Converted",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "First Response Time for Opportunity",
|
||||
"link_to": "First Response Time for Opportunity",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Campaign Efficiency",
|
||||
"link_to": "Campaign Efficiency",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Lead Owner Efficiency",
|
||||
"link_to": "Lead Owner Efficiency",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "getting-started",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Maintenance",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Maintenance Schedule",
|
||||
"link_to": "Maintenance Schedule",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Maintenance Visit",
|
||||
"link_to": "Maintenance Visit",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Warranty Claim",
|
||||
"link_to": "Warranty Claim",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "funnel",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Sales Pipeline",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Lead",
|
||||
"link_to": "Lead",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Opportunity",
|
||||
"link_to": "Opportunity",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer",
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Contract",
|
||||
"link_to": "Contract",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Appointment",
|
||||
"link_to": "Appointment",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Communication",
|
||||
"link_to": "Communication",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sell",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Campaign",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Campaign",
|
||||
"link_to": "Campaign",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Email Campaign",
|
||||
"link_to": "Email Campaign",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "SMS Center",
|
||||
"link_to": "SMS Center",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "SMS Log",
|
||||
"link_to": "SMS Log",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Email Group",
|
||||
"link_to": "Email Group",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Territory",
|
||||
"link_to": "Territory",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer Group",
|
||||
"link_to": "Customer Group",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Contact",
|
||||
"link_to": "Contact",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Prospect",
|
||||
"link_to": "Prospect",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Person",
|
||||
"link_to": "Sales Person",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Stage",
|
||||
"link_to": "Sales Stage",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Lead Source",
|
||||
"link_to": "UTM Source",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "settings",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "CRM Settings",
|
||||
"link_to": "CRM Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "SMS Settings",
|
||||
"link_to": "SMS Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "CRM",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ app_email = "hello@frappe.io"
|
||||
app_license = "GNU General Public License (v3)"
|
||||
source_link = "https://github.com/frappe/erpnext"
|
||||
app_logo_url = "/assets/erpnext/images/erpnext-logo.svg"
|
||||
app_home = "/desk"
|
||||
app_home = "/desk/home"
|
||||
|
||||
add_to_apps_screen = [
|
||||
{
|
||||
|
||||
@@ -739,7 +739,7 @@ class BOM(WebsiteGenerator):
|
||||
)
|
||||
)
|
||||
|
||||
def check_recursion(self, bom_list=None):
|
||||
def check_recursion(self):
|
||||
"""Check whether recursion occurs in any bom"""
|
||||
bom_list = self.traverse_tree()
|
||||
child_items = frappe.get_all(
|
||||
@@ -861,21 +861,30 @@ class BOM(WebsiteGenerator):
|
||||
|
||||
self.append("items", row)
|
||||
|
||||
def traverse_tree(self, bom_list=None):
|
||||
count = 0
|
||||
if not bom_list:
|
||||
bom_list = []
|
||||
def traverse_tree(self):
|
||||
"""Return this BOM and every descendant BOM. The whole sub-tree is fetched in one recursive
|
||||
CTE (frappe.qb) instead of a query-per-node walk; the only caller (check_recursion) uses the
|
||||
result purely as a membership set. Portable across postgres and mariadb 10.2+."""
|
||||
bom_item = frappe.qb.DocType("BOM Item")
|
||||
tree = frappe.qb.Table("bom_tree")
|
||||
|
||||
if self.name not in bom_list:
|
||||
bom_list.append(self.name)
|
||||
seed = (
|
||||
frappe.qb.from_(bom_item)
|
||||
.select(bom_item.bom_no.as_("bom"))
|
||||
.where((bom_item.parent == self.name) & (bom_item.bom_no != "") & (bom_item.parenttype == "BOM"))
|
||||
)
|
||||
recursion = (
|
||||
frappe.qb.from_(bom_item)
|
||||
.join(tree)
|
||||
.on(bom_item.parent == tree.bom)
|
||||
.select(bom_item.bom_no)
|
||||
.where((bom_item.bom_no != "") & (bom_item.parenttype == "BOM"))
|
||||
)
|
||||
descendants = (
|
||||
frappe.qb.with_(seed + recursion, "bom_tree", recursive=True).from_(tree).select(tree.bom)
|
||||
).run(pluck=True)
|
||||
|
||||
while count < len(bom_list):
|
||||
for child_bom in _get_bom_children(bom_list[count]):
|
||||
if child_bom not in bom_list:
|
||||
bom_list.append(child_bom)
|
||||
count += 1
|
||||
bom_list.reverse()
|
||||
return bom_list
|
||||
return [self.name, *descendants]
|
||||
|
||||
def company_currency(self):
|
||||
return erpnext.get_company_currency(self.company)
|
||||
|
||||
@@ -67,29 +67,33 @@ def update_cost_in_level(doc: "BOMUpdateLog", bom_list: list[str], batch_name: i
|
||||
frappe.db.commit() # nosemgrep
|
||||
|
||||
|
||||
def get_ancestor_boms(new_bom: str, bom_list: list | None = None) -> list:
|
||||
"Recursively get all ancestors of BOM."
|
||||
|
||||
bom_list = bom_list or []
|
||||
def get_ancestor_boms(new_bom: str) -> list:
|
||||
"""Return every ancestor BOM of `new_bom` (BOMs that consume it, transitively) in one recursive
|
||||
CTE built with frappe.qb -- portable across postgres and mariadb 10.2+. `UNION` makes it
|
||||
cycle-safe (it stops once no new BOM is reached); a BOM that is its own ancestor is rejected."""
|
||||
bom_item = frappe.qb.DocType("BOM Item")
|
||||
tree = frappe.qb.Table("ancestor_boms")
|
||||
|
||||
parents = (
|
||||
seed = (
|
||||
frappe.qb.from_(bom_item)
|
||||
.select(bom_item.parent)
|
||||
.select(bom_item.parent.as_("bom"))
|
||||
.where((bom_item.bom_no == new_bom) & (bom_item.docstatus < 2) & (bom_item.parenttype == "BOM"))
|
||||
.run(as_dict=True)
|
||||
)
|
||||
recursion = (
|
||||
frappe.qb.from_(bom_item)
|
||||
.join(tree)
|
||||
.on(bom_item.bom_no == tree.bom)
|
||||
.select(bom_item.parent)
|
||||
.where((bom_item.docstatus < 2) & (bom_item.parenttype == "BOM"))
|
||||
)
|
||||
ancestors = (
|
||||
frappe.qb.with_(seed + recursion, "ancestor_boms", recursive=True).from_(tree).select(tree.bom)
|
||||
).run(pluck=True)
|
||||
|
||||
for d in parents:
|
||||
if new_bom == d.parent:
|
||||
frappe.throw(_("BOM recursion: {0} cannot be child of {1}").format(new_bom, d.parent))
|
||||
if new_bom in ancestors:
|
||||
frappe.throw(_("BOM recursion: {0} cannot be an ancestor of itself").format(new_bom))
|
||||
|
||||
if d.parent not in tuple(bom_list):
|
||||
bom_list.append(d.parent)
|
||||
|
||||
get_ancestor_boms(d.parent, bom_list)
|
||||
|
||||
return bom_list
|
||||
return ancestors
|
||||
|
||||
|
||||
def update_new_bom_in_bom_items(unit_cost: float, current_bom: str, new_bom: str) -> None:
|
||||
|
||||
@@ -149,6 +149,16 @@ class RequiredItemsService:
|
||||
|
||||
self.recompute_material_transferred_for_manufacturing(transferred_items)
|
||||
|
||||
def refresh_material_transferred_for_manufacturing(self):
|
||||
"""Recompute material_transferred_for_manufacturing only, without touching per-row
|
||||
transferred_qty or stock reservations. Used to get a status decision (Not Started vs
|
||||
In Process) based on fresh data, ahead of the fuller update_required_items() pass.
|
||||
"""
|
||||
if self.doc.skip_transfer:
|
||||
return
|
||||
transferred_items = self._material_transfer_qty_by_item(is_return=0)
|
||||
self.recompute_material_transferred_for_manufacturing(transferred_items)
|
||||
|
||||
def recompute_material_transferred_for_manufacturing(self, transferred_items):
|
||||
"""Set material_transferred_for_manufacturing based on actual item-level transfers, not fg_completed_qty."""
|
||||
# When fg_completed_qty > 0 (direct stock entries, excess transfer), preserve the
|
||||
|
||||
@@ -87,6 +87,12 @@ class StatusService:
|
||||
|
||||
def update_status(self, status=None):
|
||||
"""Update status of work order if unknown"""
|
||||
if self.doc.docstatus == 1:
|
||||
# Refresh material_transferred_for_manufacturing before deciding status so pick-list-
|
||||
# driven transfers (where this qty is derived from item transfers, not fg_completed_qty)
|
||||
# are reflected immediately, instead of only after the next status update call.
|
||||
self.doc.refresh_material_transferred_for_manufacturing()
|
||||
|
||||
if self.doc.status != "Closed":
|
||||
if status not in ["Stopped", "Closed"]:
|
||||
status = self.get_status(status)
|
||||
|
||||
@@ -1003,6 +1003,9 @@ class WorkOrder(Document):
|
||||
def update_transferred_qty_for_required_items(self):
|
||||
return RequiredItemsService(self).update_transferred_qty_for_required_items()
|
||||
|
||||
def refresh_material_transferred_for_manufacturing(self):
|
||||
return RequiredItemsService(self).refresh_material_transferred_for_manufacturing()
|
||||
|
||||
def update_returned_qty(self):
|
||||
return RequiredItemsService(self).update_returned_qty()
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
@@ -14,29 +16,47 @@ def execute(filters=None):
|
||||
|
||||
|
||||
def get_data(filters, data):
|
||||
get_exploded_items(filters.bom, data)
|
||||
children_map = fetch_exploded_bom_items(filters.bom)
|
||||
build_exploded_rows(filters.bom, children_map, data)
|
||||
|
||||
|
||||
def get_exploded_items(bom, data, indent=0, qty=1):
|
||||
exploded_items = frappe.get_all(
|
||||
"BOM Item",
|
||||
filters={"parent": bom},
|
||||
fields=[
|
||||
"qty",
|
||||
"bom_no",
|
||||
"qty",
|
||||
"item_code",
|
||||
"item_name",
|
||||
"description",
|
||||
"uom",
|
||||
"idx",
|
||||
"is_phantom_item",
|
||||
],
|
||||
order_by="idx ASC",
|
||||
def fetch_exploded_bom_items(root_bom):
|
||||
"""Every BOM Item in the exploded tree of `root_bom`, grouped by its parent BOM, in one
|
||||
recursive CTE -- replaces a query-per-node walk with a single query. UNION keeps it cycle-safe
|
||||
and fetches each sub-BOM's items only once even when it is reused across the tree."""
|
||||
bom_item = frappe.qb.DocType("BOM Item")
|
||||
tree = frappe.qb.Table("exploded_bom")
|
||||
fields = [
|
||||
bom_item.parent,
|
||||
bom_item.qty,
|
||||
bom_item.bom_no,
|
||||
bom_item.item_code,
|
||||
bom_item.item_name,
|
||||
bom_item.description,
|
||||
bom_item.uom,
|
||||
bom_item.idx,
|
||||
bom_item.is_phantom_item,
|
||||
]
|
||||
seed = frappe.qb.from_(bom_item).select(*fields).where(bom_item.parent == root_bom)
|
||||
recursion = (
|
||||
frappe.qb.from_(bom_item)
|
||||
.join(tree)
|
||||
.on(bom_item.parent == tree.bom_no)
|
||||
.select(*fields)
|
||||
.where(tree.bom_no != "")
|
||||
)
|
||||
rows = (
|
||||
frappe.qb.with_(seed + recursion, "exploded_bom", recursive=True).from_(tree).select(tree.star)
|
||||
).run(as_dict=True)
|
||||
|
||||
for item in exploded_items:
|
||||
item["indent"] = indent
|
||||
children_map = defaultdict(list)
|
||||
for row in rows:
|
||||
children_map[row.parent].append(row)
|
||||
return children_map
|
||||
|
||||
|
||||
def build_exploded_rows(bom, children_map, data, indent=0, qty=1):
|
||||
for item in sorted(children_map.get(bom, []), key=lambda row: row.idx):
|
||||
data.append(
|
||||
{
|
||||
"item_code": item.item_code,
|
||||
@@ -51,7 +71,7 @@ def get_exploded_items(bom, data, indent=0, qty=1):
|
||||
}
|
||||
)
|
||||
if item.bom_no:
|
||||
get_exploded_items(item.bom_no, data, indent=indent + 1, qty=item.qty)
|
||||
build_exploded_rows(item.bom_no, children_map, data, indent + 1, item.qty)
|
||||
|
||||
|
||||
def get_columns():
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
import frappe
|
||||
|
||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||
from erpnext.manufacturing.report.bom_operations_time.bom_operations_time import execute
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
OPERATION = "_Test BOM Ops Time Operation"
|
||||
WORKSTATION = "_Test BOM Ops Time Workstation"
|
||||
TIME_IN_MINS = 45
|
||||
|
||||
|
||||
class TestBOMOperationsTime(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
ensure_workstation_and_operation()
|
||||
self.rm_item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100}).name
|
||||
self.fg_item = make_item(properties={"is_stock_item": 1}).name
|
||||
self.bom = build_bom_with_operation(self.fg_item, self.rm_item)
|
||||
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict({"bom_id": [self.bom.name]})
|
||||
filters.update(extra)
|
||||
return execute(filters)[1]
|
||||
|
||||
def test_operation_row_appears_with_expected_values(self):
|
||||
rows = self.run_report()
|
||||
|
||||
bom_rows = [row for row in rows if row.name == self.bom.name]
|
||||
self.assertEqual(len(bom_rows), 1)
|
||||
|
||||
row = bom_rows[0]
|
||||
self.assertEqual(row.item, self.fg_item)
|
||||
self.assertEqual(row.operation, OPERATION)
|
||||
self.assertEqual(row.workstation, WORKSTATION)
|
||||
self.assertEqual(row.time_in_mins, TIME_IN_MINS)
|
||||
|
||||
def test_item_code_filter_scopes_to_bom(self):
|
||||
rows = self.run_report(item_code=self.fg_item)
|
||||
|
||||
self.assertTrue(rows)
|
||||
self.assertTrue(all(row.item == self.fg_item for row in rows))
|
||||
self.assertIn(self.bom.name, {row.name for row in rows})
|
||||
|
||||
def test_workstation_filter(self):
|
||||
matching = self.run_report(workstation=WORKSTATION)
|
||||
self.assertIn(self.bom.name, {row.name for row in matching})
|
||||
|
||||
other_workstation = ensure_other_workstation()
|
||||
non_matching = self.run_report(workstation=other_workstation)
|
||||
self.assertNotIn(self.bom.name, {row.name for row in non_matching})
|
||||
|
||||
def test_draft_bom_excluded(self):
|
||||
draft_bom = build_bom_with_operation(
|
||||
make_item(properties={"is_stock_item": 1}).name, self.rm_item, do_not_submit=True
|
||||
)
|
||||
|
||||
rows = execute(frappe._dict({"bom_id": [draft_bom.name]}))[1]
|
||||
self.assertEqual(rows, [])
|
||||
|
||||
|
||||
def ensure_workstation_and_operation():
|
||||
if not frappe.db.exists("Workstation", WORKSTATION):
|
||||
frappe.get_doc({"doctype": "Workstation", "workstation_name": WORKSTATION}).insert(
|
||||
ignore_permissions=True
|
||||
)
|
||||
|
||||
if not frappe.db.exists("Operation", OPERATION):
|
||||
frappe.get_doc({"doctype": "Operation", "name": OPERATION, "workstation": WORKSTATION}).insert(
|
||||
ignore_permissions=True
|
||||
)
|
||||
|
||||
|
||||
def ensure_other_workstation():
|
||||
name = "_Test BOM Ops Time Workstation 2"
|
||||
if not frappe.db.exists("Workstation", name):
|
||||
frappe.get_doc({"doctype": "Workstation", "workstation_name": name}).insert(ignore_permissions=True)
|
||||
return name
|
||||
|
||||
|
||||
def build_bom_with_operation(fg_item, rm_item, do_not_submit=False):
|
||||
bom = make_bom(
|
||||
item=fg_item,
|
||||
raw_materials=[rm_item],
|
||||
with_operations=1,
|
||||
do_not_save=True,
|
||||
)
|
||||
bom.append(
|
||||
"operations",
|
||||
{
|
||||
"operation": OPERATION,
|
||||
"workstation": WORKSTATION,
|
||||
"time_in_mins": TIME_IN_MINS,
|
||||
"hour_rate": 100,
|
||||
},
|
||||
)
|
||||
bom.insert(ignore_permissions=True)
|
||||
if not do_not_submit:
|
||||
bom.submit()
|
||||
return bom
|
||||
@@ -0,0 +1,92 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe.utils import add_days, get_datetime, today
|
||||
|
||||
from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
|
||||
from erpnext.manufacturing.report.downtime_analysis.downtime_analysis import execute
|
||||
from erpnext.setup.doctype.employee.test_employee import make_employee
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestDowntimeAnalysis(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
self.workstation = make_workstation(workstation="_Test Downtime Workstation").name
|
||||
self.other_workstation = make_workstation(workstation="_Test Downtime Workstation 2").name
|
||||
self.operator = make_employee("test_downtime_operator@example.com", company="_Test Company")
|
||||
|
||||
# from_time / to_time are two hours apart -> downtime of 120 minutes (2 hours).
|
||||
self.from_time = get_datetime(f"{today()} 09:00:00")
|
||||
self.to_time = get_datetime(f"{today()} 11:00:00")
|
||||
self.entry = self.make_downtime_entry(self.workstation)
|
||||
|
||||
def make_downtime_entry(self, workstation, **extra):
|
||||
values = {
|
||||
"doctype": "Downtime Entry",
|
||||
"workstation": workstation,
|
||||
"operator": self.operator,
|
||||
"from_time": self.from_time,
|
||||
"to_time": self.to_time,
|
||||
"stop_reason": "Machine malfunction",
|
||||
}
|
||||
values.update(extra)
|
||||
return frappe.get_doc(values).insert()
|
||||
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"from_date": add_days(today(), -1),
|
||||
"to_date": add_days(today(), 1),
|
||||
}
|
||||
)
|
||||
filters.update(extra)
|
||||
return execute(filters)[1]
|
||||
|
||||
def row_for_entry(self, rows, name):
|
||||
return next((row for row in rows if row.get("name") == name), None)
|
||||
|
||||
def test_downtime_is_computed_in_hours(self):
|
||||
# validate() stores downtime in minutes; the report converts it to hours.
|
||||
self.assertEqual(self.entry.downtime, 120)
|
||||
|
||||
row = self.row_for_entry(self.run_report(), self.entry.name)
|
||||
self.assertIsNotNone(row, "Downtime Entry not present in report output")
|
||||
self.assertEqual(row.get("workstation"), self.workstation)
|
||||
self.assertEqual(row.get("operator"), self.operator)
|
||||
self.assertEqual(row.get("stop_reason"), "Machine malfunction")
|
||||
self.assertEqual(row.get("downtime"), 2.0)
|
||||
|
||||
def test_workstation_filter_scopes_rows(self):
|
||||
other = self.make_downtime_entry(self.other_workstation)
|
||||
|
||||
rows = self.run_report(workstation=self.workstation)
|
||||
names = {row.get("name") for row in rows}
|
||||
self.assertIn(self.entry.name, names)
|
||||
self.assertNotIn(other.name, names)
|
||||
self.assertTrue(all(row.get("workstation") == self.workstation for row in rows))
|
||||
|
||||
def test_date_range_excludes_out_of_window_entries(self):
|
||||
# The report filters from_time >= from_date and to_time <= to_date; a window
|
||||
# ending before the entry's from_time must exclude it.
|
||||
rows = self.run_report(from_date=add_days(today(), -10), to_date=add_days(today(), -5))
|
||||
self.assertIsNone(self.row_for_entry(rows, self.entry.name))
|
||||
|
||||
def test_chart_aggregates_downtime_per_workstation(self):
|
||||
self.make_downtime_entry(self.workstation)
|
||||
|
||||
chart = execute(
|
||||
frappe._dict(
|
||||
{
|
||||
"from_date": add_days(today(), -1),
|
||||
"to_date": add_days(today(), 1),
|
||||
"workstation": self.workstation,
|
||||
}
|
||||
)
|
||||
)[3]
|
||||
|
||||
self.assertIn(self.workstation, chart["data"]["labels"])
|
||||
index = chart["data"]["labels"].index(self.workstation)
|
||||
# Two entries of 2 hours each for this workstation -> 4 hours aggregated.
|
||||
self.assertEqual(chart["data"]["datasets"][0]["values"][index], 4.0)
|
||||
@@ -0,0 +1,118 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.utils import nowdate
|
||||
|
||||
from erpnext.manufacturing.doctype.work_order.mapper import make_stock_entry
|
||||
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
||||
from erpnext.manufacturing.report.process_loss_report.process_loss_report import execute
|
||||
from erpnext.stock.doctype.stock_entry import test_stock_entry
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestProcessLossReport(ERPNextTestSuite):
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"from_date": nowdate(),
|
||||
"to_date": nowdate(),
|
||||
}
|
||||
)
|
||||
filters.update(extra)
|
||||
return execute(filters)[1]
|
||||
|
||||
def find_row(self, data, work_order):
|
||||
for row in data:
|
||||
if row.get("name") == work_order:
|
||||
return row
|
||||
return None
|
||||
|
||||
def make_manufactured_work_order(self, planned_qty, produced_qty):
|
||||
"""Create a submitted WO and manufacture `produced_qty` of `planned_qty`.
|
||||
|
||||
The difference is booked as process loss on the Manufacture stock entry,
|
||||
which propagates to the work order's `process_loss_qty`.
|
||||
"""
|
||||
wo_order = make_wo_order_test_record(production_item="_Test FG Item", qty=planned_qty)
|
||||
|
||||
test_stock_entry.make_stock_entry(
|
||||
item_code="_Test Item", target="Stores - _TC", qty=100, basic_rate=100
|
||||
)
|
||||
test_stock_entry.make_stock_entry(
|
||||
item_code="_Test Item Home Desktop 100", target="Stores - _TC", qty=100, basic_rate=100
|
||||
)
|
||||
|
||||
transfer = frappe.get_doc(
|
||||
make_stock_entry(wo_order.name, "Material Transfer for Manufacture", planned_qty)
|
||||
)
|
||||
for d in transfer.get("items"):
|
||||
d.s_warehouse = "Stores - _TC"
|
||||
transfer.insert()
|
||||
transfer.submit()
|
||||
|
||||
manufacture = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", planned_qty))
|
||||
# Reduce the finished good qty below fg_completed_qty so the difference is
|
||||
# recorded as process loss.
|
||||
process_loss_qty = planned_qty - produced_qty
|
||||
if process_loss_qty:
|
||||
for d in manufacture.get("items"):
|
||||
if d.is_finished_item:
|
||||
d.qty = produced_qty
|
||||
d.transfer_qty = produced_qty * (d.conversion_factor or 1)
|
||||
manufacture.insert()
|
||||
manufacture.submit()
|
||||
|
||||
wo_order.reload()
|
||||
return wo_order
|
||||
|
||||
def test_work_order_with_process_loss_is_listed(self):
|
||||
wo_order = self.make_manufactured_work_order(planned_qty=5, produced_qty=4)
|
||||
|
||||
self.assertEqual(wo_order.process_loss_qty, 1)
|
||||
self.assertEqual(wo_order.produced_qty, 4)
|
||||
|
||||
data = self.run_report(work_order=wo_order.name)
|
||||
row = self.find_row(data, wo_order.name)
|
||||
|
||||
self.assertIsNotNone(row, "Work order with process loss should appear in the report")
|
||||
self.assertEqual(row.production_item, "_Test FG Item")
|
||||
self.assertEqual(row.qty_to_manufacture, 5)
|
||||
self.assertEqual(row.produced_qty, 4)
|
||||
self.assertEqual(row.process_loss_qty, 1)
|
||||
|
||||
# total_pl_value = process_loss_qty * (total_fg_value / qty_to_manufacture)
|
||||
expected_pl_value = row.process_loss_qty * (row.total_fg_value / row.qty_to_manufacture)
|
||||
self.assertAlmostEqual(row.total_pl_value, expected_pl_value)
|
||||
self.assertGreater(row.total_pl_value, 0)
|
||||
|
||||
def test_work_order_without_process_loss_is_not_listed(self):
|
||||
wo_order = self.make_manufactured_work_order(planned_qty=5, produced_qty=5)
|
||||
|
||||
self.assertEqual(wo_order.process_loss_qty, 0)
|
||||
self.assertEqual(wo_order.produced_qty, 5)
|
||||
|
||||
data = self.run_report(work_order=wo_order.name)
|
||||
self.assertIsNone(
|
||||
self.find_row(data, wo_order.name),
|
||||
"Work order that produced the full planned qty should not appear (no loss)",
|
||||
)
|
||||
|
||||
def test_item_and_work_order_filters_are_ineffective(self):
|
||||
"""BUG: the `item` and `work_order` filters in process_loss_report.get_data
|
||||
call `query.where(...)` without reassigning the result. frappe's query
|
||||
builder is immutable, so `.where()` returns a new query and these extra
|
||||
conditions are silently dropped. A non-matching item filter therefore fails
|
||||
to exclude the row. This test documents the current (buggy) behaviour; if the
|
||||
report is fixed to reassign the query, update the assertion below to
|
||||
`assertIsNone`.
|
||||
"""
|
||||
wo_order = self.make_manufactured_work_order(planned_qty=5, produced_qty=4)
|
||||
|
||||
# A non-matching item filter should exclude the row, but currently does not.
|
||||
data = self.run_report(item="_Test FG Item 2")
|
||||
self.assertIsNotNone(
|
||||
self.find_row(data, wo_order.name),
|
||||
"Filter bug regressed/fixed: `item` filter now takes effect - update this test",
|
||||
)
|
||||
@@ -432,9 +432,10 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-05-05 11:00:26.131777",
|
||||
"modified": "2026-06-14 13:44:07.420267",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"module_onboarding": "Manufacturing Onboarding",
|
||||
"name": "Manufacturing",
|
||||
"number_cards": [
|
||||
{
|
||||
@@ -458,6 +459,465 @@
|
||||
"roles": [],
|
||||
"sequence_id": 8.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "home",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Manufacturing",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "chart",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dashboard",
|
||||
"link_to": "Manufacturing",
|
||||
"link_type": "Dashboard",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "list-tree",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "BOM",
|
||||
"link_to": "BOM",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "factory",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Work Order",
|
||||
"link_to": "Work Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "person-standing",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Job Card",
|
||||
"link_to": "Job Card",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "stock",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Entry",
|
||||
"link_to": "Stock Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "getting-started",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Material Planning",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Lead Time",
|
||||
"link_to": "Item Lead Time",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Production Plan",
|
||||
"link_to": "Production Plan",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Forecasting",
|
||||
"link_to": "Exponential Smoothing Forecasting",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Master Production Schedule",
|
||||
"link_to": "Master Production Schedule",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Forecast",
|
||||
"link_to": "Sales Forecast",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Production Planning Report",
|
||||
"link_to": "Production Planning Report",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "tool",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Tools",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "BOM Creator",
|
||||
"link_to": "BOM Creator",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "BOM Update Tool",
|
||||
"link_to": "BOM Update Tool",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "BOM Comparison Tool",
|
||||
"link_to": "bom-comparison-tool",
|
||||
"link_type": "Page",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Downtime Entry",
|
||||
"link_to": "Downtime Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "notepad-text",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Production Planning Report",
|
||||
"link_to": "Production Planning Report",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Work Order Summary",
|
||||
"link_to": "Work Order Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Inspection Summary",
|
||||
"link_to": "Quality Inspection Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Downtime Analysis",
|
||||
"link_to": "Downtime Analysis",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Job Card Summary",
|
||||
"link_to": "Job Card Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "BOM Search",
|
||||
"link_to": "BOM Search",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Production Analytics",
|
||||
"link_to": "Production Analytics",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "BOM Operations Time",
|
||||
"link_to": "BOM Operations Time",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Work Order Consumed Materials",
|
||||
"link_to": "Work Order Consumed Materials",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Warehouse",
|
||||
"link_to": "Warehouse",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Operation",
|
||||
"link_to": "Operation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Workstation",
|
||||
"link_to": "Workstation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Workstation Type",
|
||||
"link_to": "Workstation Type",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Plant Floor",
|
||||
"link_to": "Plant Floor",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Routing",
|
||||
"link_to": "Routing",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "settings",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Settings",
|
||||
"link_to": "Manufacturing Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Manufacturing",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -492,3 +492,4 @@ erpnext.patches.v16_0.rename_subscription_billing_period_fields
|
||||
erpnext.patches.v16_0.drop_redundant_serial_no_index_from_sabb
|
||||
erpnext.patches.v16_0.set_default_close_opportunity_after_days
|
||||
execute:frappe.db.set_single_value("Accounts Settings", "pcv_job_timeout", 3600)
|
||||
erpnext.patches.v16_0.backfill_pick_list_transferred_qty
|
||||
|
||||
58
erpnext/patches/v16_0/backfill_pick_list_transferred_qty.py
Normal file
58
erpnext/patches/v16_0/backfill_pick_list_transferred_qty.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import frappe
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.utils import flt
|
||||
|
||||
|
||||
def execute():
|
||||
StockEntry = frappe.qb.DocType("Stock Entry")
|
||||
StockEntryDetail = frappe.qb.DocType("Stock Entry Detail")
|
||||
|
||||
pick_lists = (
|
||||
frappe.qb.from_(StockEntry)
|
||||
.select(StockEntry.pick_list)
|
||||
.distinct()
|
||||
.where((StockEntry.pick_list.isnotnull()) & (StockEntry.docstatus == 1))
|
||||
).run(pluck=True)
|
||||
|
||||
if not pick_lists:
|
||||
return
|
||||
|
||||
rows = (
|
||||
frappe.qb.from_(StockEntryDetail)
|
||||
.join(StockEntry)
|
||||
.on(StockEntryDetail.parent == StockEntry.name)
|
||||
.select(
|
||||
StockEntry.pick_list,
|
||||
StockEntryDetail.item_code,
|
||||
StockEntryDetail.s_warehouse,
|
||||
Sum(StockEntryDetail.transfer_qty).as_("qty"),
|
||||
)
|
||||
.where((StockEntry.pick_list.isin(pick_lists)) & (StockEntry.docstatus == 1))
|
||||
.groupby(StockEntry.pick_list, StockEntryDetail.item_code, StockEntryDetail.s_warehouse)
|
||||
).run(as_dict=True)
|
||||
|
||||
transferred = {(r.pick_list, r.item_code, r.s_warehouse): flt(r.qty) for r in rows}
|
||||
|
||||
items = frappe.get_all(
|
||||
"Pick List Item",
|
||||
filters={"parent": ("in", pick_lists), "picked_qty": (">", 0)},
|
||||
fields=["name", "parent", "item_code", "warehouse", "picked_qty"],
|
||||
order_by="idx",
|
||||
)
|
||||
|
||||
updates = {}
|
||||
for row in items:
|
||||
key = (row.parent, row.item_code, row.warehouse)
|
||||
available = transferred.get(key, 0)
|
||||
if available <= 0:
|
||||
continue
|
||||
qty = min(flt(row.picked_qty), available)
|
||||
transferred[key] = available - qty
|
||||
updates[row.name] = {"transferred_qty": qty}
|
||||
|
||||
if not updates:
|
||||
return
|
||||
|
||||
frappe.db.auto_commit_on_many_writes = True
|
||||
frappe.db.bulk_update("Pick List Item", updates)
|
||||
frappe.db.auto_commit_on_many_writes = False
|
||||
@@ -9,7 +9,7 @@ from frappe import _, throw
|
||||
from frappe.desk.form.assign_to import clear, close_all_assignments
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.query_builder.functions import Max, Min, Sum
|
||||
from frappe.utils import add_days, add_to_date, cstr, date_diff, flt, get_link_to_form, getdate, today
|
||||
from frappe.utils import add_days, add_to_date, date_diff, flt, get_link_to_form, getdate, today
|
||||
from frappe.utils.data import format_date
|
||||
from frappe.utils.nestedset import NestedSet
|
||||
|
||||
@@ -247,25 +247,32 @@ class Task(NestedSet):
|
||||
def check_recursion(self):
|
||||
if self.flags.ignore_recursion_check:
|
||||
return
|
||||
check_list = [["task", "parent"], ["parent", "task"]]
|
||||
for d in check_list:
|
||||
task_list, count = [self.name], 0
|
||||
while len(task_list) > count:
|
||||
tasks = frappe.get_all(
|
||||
"Task Depends On",
|
||||
filters={d[1]: cstr(task_list[count])},
|
||||
fields=[d[0]],
|
||||
as_list=True,
|
||||
)
|
||||
count = count + 1
|
||||
for b in tasks:
|
||||
if b[0] == self.name:
|
||||
frappe.throw(_("Circular Reference Error"), CircularReferenceError)
|
||||
if b[0]:
|
||||
task_list.append(b[0])
|
||||
# "Task Depends On" is a directed edge (parent depends on `task`); a cycle exists if this
|
||||
# task is reachable from itself along either direction. One recursive CTE per direction
|
||||
# fetches the whole reachable set in a single query -- UNION makes it cycle-safe at any
|
||||
# depth, so unlike the old per-node BFS it needs no arbitrary depth cap.
|
||||
for select_field, filter_field in (("task", "parent"), ("parent", "task")):
|
||||
if self._reaches_self(select_field, filter_field):
|
||||
frappe.throw(_("Circular Reference Error"), CircularReferenceError)
|
||||
|
||||
if count == 15:
|
||||
break
|
||||
def _reaches_self(self, select_field: str, filter_field: str) -> bool:
|
||||
depends_on = frappe.qb.DocType("Task Depends On")
|
||||
tree = frappe.qb.Table("dependency_tree")
|
||||
seed = (
|
||||
frappe.qb.from_(depends_on)
|
||||
.select(depends_on[select_field].as_("node"))
|
||||
.where(depends_on[filter_field] == self.name)
|
||||
)
|
||||
recursion = (
|
||||
frappe.qb.from_(depends_on)
|
||||
.join(tree)
|
||||
.on(depends_on[filter_field] == tree.node)
|
||||
.select(depends_on[select_field])
|
||||
)
|
||||
reachable = (
|
||||
frappe.qb.with_(seed + recursion, "dependency_tree", recursive=True).from_(tree).select(tree.node)
|
||||
).run(pluck=True)
|
||||
return self.name in reachable
|
||||
|
||||
def reschedule_dependent_tasks(self):
|
||||
end_date = self.exp_end_date or self.act_end_date
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
"is_hidden": 0,
|
||||
"label": "Projects",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Projects",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
@@ -37,6 +45,28 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Project",
|
||||
"link_count": 0,
|
||||
"link_to": "Project",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Task",
|
||||
"link_count": 0,
|
||||
"link_to": "Task",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -59,6 +89,17 @@
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Project Template",
|
||||
"link_count": 0,
|
||||
"link_to": "Project Template",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -70,6 +111,28 @@
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Project Type",
|
||||
"link_count": 0,
|
||||
"link_to": "Project Type",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Project",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Project Update",
|
||||
"link_count": 0,
|
||||
"link_to": "Project Update",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Project",
|
||||
"hidden": 0,
|
||||
@@ -89,6 +152,14 @@
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Time Tracking",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -100,6 +171,28 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Timesheet",
|
||||
"link_count": 0,
|
||||
"link_to": "Timesheet",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Activity Type",
|
||||
"link_count": 0,
|
||||
"link_to": "Activity Type",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -122,6 +215,17 @@
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Activity Type",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Activity Cost",
|
||||
"link_count": 0,
|
||||
"link_to": "Activity Cost",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
@@ -130,6 +234,25 @@
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Reports",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "Timesheet",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Daily Timesheet Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Daily Timesheet Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Timesheet",
|
||||
"hidden": 0,
|
||||
@@ -152,6 +275,17 @@
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Project",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Project wise Stock Tracking",
|
||||
"link_count": 0,
|
||||
"link_to": "Project wise Stock Tracking",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Project",
|
||||
"hidden": 0,
|
||||
@@ -163,6 +297,28 @@
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Project",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Timesheet Billing Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Timesheet Billing Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Task",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Delayed Tasks Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Delayed Tasks Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Task",
|
||||
"hidden": 0,
|
||||
@@ -182,6 +338,24 @@
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
@@ -193,9 +367,10 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-01-02 17:26:44.644507",
|
||||
"modified": "2026-07-01 13:20:50.651608",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"module_onboarding": "Projects Onboarding",
|
||||
"name": "Projects",
|
||||
"number_cards": [
|
||||
{
|
||||
@@ -219,6 +394,250 @@
|
||||
"roles": [],
|
||||
"sequence_id": 11.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "home",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Projects",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "chart",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dashboard",
|
||||
"link_to": "Project",
|
||||
"link_type": "Dashboard",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "projects",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Project",
|
||||
"link_to": "Project",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "list-todo",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Task",
|
||||
"link_to": "Task",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "calendar-clock",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Timesheet",
|
||||
"link_to": "Timesheet",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Activity Type",
|
||||
"link_to": "Activity Type",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Activity Cost",
|
||||
"link_to": "Activity Cost",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Project Template",
|
||||
"link_to": "Project Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Project Type",
|
||||
"link_to": "Project Type",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Project Update",
|
||||
"link_to": "Project Update",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "sheet",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Project Summary",
|
||||
"link_to": "Project Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Daily Timesheet Summary",
|
||||
"link_to": "Daily Timesheet Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Timesheet Billing Summary",
|
||||
"link_to": "Timesheet Billing Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Project wise Stock Tracking",
|
||||
"link_to": "Project wise Stock Tracking",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Delayed Tasks Summary",
|
||||
"link_to": "Delayed Tasks Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "settings",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Settings",
|
||||
"link_to": "Projects Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Projects",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-01-02 17:32:47.522875",
|
||||
"modified": "2026-06-14 13:44:07.920643",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Quality Management",
|
||||
"name": "Quality",
|
||||
@@ -174,6 +174,161 @@
|
||||
"roles": [],
|
||||
"sequence_id": 9.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "home",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Quality",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "inspection-panel",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Inspection",
|
||||
"link_to": "Quality Inspection",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "goal",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Goal",
|
||||
"link_to": "Quality Goal",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "review",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Review",
|
||||
"link_to": "Quality Review",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "square-activity",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Action",
|
||||
"link_to": "Quality Action",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "grid-2x2-check",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Non Conformance",
|
||||
"link_to": "Non Conformance",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "thumbs-up",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Feedback",
|
||||
"link_to": "Quality Feedback",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "users",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Meeting",
|
||||
"link_to": "Quality Meeting",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Procedure",
|
||||
"link_to": "Quality Procedure",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Feedback Template",
|
||||
"link_to": "Quality Feedback Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Inspection Template",
|
||||
"link_to": "Quality Inspection Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Quality",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ def make_quotation(source_name: str, target_doc: str | Document | None = None):
|
||||
)
|
||||
|
||||
target_doc.quotation_to = "Customer"
|
||||
target_doc.run_method("set_missing_values")
|
||||
target_doc.run_method("set_other_charges")
|
||||
target_doc.run_method("calculate_taxes_and_totals")
|
||||
|
||||
price_list, currency = frappe.db.get_value(
|
||||
"Customer", {"name": source_name}, ["default_price_list", "default_currency"]
|
||||
@@ -33,6 +30,10 @@ def make_quotation(source_name: str, target_doc: str | Document | None = None):
|
||||
if currency:
|
||||
target_doc.currency = currency
|
||||
|
||||
target_doc.run_method("set_missing_values")
|
||||
target_doc.run_method("set_other_charges")
|
||||
target_doc.run_method("calculate_taxes_and_totals")
|
||||
|
||||
return target_doc
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import flt, nowdate
|
||||
|
||||
from erpnext.accounts.party import get_due_date
|
||||
from erpnext.exceptions import PartyDisabled, PartyFrozen
|
||||
@@ -14,12 +14,53 @@ from erpnext.selling.doctype.customer.customer import (
|
||||
get_customer_outstanding,
|
||||
)
|
||||
from erpnext.selling.doctype.customer.mapper import (
|
||||
make_quotation,
|
||||
parse_full_name,
|
||||
)
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestCustomer(ERPNextTestSuite):
|
||||
def test_quotation_from_customer_uses_actual_exchange_rate(self):
|
||||
company = "_Test Company"
|
||||
company_currency = frappe.get_cached_value("Company", company, "default_currency")
|
||||
foreign_currency = "USD" if company_currency != "USD" else "EUR"
|
||||
|
||||
frappe.defaults.set_user_default("company", company)
|
||||
self.addCleanup(frappe.defaults.clear_user_default, "company")
|
||||
|
||||
# Seed a deterministic rate so the test does not depend on the live exchange-rate API.
|
||||
rate = 83.0
|
||||
exchange = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Currency Exchange",
|
||||
"date": nowdate(),
|
||||
"from_currency": foreign_currency,
|
||||
"to_currency": company_currency,
|
||||
"exchange_rate": rate,
|
||||
"for_selling": 1,
|
||||
"for_buying": 1,
|
||||
}
|
||||
).insert(ignore_if_duplicate=True)
|
||||
self.addCleanup(frappe.delete_doc, "Currency Exchange", exchange.name, force=1)
|
||||
|
||||
customer = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Customer",
|
||||
"customer_name": "_Test Customer FX Quotation",
|
||||
"customer_type": "Company",
|
||||
"default_currency": foreign_currency,
|
||||
}
|
||||
).insert()
|
||||
self.addCleanup(frappe.delete_doc, "Customer", customer.name, force=1)
|
||||
|
||||
quotation = make_quotation(customer.name)
|
||||
|
||||
self.assertEqual(quotation.currency, foreign_currency)
|
||||
self.assertNotEqual(flt(quotation.conversion_rate), 1.0)
|
||||
self.assertNotEqual(flt(quotation.conversion_rate), 0.0)
|
||||
self.assertEqual(flt(quotation.conversion_rate), rate)
|
||||
|
||||
def test_get_customer_name_dedupes_with_numeric_suffix(self):
|
||||
# When a customer name already exists, get_customer_name appends "- <max suffix + 1>". The
|
||||
# Postgres branch extracts the suffix with regexp_replace/NULLIF/CAST (pypika's Substring cannot
|
||||
|
||||
@@ -228,7 +228,7 @@ def _make_customer(source_name, ignore_permissions=False):
|
||||
|
||||
|
||||
def create_customer_from_lead(lead_name, ignore_permissions=False):
|
||||
from erpnext.crm.doctype.lead.lead import _make_customer
|
||||
from erpnext.crm.doctype.lead.mapper import _make_customer
|
||||
|
||||
customer = _make_customer(lead_name, ignore_permissions=ignore_permissions)
|
||||
customer.flags.ignore_permissions = ignore_permissions
|
||||
|
||||
@@ -622,9 +622,10 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-02-19 13:01:26.893303",
|
||||
"modified": "2026-06-14 13:44:07.820564",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"module_onboarding": "Selling Onboarding",
|
||||
"name": "Selling",
|
||||
"number_cards": [
|
||||
{
|
||||
@@ -648,6 +649,762 @@
|
||||
"roles": [],
|
||||
"sequence_id": 6.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "home",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Selling",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "chart",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dashboard",
|
||||
"link_to": "Selling",
|
||||
"link_type": "Dashboard",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "receipt-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quotation",
|
||||
"link_to": "Quotation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sell",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Order",
|
||||
"link_to": "Sales Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "receipt",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Invoice",
|
||||
"link_to": "Sales Invoice",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "computer",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "POS",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "POS",
|
||||
"link_to": "point-of-sale",
|
||||
"link_type": "Page",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "POS Profile",
|
||||
"link_to": "POS Profile",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "POS Invoice",
|
||||
"link_to": "POS Invoice",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "POS Opening Entry",
|
||||
"link_to": "POS Opening Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "POS Closing Entry",
|
||||
"link_to": "POS Closing Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "POS Invoice Merge Log",
|
||||
"link_to": "POS Invoice Merge Log",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "POS Settings",
|
||||
"link_to": "POS Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Loyalty Program",
|
||||
"link_to": "Loyalty Program",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Loyalty Point Entry",
|
||||
"link_to": "Loyalty Point Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "stock",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Items & Pricing",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Group",
|
||||
"link_to": "Item Group",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Price List",
|
||||
"link_to": "Price List",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Price",
|
||||
"link_to": "Item Price",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Pricing Rule",
|
||||
"link_to": "Pricing Rule",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Promotional Scheme",
|
||||
"link_to": "Promotional Scheme",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Coupon Code",
|
||||
"link_to": "Coupon Code",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Blanket Order",
|
||||
"link_to": "Blanket Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer",
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer Group",
|
||||
"link_to": "Customer Group",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Address",
|
||||
"link_to": "Address",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Contact",
|
||||
"link_to": "Contact",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Territory",
|
||||
"link_to": "Territory",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Campaign",
|
||||
"link_to": "Campaign",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Person",
|
||||
"link_to": "Sales Person",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Partner",
|
||||
"link_to": "Sales Partner",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Monthly Distribution",
|
||||
"link_to": "Monthly Distribution",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Terms Template",
|
||||
"link_to": "Terms and Conditions",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Tax Template",
|
||||
"link_to": "Sales Taxes and Charges Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Product Bundle",
|
||||
"link_to": "Product Bundle",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "UTM Source",
|
||||
"link_to": "UTM Source",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Shipping Rule",
|
||||
"link_to": "Shipping Rule",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sheet",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Register",
|
||||
"link_to": "Sales Register",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item-wise Sales Register",
|
||||
"link_to": "Item-wise Sales Register",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Analytics",
|
||||
"link_to": "Sales Analytics",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer Addresses And Contacts",
|
||||
"link_to": "Address And Contacts",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Inactive Customers",
|
||||
"link_to": "Inactive Customers",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Invoice Trends",
|
||||
"link_to": "Sales Invoice Trends",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer Credit Balance",
|
||||
"link_to": "Customer Credit Balance",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customers Without Any Sales Transactions",
|
||||
"link_to": "Customers Without Any Sales Transactions",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Partners Commission",
|
||||
"link_to": "Sales Partners Commission",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Available Stock for Packing Items",
|
||||
"link_to": "Available Stock for Packing Items",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Territory Target Variance Based On Item Group",
|
||||
"link_to": "Territory Target Variance Based On Item Group",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Person Target Variance Based On Item Group",
|
||||
"link_to": "Sales Person Target Variance Based On Item Group",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Partner Target Variance Based On Item Group",
|
||||
"link_to": "Sales Partner Target Variance based on Item Group",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Pending SO Items For Purchase Request",
|
||||
"link_to": "Pending SO Items For Purchase Request",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Funnel",
|
||||
"link_to": "sales-funnel",
|
||||
"link_type": "Page",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Order Analysis",
|
||||
"link_to": "Sales Order Analysis",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer Acquisition and Loyalty",
|
||||
"link_to": "Customer Acquisition and Loyalty",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quotation Trends",
|
||||
"link_to": "Quotation Trends",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Order Trends",
|
||||
"link_to": "Sales Order Trends",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item-wise Sales History",
|
||||
"link_to": "Item-wise Sales History",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Person-wise Transaction Summary",
|
||||
"link_to": "Sales Person-wise Transaction Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "settings",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Settings",
|
||||
"link_to": "Selling Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Selling",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -353,33 +353,48 @@
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "round_off_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Round Off Account",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "round_off_cost_center",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Round Off Cost Center",
|
||||
"no_copy": 1,
|
||||
"options": "Cost Center"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "write_off_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Write Off Account",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "exchange_gain_loss_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Exchange Gain / Loss Account",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "unrealized_exchange_gain_loss_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Unrealized Exchange Gain/Loss Account",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
@@ -526,15 +541,19 @@
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "accumulated_depreciation_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Accumulated Depreciation Account",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "depreciation_expense_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Depreciation Expense Account",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
@@ -549,29 +568,39 @@
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "disposal_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Gain/Loss Account on Asset Disposal",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "depreciation_cost_center",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Asset Depreciation Cost Center",
|
||||
"no_copy": 1,
|
||||
"options": "Cost Center"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "capital_work_in_progress_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Capital Work In Progress Account",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "asset_received_but_not_billed",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Asset Received But Not Billed",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
@@ -703,15 +732,21 @@
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "unrealized_profit_loss_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Unrealized Profit / Loss Account",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "default_discount_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Default Payment Discount Account",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
@@ -753,8 +788,10 @@
|
||||
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/advance-in-separate-party-account",
|
||||
"fieldname": "default_advance_received_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Default Advance Received Account",
|
||||
"mandatory_depends_on": "book_advance_payments_as_liability",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
@@ -763,8 +800,10 @@
|
||||
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/advance-in-separate-party-account",
|
||||
"fieldname": "default_advance_paid_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Default Advance Paid Account",
|
||||
"mandatory_depends_on": "book_advance_payments_as_liability",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
@@ -844,9 +883,12 @@
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "round_off_for_opening",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Round Off for Opening",
|
||||
"no_copy": 1,
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
@@ -1026,7 +1068,7 @@
|
||||
"image_field": "company_logo",
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2026-07-01 11:48:07.853494",
|
||||
"modified": "2026-07-02 07:21:21.794533",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Company",
|
||||
|
||||
@@ -84,6 +84,7 @@ class Company(NestedSet):
|
||||
default_operating_cost_account: DF.Link | None
|
||||
default_payable_account: DF.Link | None
|
||||
default_provisional_account: DF.Link | None
|
||||
default_purchase_price_variance_account: DF.Link | None
|
||||
default_receivable_account: DF.Link | None
|
||||
default_sales_contact: DF.Link | None
|
||||
default_scrap_warehouse: DF.Link | None
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
frappe.listview_settings["Company"] = {
|
||||
onload() {
|
||||
frappe.breadcrumbs.add("Accounts");
|
||||
},
|
||||
};
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-01-09 13:05:08.007297",
|
||||
"modified": "2026-06-14 13:43:50.429297",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "ERPNext Settings",
|
||||
@@ -128,6 +128,236 @@
|
||||
"type": "DocType"
|
||||
}
|
||||
],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "earth",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Global Defaults",
|
||||
"link_to": "Global Defaults",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "washing-machine",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "System Settings",
|
||||
"link_to": "System Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "accounting",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Accounts Settings",
|
||||
"link_to": "Accounts Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "computer",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "POS Settings",
|
||||
"link_to": "POS Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sell",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Selling Settings",
|
||||
"link_to": "Selling Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "buying",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Buying Settings",
|
||||
"link_to": "Buying Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "stock",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Settings",
|
||||
"link_to": "Stock Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "building-2",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Manufacturing Settings",
|
||||
"link_to": "Manufacturing Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "projects",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Projects Settings",
|
||||
"link_to": "Projects Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "crm",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "CRM Settings",
|
||||
"link_to": "CRM Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "support",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Support Settings",
|
||||
"link_to": "Support Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "getting-started",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Other Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Subscription Settings",
|
||||
"link_to": "Subscription Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Variant Settings",
|
||||
"link_to": "Item Variant Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Delivery Settings",
|
||||
"link_to": "Delivery Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Currency Exchange Settings",
|
||||
"link_to": "Currency Exchange Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Appointment Booking Settings",
|
||||
"link_to": "Appointment Booking Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Reposting Settings",
|
||||
"link_to": "Stock Reposting Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "ERPNext Settings",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[{\"id\":\"aCk49ShVRs\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Home\",\"col\":12}},{\"id\":\"kb3XPLg8lb\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"id\":\"nWd2KJPW8l\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"snrzfbFr5Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":3}},{\"id\":\"SHJKakmLLf\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Supplier\",\"col\":3}},{\"id\":\"CPxEyhaf3G\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"WU4F-HUcIQ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leaderboard\",\"col\":3}},{\"id\":\"d_KVM1gsf9\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"JVu8-FJZCu\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"JiuSi0ubOg\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting\",\"col\":4}},{\"id\":\"ji2Jlm3Q8i\",\"type\":\"card\",\"data\":{\"card_name\":\"Stock\",\"col\":4}},{\"id\":\"N61oiXpuwK\",\"type\":\"card\",\"data\":{\"card_name\":\"CRM\",\"col\":4}},{\"id\":\"6J0CVl1mPo\",\"type\":\"card\",\"data\":{\"card_name\":\"Data Import and Settings\",\"col\":4}}]",
|
||||
"content": "[{\"id\":\"kb3XPLg8lb\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"id\":\"nWd2KJPW8l\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"snrzfbFr5Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":3}},{\"id\":\"SHJKakmLLf\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Supplier\",\"col\":3}},{\"id\":\"CPxEyhaf3G\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"d_KVM1gsf9\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"JVu8-FJZCu\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"JiuSi0ubOg\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting\",\"col\":4}},{\"id\":\"ji2Jlm3Q8i\",\"type\":\"card\",\"data\":{\"card_name\":\"Stock\",\"col\":4}},{\"id\":\"N61oiXpuwK\",\"type\":\"card\",\"data\":{\"card_name\":\"CRM\",\"col\":4}},{\"id\":\"6J0CVl1mPo\",\"type\":\"card\",\"data\":{\"card_name\":\"Data Import and Settings\",\"col\":4}}]",
|
||||
"creation": "2020-01-23 13:46:38.833076",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
@@ -13,6 +13,14 @@
|
||||
"is_hidden": 0,
|
||||
"label": "Home",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounting",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
@@ -32,6 +40,28 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Chart of Accounts",
|
||||
"link_count": 0,
|
||||
"link_to": "Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Company",
|
||||
"link_count": 0,
|
||||
"link_to": "Company",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -54,6 +84,28 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Customer",
|
||||
"link_count": 0,
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Supplier",
|
||||
"link_count": 0,
|
||||
"link_to": "Supplier",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -73,6 +125,14 @@
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Stock",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -84,6 +144,28 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Item",
|
||||
"link_count": 0,
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Warehouse",
|
||||
"link_count": 0,
|
||||
"link_to": "Warehouse",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -106,6 +188,17 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Brand",
|
||||
"link_count": 0,
|
||||
"link_to": "Brand",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -117,6 +210,28 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Unit of Measure (UOM)",
|
||||
"link_count": 0,
|
||||
"link_to": "UOM",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Stock Reconciliation",
|
||||
"link_count": 0,
|
||||
"link_to": "Stock Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -136,6 +251,25 @@
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "CRM",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Lead",
|
||||
"link_count": 0,
|
||||
"link_to": "Lead",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -158,6 +292,28 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Customer Group",
|
||||
"link_count": 0,
|
||||
"link_to": "Customer Group",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Territory",
|
||||
"link_count": 0,
|
||||
"link_to": "Territory",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -177,6 +333,14 @@
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Data Import and Settings",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -188,6 +352,28 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Import Data",
|
||||
"link_count": 0,
|
||||
"link_to": "Data Import",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Opening Invoice Creation Tool",
|
||||
"link_count": 0,
|
||||
"link_to": "Opening Invoice Creation Tool",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -210,6 +396,28 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Chart of Accounts Importer",
|
||||
"link_count": 0,
|
||||
"link_to": "Chart of Accounts Importer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Letter Head",
|
||||
"link_count": 0,
|
||||
"link_to": "Letter Head",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -221,6 +429,17 @@
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Email Account",
|
||||
"link_count": 0,
|
||||
"link_to": "Email Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
@@ -233,7 +452,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2025-07-02 14:12:28.407612",
|
||||
"modified": "2026-07-01 14:22:16.927245",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Home",
|
||||
@@ -267,6 +486,74 @@
|
||||
"type": "DocType"
|
||||
}
|
||||
],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Home",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Customer",
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Supplier",
|
||||
"link_to": "Supplier",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Invoice",
|
||||
"link_to": "Sales Invoice",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Home",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
204
erpnext/setup/workspace/organization/organization.json
Normal file
204
erpnext/setup/workspace/organization/organization.json
Normal file
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"allowed_users": [
|
||||
{
|
||||
"user": "Administrator"
|
||||
},
|
||||
{
|
||||
"user": "Guest"
|
||||
},
|
||||
{
|
||||
"user": "accounts@test.com"
|
||||
},
|
||||
{
|
||||
"user": "ankush@erpnext.com"
|
||||
},
|
||||
{
|
||||
"user": "faris@erpnext.com"
|
||||
},
|
||||
{
|
||||
"user": "mention_test_user@example.com"
|
||||
},
|
||||
{
|
||||
"user": "project@frappe.io"
|
||||
},
|
||||
{
|
||||
"user": "rushabh@erpnext.com"
|
||||
},
|
||||
{
|
||||
"user": "saqib@erpnext.com"
|
||||
},
|
||||
{
|
||||
"user": "soham@frappe.io"
|
||||
},
|
||||
{
|
||||
"user": "sohamengineer123@gmail.com"
|
||||
},
|
||||
{
|
||||
"user": "sohamkulkarns9@gmail.com"
|
||||
},
|
||||
{
|
||||
"user": "sydel@frappe.io"
|
||||
},
|
||||
{
|
||||
"user": "test'5@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test1@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test2@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test3@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test4@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test@portal.com"
|
||||
},
|
||||
{
|
||||
"user": "testpassword@example.com"
|
||||
},
|
||||
{
|
||||
"user": "testperm@example.com"
|
||||
},
|
||||
{
|
||||
"user": "web@web.com"
|
||||
}
|
||||
],
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-11 11:51:21.789012",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "organization",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Organization",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-06-16 00:45:57.595188",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"module_onboarding": "Organization Onboarding",
|
||||
"name": "Organization",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 46.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 1,
|
||||
"icon": "organization",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Company",
|
||||
"link_to": "Company",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "book-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Letter Head",
|
||||
"link_to": "Letter Head",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "file-user",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Department",
|
||||
"link_to": "Department",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "book-user",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Branch",
|
||||
"link_to": "Branch",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "users",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "User",
|
||||
"link_to": "User",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "user-round-check",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Role Permissions",
|
||||
"link_to": "permission-manager",
|
||||
"link_type": "Page",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "mail",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Email Account",
|
||||
"link_to": "Email Account",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Organization",
|
||||
"type": "Workspace"
|
||||
}
|
||||
@@ -1074,62 +1074,141 @@ $.extend(erpnext.item, {
|
||||
|
||||
function make_fields_from_attribute_values(attr_dict) {
|
||||
let fields = [];
|
||||
let att_key = frm.doc.attributes.map((idx) => idx.attribute);
|
||||
att_key.forEach((name, i) => {
|
||||
let attributes = frm.doc.attributes.filter((row) => !row.disabled);
|
||||
attributes.forEach((row, i) => {
|
||||
let name = row.attribute;
|
||||
if (i % 3 === 0) {
|
||||
fields.push({ fieldtype: "Section Break" });
|
||||
}
|
||||
fields.push({ fieldtype: "Column Break", label: name });
|
||||
fields.push({ fieldtype: "Column Break" });
|
||||
fields.push({
|
||||
fieldtype: "Data",
|
||||
placeholder: "Search",
|
||||
fieldname: `search_${frappe.scrub(name)}`,
|
||||
onchange: function (e) {
|
||||
let value = e.target.value;
|
||||
let result = attr_dict[name].filter((attr_value) =>
|
||||
attr_value.toString().toLowerCase().includes(value.toLowerCase())
|
||||
);
|
||||
attr_dict[name].forEach((attr_value) => {
|
||||
if (result.includes(attr_value)) {
|
||||
me.multiple_variant_dialog.set_df_property(attr_value, "hidden", 0);
|
||||
} else {
|
||||
me.multiple_variant_dialog.set_df_property(attr_value, "hidden", 1);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
attr_dict[name].forEach((value) => {
|
||||
fields.push({
|
||||
fieldtype: "Check",
|
||||
label: value,
|
||||
fieldname: value,
|
||||
default: 0,
|
||||
onchange: function () {
|
||||
let selected_attributes = get_selected_attributes();
|
||||
let lengths = Object.keys(selected_attributes).map((key) => {
|
||||
return selected_attributes[key].length;
|
||||
});
|
||||
if (!lengths.length) {
|
||||
me.multiple_variant_dialog.get_primary_btn().html(__("Create Variants"));
|
||||
me.multiple_variant_dialog.disable_primary_action();
|
||||
} else {
|
||||
let no_of_combinations = lengths.reduce((a, b) => a * b, 1);
|
||||
let msg;
|
||||
if (no_of_combinations === 1) {
|
||||
msg = __("Make {0} Variant", [no_of_combinations]);
|
||||
} else {
|
||||
msg = __("Make {0} Variants", [no_of_combinations]);
|
||||
}
|
||||
me.multiple_variant_dialog.get_primary_btn().html(msg);
|
||||
me.multiple_variant_dialog.enable_primary_action();
|
||||
}
|
||||
},
|
||||
});
|
||||
fieldtype: "MultiSelectPills",
|
||||
label: name,
|
||||
fieldname: frappe.scrub(name),
|
||||
placeholder: __("Search values..."),
|
||||
get_data: (txt) => get_attribute_suggestions(attr_dict[name], txt),
|
||||
onchange: update_primary_action,
|
||||
});
|
||||
});
|
||||
return fields;
|
||||
}
|
||||
|
||||
function get_attribute_suggestions(spec, txt) {
|
||||
if (!spec) return [];
|
||||
return Array.isArray(spec) ? filter_list(spec, txt) : numeric_suggestions(spec, txt);
|
||||
}
|
||||
|
||||
// Cap matches so a long value list never hands everything to Awesomplete,
|
||||
// which would freeze the browser.
|
||||
function filter_list(values, txt) {
|
||||
txt = (txt || "").toLowerCase();
|
||||
let matches = [];
|
||||
for (let value of values) {
|
||||
if (!txt || value.toLowerCase().includes(txt)) {
|
||||
matches.push(value);
|
||||
if (matches.length >= 50) break;
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
// Numeric ranges aren't enumerated. With no input, preview the first few
|
||||
// values; once the user types, accept it only if it lies on the increment
|
||||
// within [from, to]. Both paths are cheap even for huge ranges.
|
||||
function numeric_suggestions(range, txt) {
|
||||
let { from_range: from, to_range: to, increment } = range;
|
||||
if (!(increment > 0) || from > to) return [];
|
||||
|
||||
txt = (txt || "").trim();
|
||||
if (!txt) {
|
||||
let preview = [];
|
||||
for (
|
||||
let value = from;
|
||||
value <= to && preview.length < 50;
|
||||
value = flt(value + increment, 6)
|
||||
) {
|
||||
preview.push(String(value));
|
||||
}
|
||||
return preview;
|
||||
}
|
||||
|
||||
return is_valid_attribute_value(range, txt) ? [String(flt(txt, 6))] : [];
|
||||
}
|
||||
|
||||
function is_valid_attribute_value(spec, value) {
|
||||
if (!spec || !value) return false;
|
||||
if (Array.isArray(spec)) return spec.includes(value);
|
||||
|
||||
let { from_range: from, to_range: to, increment } = spec;
|
||||
if (!(increment > 0)) return false;
|
||||
|
||||
// Reject anything that isn't cleanly a number ("abc", "5000xyz", "");
|
||||
// flt would coerce these to 0 and wrongly accept them.
|
||||
let text = String(value).trim();
|
||||
let num = Number(text);
|
||||
if (text === "" || !Number.isFinite(num)) return false;
|
||||
|
||||
if (num < from || num > to) return false;
|
||||
let steps = (num - from) / increment;
|
||||
return Math.abs(Math.round(steps) - steps) <= 1e-6;
|
||||
}
|
||||
|
||||
// Block variant creation if anything is wrong: an invalid committed pill, or
|
||||
// text typed but not added as a pill (which get_selected_attributes would
|
||||
// otherwise drop silently). The user must fix each before creation proceeds.
|
||||
function validate_selected_attributes() {
|
||||
let errors = [];
|
||||
frm.doc.attributes.forEach((row) => {
|
||||
if (row.disabled) return;
|
||||
let field = me.multiple_variant_dialog.get_field(frappe.scrub(row.attribute));
|
||||
if (!field) return;
|
||||
|
||||
let attribute = frappe.utils.escape_html(row.attribute);
|
||||
let spec = attr_val_fields[row.attribute];
|
||||
|
||||
let invalid = [
|
||||
...new Set((field.get_value() || []).filter((v) => !is_valid_attribute_value(spec, v))),
|
||||
];
|
||||
if (invalid.length) {
|
||||
let values = invalid.map(frappe.utils.escape_html).join(", ");
|
||||
errors.push(__("{0}: remove invalid value(s) {1}", [attribute, values]));
|
||||
}
|
||||
|
||||
let pending = (field.$input?.val() || "").trim();
|
||||
if (pending) {
|
||||
let value = frappe.utils.escape_html(pending);
|
||||
errors.push(
|
||||
__("{0}: select the typed value {1} from the list or clear it", [attribute, value])
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (errors.length) {
|
||||
frappe.throw({
|
||||
title: __("Invalid Attribute Values"),
|
||||
message: errors.join("<br>"),
|
||||
indicator: "red",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function update_primary_action() {
|
||||
let selected_attributes = get_selected_attributes();
|
||||
let counts = Object.keys(selected_attributes).map((key) => selected_attributes[key].length);
|
||||
if (!counts.length) {
|
||||
me.multiple_variant_dialog.get_primary_btn().html(__("Create Variants"));
|
||||
me.multiple_variant_dialog.disable_primary_action();
|
||||
} else {
|
||||
let no_of_combinations = counts.reduce((a, b) => a * b, 1);
|
||||
let msg =
|
||||
no_of_combinations === 1
|
||||
? __("Make {0} Variant", [no_of_combinations])
|
||||
: __("Make {0} Variants", [no_of_combinations]);
|
||||
me.multiple_variant_dialog.get_primary_btn().html(msg);
|
||||
me.multiple_variant_dialog.enable_primary_action();
|
||||
}
|
||||
}
|
||||
|
||||
function make_and_show_dialog(fields) {
|
||||
me.multiple_variant_dialog = new frappe.ui.Dialog({
|
||||
title: __("Select Attribute Values"),
|
||||
@@ -1155,6 +1234,8 @@ $.extend(erpnext.item, {
|
||||
});
|
||||
|
||||
me.multiple_variant_dialog.set_primary_action(__("Create Variants"), () => {
|
||||
validate_selected_attributes();
|
||||
|
||||
let selected_attributes = get_selected_attributes();
|
||||
let use_template_image = me.multiple_variant_dialog.get_value("use_template_image");
|
||||
|
||||
@@ -1182,72 +1263,70 @@ $.extend(erpnext.item, {
|
||||
});
|
||||
});
|
||||
|
||||
$($(me.multiple_variant_dialog.$wrapper.find(".form-column")).find(".frappe-control")).css(
|
||||
"margin-bottom",
|
||||
"0px"
|
||||
);
|
||||
|
||||
me.multiple_variant_dialog.disable_primary_action();
|
||||
me.multiple_variant_dialog.clear();
|
||||
me.multiple_variant_dialog.show();
|
||||
me.multiple_variant_dialog.$wrapper
|
||||
.find("div[data-fieldname^='search_']")
|
||||
.find(".clearfix")
|
||||
.hide();
|
||||
}
|
||||
|
||||
function get_selected_attributes() {
|
||||
let selected_attributes = {};
|
||||
me.multiple_variant_dialog.$wrapper.find(".form-column").each((i, col) => {
|
||||
if (i === 0) return;
|
||||
let attribute_name = $(col).find(".column-label").html().trim();
|
||||
selected_attributes[attribute_name] = [];
|
||||
let checked_opts = $(col).find(".checkbox input");
|
||||
checked_opts.each((i, opt) => {
|
||||
if ($(opt).is(":checked")) {
|
||||
selected_attributes[attribute_name].push($(opt).attr("data-fieldname"));
|
||||
}
|
||||
});
|
||||
if (!selected_attributes[attribute_name].length) {
|
||||
delete selected_attributes[attribute_name];
|
||||
frm.doc.attributes.forEach((row) => {
|
||||
if (row.disabled) return;
|
||||
let values = me.multiple_variant_dialog.get_value(frappe.scrub(row.attribute));
|
||||
if (values && values.length) {
|
||||
selected_attributes[row.attribute] = values;
|
||||
}
|
||||
});
|
||||
|
||||
return selected_attributes;
|
||||
}
|
||||
|
||||
frm.doc.attributes.forEach(function (d) {
|
||||
if (!d.disabled) {
|
||||
let p = new Promise((resolve) => {
|
||||
if (!d.numeric_values) {
|
||||
frappe
|
||||
.call({
|
||||
method: "frappe.client.get_list",
|
||||
args: {
|
||||
doctype: "Item Attribute Value",
|
||||
filters: [["parent", "=", d.attribute]],
|
||||
fields: ["attribute_value"],
|
||||
limit_page_length: 0,
|
||||
parent: "Item Attribute",
|
||||
order_by: "idx",
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
if (r.message) {
|
||||
attr_val_fields[d.attribute] = r.message.map(function (d) {
|
||||
return d.attribute_value;
|
||||
// Read the numeric configuration from the Item Attribute master
|
||||
// instead of the variant attribute row, which may be stale or
|
||||
// blank if the attribute was made numeric after it was added here.
|
||||
frappe.db
|
||||
.get_value("Item Attribute", d.attribute, [
|
||||
"numeric_values",
|
||||
"from_range",
|
||||
"to_range",
|
||||
"increment",
|
||||
])
|
||||
.then((res) => {
|
||||
let attr = res.message || {};
|
||||
|
||||
if (!attr.numeric_values) {
|
||||
frappe
|
||||
.call({
|
||||
method: "frappe.client.get_list",
|
||||
args: {
|
||||
doctype: "Item Attribute Value",
|
||||
filters: [["parent", "=", d.attribute]],
|
||||
fields: ["attribute_value"],
|
||||
limit_page_length: 0,
|
||||
parent: "Item Attribute",
|
||||
order_by: "idx",
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
attr_val_fields[d.attribute] = (r.message || []).map(
|
||||
(row) => row.attribute_value
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let values = [];
|
||||
for (var i = d.from_range; i <= d.to_range; i = flt(i + d.increment, 6)) {
|
||||
values.push(i);
|
||||
}
|
||||
attr_val_fields[d.attribute] = values;
|
||||
resolve();
|
||||
}
|
||||
} else {
|
||||
// Store the range instead of enumerating it; a large range
|
||||
// (e.g. 1-100000) is slow to build and to search. Values are
|
||||
// validated against the range on demand while typing.
|
||||
attr_val_fields[d.attribute] = {
|
||||
from_range: flt(attr.from_range),
|
||||
to_range: flt(attr.to_range),
|
||||
increment: flt(attr.increment),
|
||||
};
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
promises.push(p);
|
||||
|
||||
@@ -1372,7 +1372,8 @@ def get_purchase_voucher_details(doctype, item_code, document_name=None):
|
||||
query = query.select(parent_doc.transaction_date)
|
||||
query = query.orderby(parent_doc.transaction_date, parent_doc.name, order=Order.desc)
|
||||
|
||||
return query.run(as_dict=1)
|
||||
# only the latest ([0]) row is ever used, so fetch just that instead of every purchase of the item
|
||||
return query.limit(1).run(as_dict=1)
|
||||
|
||||
|
||||
def check_stock_uom_with_bin(item, stock_uom):
|
||||
@@ -1762,3 +1763,13 @@ def get_default_warehouse_for_opening_stock(item, company: str, warehouse: str |
|
||||
"No warehouse found for company {0}. Please set a Default Warehouse in Item Defaults or Stock Settings."
|
||||
).format(frappe.bold(company))
|
||||
)
|
||||
|
||||
|
||||
def on_doctype_update():
|
||||
if frappe.db.db_type == "postgres":
|
||||
# The Item link-search (erpnext.controllers.queries.item_query) filters
|
||||
# `item_code/item_name LIKE '%txt%'` -- a leading-wildcard LIKE no btree can serve. pg_trgm
|
||||
# GIN indexes accelerate it. Item is read-heavy/write-light master data, so GIN maintenance
|
||||
# cost is negligible. Postgres-only (`using` is a no-op on MariaDB, which has its own FULLTEXT).
|
||||
frappe.db.add_index("Item", ["item_code"], using="gin_trgm")
|
||||
frappe.db.add_index("Item", ["item_name"], using="gin_trgm")
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Item Attribute", {});
|
||||
frappe.ui.form.on("Item Attribute", {
|
||||
numeric_values(frm) {
|
||||
// Numeric attributes have no discrete values; drop the rows so their
|
||||
// mandatory Attribute Value / Abbreviation don't block the save.
|
||||
if (frm.doc.numeric_values) {
|
||||
frm.clear_table("item_attribute_values");
|
||||
frm.refresh_field("item_attribute_values");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -285,9 +285,6 @@ def create_stock_entry(pick_list: str | dict):
|
||||
pick_list = frappe.get_doc(frappe.parse_json(pick_list))
|
||||
validate_item_locations(pick_list)
|
||||
|
||||
if stock_entry_exists(pick_list.get("name")):
|
||||
return frappe.msgprint(_("Stock Entry has already been created against this Pick List"))
|
||||
|
||||
stock_entry = frappe.new_doc("Stock Entry")
|
||||
stock_entry.pick_list = pick_list.get("name")
|
||||
stock_entry.purpose = pick_list.get("purpose")
|
||||
@@ -301,6 +298,9 @@ def create_stock_entry(pick_list: str | dict):
|
||||
else:
|
||||
stock_entry = update_stock_entry_items_with_no_reference(pick_list, stock_entry)
|
||||
|
||||
if not stock_entry.get("items"):
|
||||
return frappe.msgprint(_("All picked items have already been transferred against this Pick List"))
|
||||
|
||||
stock_entry.set_missing_values()
|
||||
|
||||
return stock_entry.as_dict()
|
||||
@@ -366,6 +366,8 @@ def update_stock_entry_based_on_work_order(pick_list, stock_entry):
|
||||
stock_entry.project = work_order.project
|
||||
|
||||
for location in pick_list.locations:
|
||||
if get_pending_transfer_stock_qty(location) <= 0:
|
||||
continue
|
||||
item = frappe._dict()
|
||||
update_common_item_properties(item, location)
|
||||
item.t_warehouse = wip_warehouse
|
||||
@@ -377,6 +379,8 @@ def update_stock_entry_based_on_work_order(pick_list, stock_entry):
|
||||
|
||||
def update_stock_entry_based_on_material_request(pick_list, stock_entry):
|
||||
for location in pick_list.locations:
|
||||
if get_pending_transfer_stock_qty(location) <= 0:
|
||||
continue
|
||||
target_warehouse = None
|
||||
if location.material_request_item:
|
||||
target_warehouse = frappe.get_value(
|
||||
@@ -392,6 +396,8 @@ def update_stock_entry_based_on_material_request(pick_list, stock_entry):
|
||||
|
||||
def update_stock_entry_items_with_no_reference(pick_list, stock_entry):
|
||||
for location in pick_list.locations:
|
||||
if get_pending_transfer_stock_qty(location) <= 0:
|
||||
continue
|
||||
item = frappe._dict()
|
||||
update_common_item_properties(item, location)
|
||||
|
||||
@@ -400,11 +406,18 @@ def update_stock_entry_items_with_no_reference(pick_list, stock_entry):
|
||||
return stock_entry
|
||||
|
||||
|
||||
def get_pending_transfer_stock_qty(location):
|
||||
"""Stock qty of this pick list row still to be moved into a Stock Entry."""
|
||||
return flt(location.picked_qty) - flt(location.transferred_qty)
|
||||
|
||||
|
||||
def update_common_item_properties(item, location):
|
||||
pending_stock_qty = get_pending_transfer_stock_qty(location)
|
||||
item.item_code = location.item_code
|
||||
item.item_name = location.item_name
|
||||
item.s_warehouse = location.warehouse
|
||||
item.transfer_qty = location.picked_qty
|
||||
item.qty = flt(location.picked_qty / (location.conversion_factor or 1), location.precision("qty"))
|
||||
item.transfer_qty = pending_stock_qty
|
||||
item.qty = flt(pending_stock_qty / (location.conversion_factor or 1), location.precision("qty"))
|
||||
item.uom = location.uom
|
||||
item.conversion_factor = location.conversion_factor
|
||||
item.stock_uom = location.stock_uom
|
||||
@@ -412,3 +425,4 @@ def update_common_item_properties(item, location):
|
||||
item.serial_no = location.serial_no
|
||||
item.batch_no = location.batch_no
|
||||
item.material_request_item = location.material_request_item
|
||||
item.pick_list_item = location.name
|
||||
|
||||
@@ -190,7 +190,7 @@
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"options": "Draft\nOpen\nPartly Delivered\nCompleted\nCancelled",
|
||||
"options": "Draft\nOpen\nPartly Delivered\nPartially Transferred\nCompleted\nCancelled",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1,
|
||||
@@ -278,7 +278,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2026-02-06 18:14:18.361039",
|
||||
"modified": "2026-07-01 14:27:50.617011",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Pick List",
|
||||
|
||||
@@ -71,7 +71,9 @@ class PickList(TransactionBase):
|
||||
purpose: DF.Literal["Material Transfer for Manufacture", "Material Transfer", "Delivery"]
|
||||
scan_barcode: DF.Data | None
|
||||
scan_mode: DF.Check
|
||||
status: DF.Literal["Draft", "Open", "Partly Delivered", "Completed", "Cancelled"]
|
||||
status: DF.Literal[
|
||||
"Draft", "Open", "Partly Delivered", "Partially Transferred", "Completed", "Cancelled"
|
||||
]
|
||||
work_order: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
@@ -417,6 +419,34 @@ class PickList(TransactionBase):
|
||||
|
||||
return stock_entry_exists(self.name)
|
||||
|
||||
def get_transfer_status(self):
|
||||
"""Return the pick list's transfer progress based on how much of the picked qty has been
|
||||
moved into submitted Stock Entries (tracked on Pick List Item.transferred_qty).
|
||||
|
||||
Only applies to purposes that move stock via Stock Entry; the Delivery purpose is tracked
|
||||
via delivery_status instead. Returns "Completed", "Partially Transferred" or None."""
|
||||
if self.purpose == "Delivery":
|
||||
return None
|
||||
|
||||
total_picked = sum(flt(row.picked_qty) for row in self.locations)
|
||||
if not total_picked:
|
||||
return None
|
||||
|
||||
total_transferred = sum(flt(row.transferred_qty) for row in self.locations)
|
||||
if total_transferred <= 0:
|
||||
return None
|
||||
|
||||
if total_transferred >= total_picked:
|
||||
return "Completed"
|
||||
|
||||
return "Partially Transferred"
|
||||
|
||||
def is_fully_transferred(self):
|
||||
return self.get_transfer_status() == "Completed"
|
||||
|
||||
def is_partially_transferred(self):
|
||||
return self.get_transfer_status() == "Partially Transferred"
|
||||
|
||||
def update_reference_qty(self):
|
||||
packed_items = []
|
||||
so_items = []
|
||||
|
||||
@@ -7,6 +7,7 @@ frappe.listview_settings["Pick List"] = {
|
||||
Draft: "red",
|
||||
Open: "orange",
|
||||
"Partly Delivered": "orange",
|
||||
"Partially Transferred": "yellow",
|
||||
Completed: "green",
|
||||
Cancelled: "red",
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ from erpnext.stock.doctype.pick_list.mapper import (
|
||||
create_delivery,
|
||||
create_delivery_note,
|
||||
create_dn_for_pick_lists,
|
||||
create_stock_entry,
|
||||
)
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import (
|
||||
@@ -1221,6 +1222,64 @@ class TestPickList(ERPNextTestSuite):
|
||||
pl.reload()
|
||||
self.assertEqual(pl.status, "Cancelled")
|
||||
|
||||
def test_pick_list_partial_transfer_status(self):
|
||||
"""Partial Stock Entries from a Pick List should track transferred_qty and drive the
|
||||
Partially Transferred / Completed status, and allow further transfers for the remainder."""
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
|
||||
item = make_item(properties={"is_stock_item": 1}).name
|
||||
source_warehouse = "_Test Warehouse - _TC"
|
||||
target_warehouse = create_warehouse("_Test Transfer Target Warehouse")
|
||||
make_stock_entry(item=item, to_warehouse=source_warehouse, qty=10)
|
||||
|
||||
pick_list = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Pick List",
|
||||
"company": "_Test Company",
|
||||
"purpose": "Material Transfer",
|
||||
"pick_manually": 1,
|
||||
"locations": [
|
||||
{
|
||||
"item_code": item,
|
||||
"qty": 10,
|
||||
"stock_qty": 10,
|
||||
"conversion_factor": 1,
|
||||
"warehouse": source_warehouse,
|
||||
"picked_qty": 10,
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
pick_list.submit()
|
||||
self.assertEqual(pick_list.status, "Open")
|
||||
|
||||
# Transfer 4 of the 10 picked units.
|
||||
se1 = frappe.get_doc(create_stock_entry(pick_list.as_dict()))
|
||||
self.assertEqual(se1.items[0].qty, 10)
|
||||
se1.items[0].qty = 4
|
||||
se1.items[0].t_warehouse = target_warehouse
|
||||
se1.submit()
|
||||
|
||||
pick_list.reload()
|
||||
self.assertEqual(pick_list.locations[0].transferred_qty, 4)
|
||||
self.assertEqual(pick_list.status, "Partially Transferred")
|
||||
|
||||
# The next Stock Entry should only offer the remaining 6 units.
|
||||
se2 = frappe.get_doc(create_stock_entry(pick_list.as_dict()))
|
||||
self.assertEqual(se2.items[0].qty, 6)
|
||||
se2.items[0].t_warehouse = target_warehouse
|
||||
se2.submit()
|
||||
|
||||
pick_list.reload()
|
||||
self.assertEqual(pick_list.locations[0].transferred_qty, 10)
|
||||
self.assertEqual(pick_list.status, "Completed")
|
||||
|
||||
# Cancelling the last entry rolls transferred_qty and status back.
|
||||
se2.cancel()
|
||||
pick_list.reload()
|
||||
self.assertEqual(pick_list.locations[0].transferred_qty, 4)
|
||||
self.assertEqual(pick_list.status, "Partially Transferred")
|
||||
|
||||
def test_pick_list_validation(self):
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
item = make_item("Test Non Serialized Pick List Item", properties={"is_stock_item": 1}).name
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"conversion_factor",
|
||||
"stock_uom",
|
||||
"delivered_qty",
|
||||
"transferred_qty",
|
||||
"available_quantity_section",
|
||||
"actual_qty",
|
||||
"column_break_kyek",
|
||||
@@ -255,6 +256,16 @@
|
||||
"read_only": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "transferred_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Transferred Qty (in Stock UOM)",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "available_quantity_section",
|
||||
"fieldtype": "Section Break",
|
||||
@@ -285,7 +296,7 @@
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2026-03-17 16:25:10.358013",
|
||||
"modified": "2026-07-01 14:27:50.617011",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Pick List Item",
|
||||
|
||||
@@ -39,6 +39,7 @@ class PickListItem(Document):
|
||||
stock_qty: DF.Float
|
||||
stock_reserved_qty: DF.Float
|
||||
stock_uom: DF.Link | None
|
||||
transferred_qty: DF.Float
|
||||
uom: DF.Link | None
|
||||
use_serial_batch_fields: DF.Check
|
||||
warehouse: DF.Link | None
|
||||
|
||||
@@ -425,10 +425,10 @@ def repost(doc):
|
||||
if isinstance(message, dict):
|
||||
message = message.get("message")
|
||||
|
||||
status = "Failed"
|
||||
# If failed because of timeout, set status to In Progress
|
||||
if traceback and ("timeout" in traceback.lower() or "Deadlock found" in traceback):
|
||||
status = "In Progress"
|
||||
# Recoverable errors (deadlock, lock/query timeout, job timeout) re-queue as In Progress.
|
||||
# Classify by type: the old traceback string-match only knew MariaDB's "Deadlock found" and
|
||||
# missed Postgres deadlocks ("deadlock detected"), failing them permanently.
|
||||
status = "In Progress" if isinstance(e, RecoverableErrors) else "Failed"
|
||||
|
||||
if traceback:
|
||||
message += "<br><br>" + "<b>Traceback:</b> <br>" + traceback
|
||||
@@ -447,7 +447,8 @@ def repost(doc):
|
||||
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
|
||||
)
|
||||
|
||||
if outgoing_email_account and not isinstance(e, RecoverableErrors):
|
||||
# status == "Failed" already implies e is not recoverable, so no need to re-check here.
|
||||
if outgoing_email_account:
|
||||
notify_error_to_stock_managers(doc, message)
|
||||
doc.set_status("Failed")
|
||||
finally:
|
||||
|
||||
@@ -220,6 +220,39 @@ class TestRepostItemValuation(ERPNextTestSuite, StockTestMixin):
|
||||
sorted(frappe.parse_json(frappe.as_json(set([("a", "b"), ("c", "d")])))),
|
||||
)
|
||||
|
||||
def test_recoverable_error_requeues_instead_of_failing(self):
|
||||
# A recoverable DB error (e.g. Postgres deadlock -> QueryDeadlockError) must re-queue the
|
||||
# repost as "In Progress"; a non-recoverable error still fails. Regression: the old check
|
||||
# string-matched MariaDB's "Deadlock found" and missed Postgres deadlocks ("deadlock detected").
|
||||
from unittest.mock import patch
|
||||
|
||||
from frappe.exceptions import QueryDeadlockError
|
||||
|
||||
from erpnext.stock.doctype.repost_item_valuation import repost_item_valuation as riv
|
||||
|
||||
orig_max_writes = frappe.db.MAX_WRITES_PER_TRANSACTION
|
||||
self.addCleanup(setattr, frappe.db, "MAX_WRITES_PER_TRANSACTION", orig_max_writes)
|
||||
|
||||
def status_after(error):
|
||||
doc = frappe.new_doc("Repost Item Valuation")
|
||||
doc.name = "test-recoverable-riv"
|
||||
doc.set_status = doc.log_error = doc.db_set = MagicMock()
|
||||
captured = {}
|
||||
with (
|
||||
patch.object(frappe, "in_test", False),
|
||||
patch.object(frappe.db, "exists", return_value=True),
|
||||
patch.object(frappe.db, "commit"),
|
||||
patch.object(frappe.db, "rollback"),
|
||||
patch.object(frappe.db, "set_value", side_effect=lambda *a, **k: captured.update(a[2])),
|
||||
patch.object(riv, "repost_sl_entries", side_effect=error),
|
||||
patch.object(frappe, "get_cached_value", return_value=None),
|
||||
):
|
||||
riv.repost(doc)
|
||||
return captured.get("status")
|
||||
|
||||
self.assertEqual(status_after(QueryDeadlockError("deadlock detected")), "In Progress")
|
||||
self.assertEqual(status_after(ValueError("boom")), "Failed")
|
||||
|
||||
def test_gl_repost_progress(self):
|
||||
from erpnext.accounts import utils
|
||||
|
||||
|
||||
@@ -1814,6 +1814,27 @@ class SerialandBatchBundle(Document):
|
||||
self.set("entries", [])
|
||||
|
||||
|
||||
def on_doctype_update():
|
||||
if frappe.db.db_type == "postgres":
|
||||
# Bundle-direct lookups (get_ledgers_from_serial_batch_bundle, get_picked_*) always filter
|
||||
# `is_cancelled = 0` and scope by voucher_no or item_code+warehouse -- none of which the parent
|
||||
# bundle is otherwise indexed on (only voucher_type/voucher_detail_no are). Partial indexes keep
|
||||
# only the active bundles. Postgres-only (`where` is a no-op on MariaDB, and MariaDB's optimizer
|
||||
# ignores partial predicates anyway).
|
||||
frappe.db.add_index(
|
||||
"Serial and Batch Bundle",
|
||||
["voucher_no"],
|
||||
index_name="sabb_active_voucher",
|
||||
where="is_cancelled = 0",
|
||||
)
|
||||
frappe.db.add_index(
|
||||
"Serial and Batch Bundle",
|
||||
["item_code", "warehouse"],
|
||||
index_name="sabb_active_item_wh",
|
||||
where="is_cancelled = 0",
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def download_blank_csv_template(content: str | list):
|
||||
csv_data = []
|
||||
|
||||
@@ -164,6 +164,15 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._configure_purpose_class()
|
||||
self.status_updater = [
|
||||
{
|
||||
"source_dt": "Stock Entry Detail",
|
||||
"target_dt": "Pick List Item",
|
||||
"join_field": "pick_list_item",
|
||||
"target_field": "transferred_qty",
|
||||
"source_field": "transfer_qty",
|
||||
}
|
||||
]
|
||||
|
||||
if self.subcontracting_inward_order:
|
||||
self.subcontract_data = frappe._dict(
|
||||
@@ -349,6 +358,7 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
self.delink_asset_repair_sabb()
|
||||
self.validate_closed_subcontracting_order()
|
||||
self.update_subcontracting_order_status()
|
||||
self.update_pick_list_status()
|
||||
self.cancel_stock_reserve_for_wip_and_fg()
|
||||
|
||||
if self.work_order and self.purpose == "Material Consumption for Manufacture":
|
||||
@@ -1484,6 +1494,9 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
def update_pick_list_status(self):
|
||||
from erpnext.stock.doctype.pick_list.pick_list import update_pick_list_status
|
||||
|
||||
if self.pick_list:
|
||||
self.update_qty()
|
||||
|
||||
update_pick_list_status(self.pick_list)
|
||||
|
||||
def set_missing_values(self):
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"col_break6",
|
||||
"material_request",
|
||||
"material_request_item",
|
||||
"pick_list_item",
|
||||
"original_item",
|
||||
"reference_section",
|
||||
"against_stock_entry",
|
||||
@@ -424,6 +425,16 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "pick_list_item",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"label": "Pick List Item",
|
||||
"no_copy": 1,
|
||||
"options": "Pick List Item",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "original_item",
|
||||
"fieldtype": "Link",
|
||||
@@ -679,7 +690,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2026-06-30 12:18:34.132425",
|
||||
"modified": "2026-07-01 14:27:50.617011",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry Detail",
|
||||
|
||||
@@ -58,6 +58,7 @@ class StockEntryDetail(Document):
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
pick_list_item: DF.Link | None
|
||||
po_detail: DF.Data | None
|
||||
project: DF.Link | None
|
||||
putaway_rule: DF.Link | None
|
||||
|
||||
@@ -364,3 +364,15 @@ class StockLedgerEntry(Document):
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
|
||||
frappe.db.add_index("Stock Ledger Entry", ["item_code", "warehouse", "posting_datetime", "creation"])
|
||||
|
||||
if frappe.db.db_type == "postgres":
|
||||
# Postgres-only partial index for date-range stock reports (Stock Ledger / Stock Balance)
|
||||
# that scan across all items: they filter `is_cancelled = 0` and sort by posting_datetime.
|
||||
# The existing item_code-leading composite can't serve an all-items date scan. `where` is a
|
||||
# no-op on MariaDB, so this is added only on postgres.
|
||||
frappe.db.add_index(
|
||||
"Stock Ledger Entry",
|
||||
["company", "posting_datetime", "creation"],
|
||||
index_name="sle_active_posting",
|
||||
where="is_cancelled = 0",
|
||||
)
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.stock.report.incorrect_serial_and_batch_bundle.incorrect_serial_and_batch_bundle import (
|
||||
execute,
|
||||
)
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestIncorrectSerialAndBatchBundle(ERPNextTestSuite):
|
||||
def run_report(self, **extra):
|
||||
filters = frappe._dict({"company": "_Test Company"})
|
||||
filters.update(extra)
|
||||
return execute(filters)[1]
|
||||
|
||||
def test_healthy_bundles_not_flagged(self):
|
||||
batch_item = make_item(
|
||||
properties={
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "ISBB-.#####",
|
||||
}
|
||||
).name
|
||||
serial_item = "_Test Serialized Item With Series"
|
||||
|
||||
make_stock_entry(
|
||||
item_code=batch_item,
|
||||
qty=10,
|
||||
rate=100,
|
||||
to_warehouse="Stores - _TC",
|
||||
posting_date="2026-06-01",
|
||||
)
|
||||
make_stock_entry(
|
||||
item_code=serial_item,
|
||||
qty=3,
|
||||
rate=100,
|
||||
to_warehouse="Stores - _TC",
|
||||
posting_date="2026-06-01",
|
||||
)
|
||||
|
||||
data = self.run_report()
|
||||
|
||||
bundles = frappe.get_all(
|
||||
"Serial and Batch Bundle",
|
||||
filters={"item_code": ["in", [batch_item, serial_item]]},
|
||||
pluck="name",
|
||||
)
|
||||
|
||||
flagged_names = {row.get("name") for row in data}
|
||||
self.assertFalse(
|
||||
flagged_names.intersection(bundles),
|
||||
msg="Healthy serial/batch bundles should not be flagged as incorrect.",
|
||||
)
|
||||
|
||||
def test_unlinked_bundle_is_flagged(self):
|
||||
# an actual incorrect state: a submitted Serial and Batch Bundle left without any linking
|
||||
# Stock Ledger Entry (e.g. the SLE was purged but the bundle survived)
|
||||
batch_item = make_item(
|
||||
properties={
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "ISBB-ORPHAN-.#####",
|
||||
}
|
||||
).name
|
||||
|
||||
entry = make_stock_entry(
|
||||
item_code=batch_item, qty=5, rate=100, to_warehouse="Stores - _TC", posting_date="2026-06-01"
|
||||
)
|
||||
bundle = frappe.db.get_value("Serial and Batch Bundle", {"voucher_no": entry.name}, "name")
|
||||
self.assertTrue(bundle)
|
||||
|
||||
# orphan the bundle: drop the Stock Ledger Entry that referenced it
|
||||
frappe.db.delete("Stock Ledger Entry", {"serial_and_batch_bundle": bundle})
|
||||
|
||||
flagged = {row.get("name"): row for row in self.run_report()}
|
||||
self.assertIn(bundle, flagged)
|
||||
self.assertEqual(flagged[bundle]["is_cancelled"], 0)
|
||||
@@ -5,6 +5,7 @@ import copy
|
||||
import gzip
|
||||
import json
|
||||
from collections import deque
|
||||
from contextlib import nullcontext
|
||||
|
||||
import frappe
|
||||
from frappe import _, bold, scrub
|
||||
@@ -261,6 +262,28 @@ def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False):
|
||||
return sle
|
||||
|
||||
|
||||
# A repost waits this long for another repost's per-(item, warehouse) gate before giving up. Kept
|
||||
# well under the 1800s repost job timeout so a wait can't burn the whole budget, and short enough
|
||||
# that a contended worker re-queues (recoverable QueryTimeoutError) and frees the slot for other
|
||||
# items instead of pinning it.
|
||||
REPOST_LOCK_TIMEOUT = 300
|
||||
|
||||
|
||||
def repost_gate(item_code, warehouse):
|
||||
"""Serialize concurrent background reposts of the same (item, warehouse) with a session-level
|
||||
advisory lock taken before the inner `... for update` row locks, so they take turns instead of
|
||||
racing into a lock-order deadlock. Row locks still enforce correctness; this only cuts the
|
||||
deadlock/retry churn. Scope is repost-vs-repost only -- the synchronous repost_current_voucher
|
||||
submit path is deliberately not gated (blocking a submit behind a background repost would be a
|
||||
worse regression) and keeps relying on the existing deadlock retry. No advisory locks, no gate."""
|
||||
# hasattr keeps this a graceful opt-in: on an ERPNext predating frappe.db.advisory_lock, fall
|
||||
# back to no gate rather than raising and marking the Repost Item Valuation permanently Failed.
|
||||
if frappe.db.db_type in ("postgres", "mariadb") and hasattr(frappe.db, "advisory_lock"):
|
||||
# Tuple key: a colon in item_code/warehouse can't collide two distinct pairs onto one lock.
|
||||
return frappe.db.advisory_lock(("stock_repost", item_code, warehouse), timeout=REPOST_LOCK_TIMEOUT)
|
||||
return nullcontext()
|
||||
|
||||
|
||||
def repost_future_sle(
|
||||
items_to_be_repost=None,
|
||||
voucher_type=None,
|
||||
@@ -289,22 +312,25 @@ def repost_future_sle(
|
||||
while index < len(items_to_be_repost):
|
||||
validate_item_warehouse(items_to_be_repost[index])
|
||||
|
||||
obj = update_entries_after(
|
||||
{
|
||||
"item_code": items_to_be_repost[index].get("item_code"),
|
||||
"warehouse": items_to_be_repost[index].get("warehouse"),
|
||||
"posting_date": items_to_be_repost[index].get("posting_date"),
|
||||
"posting_time": items_to_be_repost[index].get("posting_time"),
|
||||
"creation": items_to_be_repost[index].get("creation"),
|
||||
"current_idx": index,
|
||||
"items_to_be_repost": items_to_be_repost,
|
||||
"repost_doc": doc,
|
||||
"repost_affected_transaction": repost_affected_transaction,
|
||||
"item_wh_wise_last_posted_sle": resume_item_wh_wise_last_posted_sle,
|
||||
},
|
||||
allow_negative_stock=allow_negative_stock,
|
||||
via_landed_cost_voucher=via_landed_cost_voucher,
|
||||
)
|
||||
item_code = items_to_be_repost[index].get("item_code")
|
||||
warehouse = items_to_be_repost[index].get("warehouse")
|
||||
with repost_gate(item_code, warehouse):
|
||||
obj = update_entries_after(
|
||||
{
|
||||
"item_code": item_code,
|
||||
"warehouse": warehouse,
|
||||
"posting_date": items_to_be_repost[index].get("posting_date"),
|
||||
"posting_time": items_to_be_repost[index].get("posting_time"),
|
||||
"creation": items_to_be_repost[index].get("creation"),
|
||||
"current_idx": index,
|
||||
"items_to_be_repost": items_to_be_repost,
|
||||
"repost_doc": doc,
|
||||
"repost_affected_transaction": repost_affected_transaction,
|
||||
"item_wh_wise_last_posted_sle": resume_item_wh_wise_last_posted_sle,
|
||||
},
|
||||
allow_negative_stock=allow_negative_stock,
|
||||
via_landed_cost_voucher=via_landed_cost_voucher,
|
||||
)
|
||||
|
||||
index += 1
|
||||
|
||||
|
||||
@@ -1,4 +1,78 @@
|
||||
{
|
||||
"allowed_users": [
|
||||
{
|
||||
"user": "Administrator"
|
||||
},
|
||||
{
|
||||
"user": "Guest"
|
||||
},
|
||||
{
|
||||
"user": "accounts@test.com"
|
||||
},
|
||||
{
|
||||
"user": "ankush@erpnext.com"
|
||||
},
|
||||
{
|
||||
"user": "faris@erpnext.com"
|
||||
},
|
||||
{
|
||||
"user": "mention_test_user@example.com"
|
||||
},
|
||||
{
|
||||
"user": "project@frappe.io"
|
||||
},
|
||||
{
|
||||
"user": "rushabh@erpnext.com"
|
||||
},
|
||||
{
|
||||
"user": "saqib@erpnext.com"
|
||||
},
|
||||
{
|
||||
"user": "soham@frappe.io"
|
||||
},
|
||||
{
|
||||
"user": "sohamengineer123@gmail.com"
|
||||
},
|
||||
{
|
||||
"user": "sohamkulkarns9@gmail.com"
|
||||
},
|
||||
{
|
||||
"user": "stock@xyz.com"
|
||||
},
|
||||
{
|
||||
"user": "sydel@frappe.io"
|
||||
},
|
||||
{
|
||||
"user": "test'5@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test1@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test2@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test3@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test4@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test@example.com"
|
||||
},
|
||||
{
|
||||
"user": "test@portal.com"
|
||||
},
|
||||
{
|
||||
"user": "testpassword@example.com"
|
||||
},
|
||||
{
|
||||
"user": "testperm@example.com"
|
||||
},
|
||||
{
|
||||
"user": "web@web.com"
|
||||
}
|
||||
],
|
||||
"app": "erpnext",
|
||||
"charts": [
|
||||
{
|
||||
@@ -789,9 +863,10 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-01-02 12:38:50.043198",
|
||||
"modified": "2026-06-17 12:11:34.739020",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"module_onboarding": "Stock Onboarding",
|
||||
"name": "Stock",
|
||||
"number_cards": [
|
||||
{
|
||||
@@ -815,6 +890,737 @@
|
||||
"roles": [],
|
||||
"sequence_id": 7.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "home",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Stock",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "chart",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dashboard",
|
||||
"link_to": "Stock",
|
||||
"link_type": "Dashboard",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "stock",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Entry",
|
||||
"link_to": "Stock Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "receipt-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Receipt",
|
||||
"link_to": "Purchase Receipt",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "truck",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Delivery Note",
|
||||
"link_to": "Delivery Note",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "arrow-left-to-line",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Material Request",
|
||||
"link_to": "Material Request",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "caravan",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Pick List",
|
||||
"link_to": "Pick List",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "tool",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Tools",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Reconciliation",
|
||||
"link_to": "Stock Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Landed Cost Voucher",
|
||||
"link_to": "Landed Cost Voucher",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Repost Item Valuation",
|
||||
"link_to": "Repost Item Valuation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Packing Slip",
|
||||
"link_to": "Packing Slip",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Inspection",
|
||||
"link_to": "Quality Inspection",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_to": "",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Group",
|
||||
"link_to": "Item Group",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Attribute",
|
||||
"link_to": "Item Attribute",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Brand",
|
||||
"link_to": "Brand",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Warehouse",
|
||||
"link_to": "Warehouse",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Unit of Measure (UOM)",
|
||||
"link_to": "UOM",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "UOM Conversion Factor",
|
||||
"link_to": "UOM Conversion Factor",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Serial No",
|
||||
"link_to": "Serial No",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Batch No",
|
||||
"link_to": "Batch",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Serial and Batch Bundle",
|
||||
"link_to": "Serial and Batch Bundle",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Inventory Dimension",
|
||||
"link_to": "Inventory Dimension",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Shipping Rule",
|
||||
"link_to": "Shipping Rule",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Alternative",
|
||||
"link_to": "Item Alternative",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quality Inspection Template",
|
||||
"link_to": "Quality Inspection Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Delivery Trip",
|
||||
"link_to": "Delivery Trip",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "sheet",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Ledger",
|
||||
"link_to": "Stock Ledger",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Balance",
|
||||
"link_to": "Stock Balance",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Quick Stock Balance",
|
||||
"link_to": "Quick Stock Balance",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Projected Qty",
|
||||
"link_to": "Stock Projected Qty",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Analytics",
|
||||
"link_to": "Stock Analytics",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Ageing",
|
||||
"link_to": "Stock Ageing",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Receipt Trends",
|
||||
"link_to": "Purchase Receipt Trends",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Delivery Note Trends",
|
||||
"link_to": "Delivery Note Trends",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Price Stock",
|
||||
"link_to": "Item Price Stock",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Warehouse Wise Stock Balance",
|
||||
"link_to": "Warehouse Wise Stock Balance",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Shortage Report",
|
||||
"link_to": "Item Shortage Report",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Serial No and Batch Traceability",
|
||||
"link_to": "Serial No and Batch Traceability",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Serial No Status",
|
||||
"link_to": "Serial No Status",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Serial No Ledger",
|
||||
"link_to": "Serial No Ledger",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Serial No Warranty Expiry",
|
||||
"link_to": "Serial No Warranty Expiry",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Batch-Wise Balance History",
|
||||
"link_to": "Batch-Wise Balance History",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Batch Item Expiry Status",
|
||||
"link_to": "Batch Item Expiry Status",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Requested Items To Be Transferred",
|
||||
"link_to": "Requested Items To Be Transferred",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Itemwise Recommended Reorder Level",
|
||||
"link_to": "Itemwise Recommended Reorder Level",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Variant Details",
|
||||
"link_to": "Item Variant Details",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "settings",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Settings",
|
||||
"link_to": "Stock Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Variant Settings",
|
||||
"link_to": "Item Variant Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Reposting Settings",
|
||||
"link_to": "Stock Reposting Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Delivery Settings",
|
||||
"link_to": "Delivery Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Stock",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "organization",
|
||||
"icon": "getting-started",
|
||||
"idx": 2,
|
||||
"is_hidden": 0,
|
||||
"label": "Subcontracting",
|
||||
@@ -138,9 +138,10 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2025-12-19 16:50:25.976741",
|
||||
"modified": "2026-06-14 13:43:50.289920",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"module_onboarding": "Subcontracting Onboarding",
|
||||
"name": "Subcontracting",
|
||||
"number_cards": [
|
||||
{
|
||||
@@ -164,6 +165,251 @@
|
||||
"roles": [],
|
||||
"sequence_id": 8.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "home",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Subcontracting",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "folder-tree",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Subcontracting BOM",
|
||||
"link_to": "Subcontracting BOM",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "move-horizontal",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Stock Entry",
|
||||
"link_to": "Stock Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "arrow-left-to-line",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Inward Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Sales Order",
|
||||
"link_to": "Sales Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Subcontracting Inward Order",
|
||||
"link_to": "Subcontracting Inward Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Subcontracting Delivery",
|
||||
"link_to": "Stock Entry",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "arrow-right-from-line",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Outward Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Purchase Order",
|
||||
"link_to": "Purchase Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Subcontracting Order",
|
||||
"link_to": "Subcontracting Order",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Subcontracting Receipt",
|
||||
"link_to": "Subcontracting Receipt",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bill of Materials",
|
||||
"link_to": "BOM",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "notepad-text",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Subcontract Order Summary",
|
||||
"link_to": "Subcontract Order Summary",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Materials To Be Transferred",
|
||||
"link_to": "Subcontracted Raw Materials To Be Transferred",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Items To Be Received",
|
||||
"link_to": "Subcontracted Item To Be Received",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "settings",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Settings",
|
||||
"link_to": "Buying Settings",
|
||||
"link_type": "DocType",
|
||||
"navigate_to_tab": "subcontract",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Subcontracting",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[{\"id\":\"HOEnlt9aR9\",\"type\":\"header\",\"data\":{\"text\":\"This module is scheduled for deprecation and will be completely removed in version 17, please use <a href=\\\"https://frappe.io/helpdesk\\\">Frappe Helpdesk</a> instead.\",\"col\":12}},{\"id\":\"oxhWhXp9b2\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"Ff8Ab3nLLN\",\"type\":\"card\",\"data\":{\"card_name\":\"Issues\",\"col\":4}},{\"id\":\"_lndiuJTVP\",\"type\":\"card\",\"data\":{\"card_name\":\"Maintenance\",\"col\":4}},{\"id\":\"R_aNO5ESzJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Service Level Agreement\",\"col\":4}},{\"id\":\"N8aA2afWfi\",\"type\":\"card\",\"data\":{\"card_name\":\"Warranty\",\"col\":4}},{\"id\":\"M5fxGuFwUR\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"xKH0kO9q4P\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
|
||||
"content": "[{\"id\":\"HOEnlt9aR9\",\"type\":\"header\",\"data\":{\"text\":\"This module is scheduled for deprecation and will be completely removed in version 17, please use <a href=\\\"https://frappe.io/helpdesk\\\">Frappe Helpdesk</a> instead.\",\"col\":12}},{\"id\":\"qzP2mZrGOu\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"id\":\"Fkdjo6bJ7A\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Issue\",\"col\":3}},{\"id\":\"OTS8kx2f3x\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Maintenance Visit\",\"col\":3}},{\"id\":\"smDTSjBR3Z\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Service Level Agreement\",\"col\":3}},{\"id\":\"WCqL_gBYGU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"oxhWhXp9b2\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"Ff8Ab3nLLN\",\"type\":\"card\",\"data\":{\"card_name\":\"Issues\",\"col\":4}},{\"id\":\"_lndiuJTVP\",\"type\":\"card\",\"data\":{\"card_name\":\"Maintenance\",\"col\":4}},{\"id\":\"R_aNO5ESzJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Service Level Agreement\",\"col\":4}},{\"id\":\"N8aA2afWfi\",\"type\":\"card\",\"data\":{\"card_name\":\"Warranty\",\"col\":4}},{\"id\":\"M5fxGuFwUR\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"xKH0kO9q4P\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
|
||||
"creation": "2020-03-02 15:48:23.224699",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
@@ -172,7 +172,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-01-02 17:45:04.203273",
|
||||
"modified": "2026-06-14 13:44:07.764547",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Support",
|
||||
"name": "Support",
|
||||
@@ -184,7 +184,179 @@
|
||||
"restrict_to_domain": "",
|
||||
"roles": [],
|
||||
"sequence_id": 12.0,
|
||||
"shortcuts": [],
|
||||
"shortcuts": [
|
||||
{
|
||||
"color": "Yellow",
|
||||
"format": "{} Assigned",
|
||||
"label": "Issue",
|
||||
"link_to": "Issue",
|
||||
"stats_filter": "{\n \"_assign\": [\"like\", '%' + frappe.session.user + '%'],\n \"status\": \"Open\"\n}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Maintenance Visit",
|
||||
"link_to": "Maintenance Visit",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Service Level Agreement",
|
||||
"link_to": "Service Level Agreement",
|
||||
"type": "DocType"
|
||||
}
|
||||
],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "home",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Support",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "file-question-mark",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Issue",
|
||||
"link_to": "Issue",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "calendar-days",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Maintenance Schedule",
|
||||
"link_to": "Maintenance Schedule",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "calendar-check-2",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Maintenance Visit",
|
||||
"link_to": "Maintenance Visit",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "grid-2x2-check",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Warranty Claim",
|
||||
"link_to": "Warranty Claim",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Issue Type",
|
||||
"link_to": "Issue Type",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Issue Priority",
|
||||
"link_to": "Issue Priority",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Service Level Agreement",
|
||||
"link_to": "Service Level Agreement",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "notepad-text",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "First Response Time for Issues",
|
||||
"link_to": "First Response Time for Issues",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "settings",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Settings",
|
||||
"link_to": "Support Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Support",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"keep_closed": 0,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
@@ -25,6 +26,7 @@
|
||||
"label": "Chart of Accounts",
|
||||
"link_to": "Account",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -36,6 +38,7 @@
|
||||
"label": "Chart of Cost Centers",
|
||||
"link_to": "Cost Center",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -47,6 +50,7 @@
|
||||
"label": "Account Category",
|
||||
"link_to": "Account Category",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -58,6 +62,7 @@
|
||||
"label": "Accounting Dimension",
|
||||
"link_to": "Accounting Dimension",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -69,6 +74,7 @@
|
||||
"label": "Currency",
|
||||
"link_to": "Currency",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -80,6 +86,7 @@
|
||||
"label": "Currency Exchange",
|
||||
"link_to": "Currency Exchange",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -91,6 +98,7 @@
|
||||
"label": "Finance Book",
|
||||
"link_to": "Finance Book",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -102,6 +110,7 @@
|
||||
"label": "Mode of Payment",
|
||||
"link_to": "Mode of Payment",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -113,6 +122,7 @@
|
||||
"label": "Payment Term",
|
||||
"link_to": "Payment Term",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -124,6 +134,7 @@
|
||||
"label": "Journal Entry Template",
|
||||
"link_to": "Journal Entry Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -135,6 +146,7 @@
|
||||
"label": "Terms and Conditions",
|
||||
"link_to": "Terms and Conditions",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -146,6 +158,7 @@
|
||||
"label": "Company",
|
||||
"link_to": "Company",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -157,6 +170,7 @@
|
||||
"label": "Fiscal Year",
|
||||
"link_to": "Fiscal Year",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -168,6 +182,7 @@
|
||||
"label": "Sales Taxes",
|
||||
"link_to": "Sales Taxes and Charges Template",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -179,6 +194,7 @@
|
||||
"keep_closed": 0,
|
||||
"label": "Opening & Closing",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
@@ -191,6 +207,7 @@
|
||||
"label": "COA Importer",
|
||||
"link_to": "Chart of Accounts Importer",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -203,6 +220,7 @@
|
||||
"label": "Opening Invoice Tool",
|
||||
"link_to": "Opening Invoice Creation Tool",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -215,6 +233,7 @@
|
||||
"label": "Accounting Period",
|
||||
"link_to": "Accounting Period",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -227,6 +246,7 @@
|
||||
"label": "FX Revaluation",
|
||||
"link_to": "Exchange Rate Revaluation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -239,6 +259,7 @@
|
||||
"label": "Period Closing Voucher",
|
||||
"link_to": "Period Closing Voucher",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -250,6 +271,7 @@
|
||||
"keep_closed": 0,
|
||||
"label": "Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
@@ -262,6 +284,7 @@
|
||||
"label": "Accounts Settings",
|
||||
"link_to": "Accounts Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -273,22 +296,12 @@
|
||||
"label": "Currency Exchange Settings",
|
||||
"link_to": "Currency Exchange Settings",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Repost Accounting Ledger Settings",
|
||||
"link_to": "Repost Accounting Ledger Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-02-23 22:20:51.043478",
|
||||
"modified": "2026-06-12 14:50:50.262533",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module_onboarding": "Accounting Onboarding",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"label": "Budget",
|
||||
"link_to": "Budget",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -27,6 +28,7 @@
|
||||
"label": "Cost Center",
|
||||
"link_to": "Cost Center",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -39,6 +41,7 @@
|
||||
"label": "Accounting Dimension",
|
||||
"link_to": "Accounting Dimension",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -51,6 +54,7 @@
|
||||
"label": "Cost Center Allocation",
|
||||
"link_to": "Cost Center Allocation",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -63,6 +67,7 @@
|
||||
"label": "Budget Variance",
|
||||
"link_to": "Budget Variance Report",
|
||||
"link_type": "Report",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
@@ -70,8 +75,8 @@
|
||||
"modified": "2026-01-10 00:06:13.032297",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Budget",
|
||||
"name": "Budgeting",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "Budget"
|
||||
"title": "Budgeting"
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
"label": "Global Defaults",
|
||||
"link_to": "Global Defaults",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -27,6 +28,7 @@
|
||||
"label": "System Settings",
|
||||
"link_to": "System Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -39,6 +41,7 @@
|
||||
"label": "Accounts Settings",
|
||||
"link_to": "Accounts Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -51,6 +54,7 @@
|
||||
"label": "POS Settings",
|
||||
"link_to": "POS Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -63,6 +67,7 @@
|
||||
"label": "Selling Settings",
|
||||
"link_to": "Selling Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -75,6 +80,7 @@
|
||||
"label": "Buying Settings",
|
||||
"link_to": "Buying Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -87,6 +93,7 @@
|
||||
"label": "Stock Settings",
|
||||
"link_to": "Stock Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -99,6 +106,7 @@
|
||||
"label": "Manufacturing Settings",
|
||||
"link_to": "Manufacturing Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -111,6 +119,7 @@
|
||||
"label": "Projects Settings",
|
||||
"link_to": "Projects Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -123,6 +132,7 @@
|
||||
"label": "CRM Settings",
|
||||
"link_to": "CRM Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -135,6 +145,7 @@
|
||||
"label": "Support Settings",
|
||||
"link_to": "Support Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -146,6 +157,7 @@
|
||||
"keep_closed": 1,
|
||||
"label": "Other Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
@@ -157,6 +169,7 @@
|
||||
"label": "Subscription Settings",
|
||||
"link_to": "Subscription Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -168,6 +181,7 @@
|
||||
"label": "Item Variant Settings",
|
||||
"link_to": "Item Variant Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -179,6 +193,7 @@
|
||||
"label": "Delivery Settings",
|
||||
"link_to": "Delivery Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -190,6 +205,7 @@
|
||||
"label": "Currency Exchange Settings",
|
||||
"link_to": "Currency Exchange Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -201,6 +217,7 @@
|
||||
"label": "Appointment Booking Settings",
|
||||
"link_to": "Appointment Booking Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -212,22 +229,12 @@
|
||||
"label": "Stock Reposting Settings",
|
||||
"link_to": "Stock Reposting Settings",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Repost Accounting Ledger Settings",
|
||||
"link_to": "Repost Accounting Ledger Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-01-10 00:06:12.956275",
|
||||
"modified": "2026-06-12 14:51:11.333051",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "ERPNext Settings",
|
||||
|
||||
@@ -9,89 +9,103 @@
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 1,
|
||||
"icon": "organization",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Company",
|
||||
"link_to": "Company",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "book-text",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Letter Head",
|
||||
"link_to": "Letter Head",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "file-user",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Department",
|
||||
"link_to": "Department",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "book-user",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Branch",
|
||||
"link_to": "Branch",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "users",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "User",
|
||||
"link_to": "User",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "user-round-check",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Role Permissions",
|
||||
"link_to": "permission-manager",
|
||||
"link_type": "Page",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "mail",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Email Account",
|
||||
"link_to": "Email Account",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-02-24 18:08:00.796746",
|
||||
"modified": "2026-06-16 00:37:22.942285",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"module_onboarding": "Organization Onboarding",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"label": "Subscription",
|
||||
"link_to": "Subscription",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -27,6 +28,7 @@
|
||||
"label": "Subscription Plan",
|
||||
"link_to": "Subscription Plan",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -39,6 +41,7 @@
|
||||
"label": "Subscription Settings",
|
||||
"link_to": "Subscription Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -50,6 +53,7 @@
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
@@ -61,6 +65,7 @@
|
||||
"label": "Customer",
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -72,6 +77,7 @@
|
||||
"label": "Supplier",
|
||||
"link_to": "Supplier",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -83,6 +89,7 @@
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
@@ -90,8 +97,8 @@
|
||||
"modified": "2026-01-10 00:06:13.048591",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Subscription",
|
||||
"name": "Subscriptions",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "Subscription"
|
||||
"title": "Subscriptions"
|
||||
}
|
||||
Reference in New Issue
Block a user