mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-19 09:28:46 +00:00
Merge branch 'develop' into strip-account-number-coa-importer
This commit is contained in:
@@ -88,7 +88,6 @@ pull_request_rules:
|
||||
actions:
|
||||
merge:
|
||||
method: squash
|
||||
commit_message_template: |
|
||||
{{ title }} (#{{ number }})
|
||||
|
||||
{{ body }}
|
||||
commit_message_format:
|
||||
title: pr-title
|
||||
body: pr-body
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
"account_number": "11530"
|
||||
},
|
||||
"account_number": "115",
|
||||
"is_group": 1
|
||||
"is_group": 1,
|
||||
"account_type": "Bank"
|
||||
},
|
||||
"Trade Receivables": {
|
||||
"Trade Debtors": {
|
||||
@@ -529,6 +530,13 @@
|
||||
"account_number": "630",
|
||||
"is_group": 1
|
||||
},
|
||||
"Accrued Manufacturing Expenses": {
|
||||
"Accrued Expenses - Manufacturing": {
|
||||
"account_number": "63510"
|
||||
},
|
||||
"account_number": "635",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "63",
|
||||
"is_group": 1
|
||||
},
|
||||
@@ -814,4 +822,4 @@
|
||||
"root_type": "Expense"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,10 +348,15 @@ class TestExchangeRateRevaluation(ERPNextTestSuite, AccountsTestMixin):
|
||||
je.reload()
|
||||
self.assertEqual(je.voucher_type, "Exchange Rate Revaluation")
|
||||
self.assertEqual(len(je.accounts), 3)
|
||||
# A gain is credited to the gain/loss account, a loss is debited. The current
|
||||
# exchange rate (from master data) may sit either side of the booked rate, so
|
||||
# derive the column from the sign instead of assuming a gain.
|
||||
gain_loss_debit = abs(total_gain_loss) if total_gain_loss < 0 else 0.0
|
||||
gain_loss_credit = total_gain_loss if total_gain_loss > 0 else 0.0
|
||||
expected = [
|
||||
(usd_account, new_balance, 0.0, 100.0, 0.0),
|
||||
(usd_account, 0.0, old_balance, 0.0, 100.0),
|
||||
(gain_loss_account, 0.0, total_gain_loss, 0.0, total_gain_loss),
|
||||
(gain_loss_account, gain_loss_debit, gain_loss_credit, gain_loss_debit, gain_loss_credit),
|
||||
]
|
||||
actual = []
|
||||
for acc in je.accounts:
|
||||
|
||||
@@ -472,7 +472,7 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
|
||||
pr = frappe.new_doc("Purchase Receipt")
|
||||
pr.currency = "USD"
|
||||
pr.company = "_Test Company with perpetual inventory"
|
||||
pr.conversion_rate = (70,)
|
||||
pr.conversion_rate = 80
|
||||
pr.supplier = "_Test Supplier USD"
|
||||
pr.append(
|
||||
"items",
|
||||
@@ -491,7 +491,7 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
|
||||
|
||||
# Createing purchase invoice against Purchase Receipt
|
||||
pi = create_purchase_invoice(pr.name)
|
||||
pi.conversion_rate = 80
|
||||
pi.conversion_rate = 70
|
||||
pi.credit_to = "_Test Payable USD - TCP1"
|
||||
pi.insert()
|
||||
pi.submit()
|
||||
|
||||
@@ -21,11 +21,21 @@ class TDSComputationSummaryReport(TaxWithholdingDetailsReport):
|
||||
AGGREGATE_FIELDS = ("total_amount", "tax_amount")
|
||||
|
||||
def validate_filters(self):
|
||||
if self.filters.from_date > self.filters.to_date:
|
||||
from_date = self.filters.from_date
|
||||
to_date = self.filters.to_date
|
||||
if not from_date or not to_date:
|
||||
frappe.throw(
|
||||
_("{0} and {1} are mandatory").format(
|
||||
frappe.bold(_("From Date")),
|
||||
frappe.bold(_("To Date")),
|
||||
)
|
||||
)
|
||||
|
||||
if from_date > to_date:
|
||||
frappe.throw(_("From Date must be before To Date"))
|
||||
|
||||
from_year = get_fiscal_year(self.filters.from_date)[0]
|
||||
to_year = get_fiscal_year(self.filters.to_date)[0]
|
||||
from_year = get_fiscal_year(from_date)[0]
|
||||
to_year = get_fiscal_year(to_date)[0]
|
||||
if from_year != to_year:
|
||||
frappe.throw(_("From Date and To Date lie in different Fiscal Year"))
|
||||
|
||||
|
||||
735
erpnext/accounts/workspace/accounting/accounting.json
Normal file
735
erpnext/accounts/workspace/accounting/accounting.json
Normal file
@@ -0,0 +1,735 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [
|
||||
{
|
||||
"chart_name": "Profit and Loss",
|
||||
"label": "Profit and Loss"
|
||||
},
|
||||
{
|
||||
"chart_name": "Accounts Receivable Ageing",
|
||||
"label": "Accounts Receivable Ageing"
|
||||
},
|
||||
{
|
||||
"chart_name": "Accounts Payable Ageing",
|
||||
"label": "Accounts Payable Ageing"
|
||||
},
|
||||
{
|
||||
"chart_name": "Bank Balance",
|
||||
"label": "Bank Balance"
|
||||
},
|
||||
{
|
||||
"chart_name": "Budget Variance",
|
||||
"label": "Budget Variance"
|
||||
}
|
||||
],
|
||||
"content": "[{\"id\":\"acc_ov_hdr1\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Accounting Overview</b></span>\",\"col\":12}},{\"id\":\"acc_ov_nc01\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Outgoing Bills\",\"col\":3}},{\"id\":\"acc_ov_nc02\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Incoming Bills\",\"col\":3}},{\"id\":\"acc_ov_nc03\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Incoming Payment\",\"col\":3}},{\"id\":\"acc_ov_nc04\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Outgoing Payment\",\"col\":3}},{\"id\":\"acc_ov_ch01\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"acc_ov_ch02\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Accounts Receivable Ageing\",\"col\":6}},{\"id\":\"acc_ov_ch03\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Accounts Payable Ageing\",\"col\":6}},{\"id\":\"acc_ov_ch04\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Bank Balance\",\"col\":6}},{\"id\":\"acc_ov_ch05\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Budget Variance\",\"col\":6}}]",
|
||||
"creation": "2026-07-14 12:00:00",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "landmark",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Accounting",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-07-14 14:28:55.763394",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module_onboarding": "Accounting Onboarding",
|
||||
"name": "Accounting",
|
||||
"number_cards": [
|
||||
{
|
||||
"label": "Outgoing Bills",
|
||||
"number_card_name": "Total Outgoing Bills"
|
||||
},
|
||||
{
|
||||
"label": "Incoming Bills",
|
||||
"number_card_name": "Total Incoming Bills"
|
||||
},
|
||||
{
|
||||
"label": "Incoming Payment",
|
||||
"number_card_name": "Total Incoming Payment"
|
||||
},
|
||||
{
|
||||
"label": "Outgoing Payment",
|
||||
"number_card_name": "Total Outgoing Payment"
|
||||
}
|
||||
],
|
||||
"owner": "Administrator",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 4.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "house",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Accounting",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "book-open-check",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Opening & Closing",
|
||||
"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": "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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"icon": "coins",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Taxes",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"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": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"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": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "package",
|
||||
"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": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"icon": "wallet",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Budgeting",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"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": 1,
|
||||
"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": "coins",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Share Management",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "user",
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "repeat",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Subscriptions",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"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": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"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": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"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": "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,
|
||||
"default_workspace": 0,
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"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,
|
||||
"default_workspace": 0,
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "wrench",
|
||||
"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": "Accounts Settings",
|
||||
"link_to": "Accounts 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": "Currency Exchange Settings",
|
||||
"link_to": "Currency Exchange Settings",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"standard": 1,
|
||||
"title": "Accounting",
|
||||
"type": "Workspace"
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
{
|
||||
"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-07-03 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": "wrench",
|
||||
"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"
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-14 14:38:20.315394",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "wallet",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Budgeting",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-07-03 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": "wallet",
|
||||
"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"
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
"label": "Payments",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-07-03 13:43:50.184761",
|
||||
"modified": "2026-07-14 12:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module_onboarding": "Accounting Onboarding",
|
||||
@@ -25,9 +25,23 @@
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 47.0,
|
||||
"sequence_id": 3.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 0,
|
||||
"icon": "house",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Payments",
|
||||
"link_type": "Workspace",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
@@ -161,6 +175,180 @@
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "circle-dollar-sign",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Banking",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"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": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "wrench",
|
||||
"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": 1,
|
||||
"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": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Banking 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": "receipt-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"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-11 11:51:22.831729",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "coins",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Share Management",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-07-03 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": "user",
|
||||
"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"
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-14 14:08:36.817393",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "wallet",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Subscriptions",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-07-03 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"
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [],
|
||||
"content": "[]",
|
||||
"creation": "2026-06-11 11:51:22.649582",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "coins",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Taxes",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-07-03 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": "package",
|
||||
"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"
|
||||
}
|
||||
@@ -259,6 +259,7 @@ class PurchaseOrder(BuyingController):
|
||||
"ref_dn_field": "material_request_item",
|
||||
"compare_fields": mri_compare_fields,
|
||||
"is_child_table": True,
|
||||
"allow_duplicate_prev_row_id": True,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -913,8 +913,10 @@
|
||||
"fieldname": "job_card",
|
||||
"fieldtype": "Link",
|
||||
"label": "Job Card",
|
||||
"no_copy": 1,
|
||||
"options": "Job Card",
|
||||
"search_index": 1
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "distributed_discount_amount",
|
||||
@@ -941,7 +943,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2026-06-08 21:00:00.000000",
|
||||
"modified": "2026-07-15 10:30:04.600510",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order Item",
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
frappe.ui.form.on("Supplier", {
|
||||
setup: function (frm) {
|
||||
frm.set_query("allowed_companies", () => ({
|
||||
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
||||
}));
|
||||
frm.set_query("default_price_list", { buying: 1 });
|
||||
if (frm.doc.__islocal == 1) {
|
||||
frm.set_value("represents_company", "");
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
"tax_withholding_category",
|
||||
"tax_withholding_group",
|
||||
"settings_tab",
|
||||
"company_restrictions_section",
|
||||
"allowed_companies",
|
||||
"invoice_settings_section",
|
||||
"is_transporter",
|
||||
"allow_purchase_invoice_creation_without_purchase_order",
|
||||
@@ -425,6 +427,20 @@
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "company_restrictions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Company Restrictions",
|
||||
"description": "If set, this Supplier is only available for transactions in the listed companies. Leave empty for no restriction.",
|
||||
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
|
||||
},
|
||||
{
|
||||
"fieldname": "allowed_companies",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Allowed Companies",
|
||||
"options": "Company Restriction",
|
||||
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
|
||||
},
|
||||
{
|
||||
"fieldname": "contact_and_address_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
@@ -562,7 +578,7 @@
|
||||
"link_fieldname": "party"
|
||||
}
|
||||
],
|
||||
"modified": "2026-06-27 16:12:33.190257",
|
||||
"modified": "2026-07-14 21:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier",
|
||||
|
||||
@@ -17,6 +17,7 @@ from erpnext.accounts.party import (
|
||||
validate_party_currency_before_merging,
|
||||
)
|
||||
from erpnext.controllers.website_list_for_contact import add_role_for_portal_user
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import validate_allowed_companies
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
|
||||
|
||||
@@ -36,12 +37,14 @@ class Supplier(TransactionBase):
|
||||
from erpnext.buying.doctype.customer_number_at_supplier.customer_number_at_supplier import (
|
||||
CustomerNumberAtSupplier,
|
||||
)
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import CompanyRestriction
|
||||
from erpnext.utilities.doctype.portal_user.portal_user import PortalUser
|
||||
|
||||
accounts: DF.Table[PartyAccount]
|
||||
alias: DF.Data | None
|
||||
allow_purchase_invoice_creation_without_purchase_order: DF.Check
|
||||
allow_purchase_invoice_creation_without_purchase_receipt: DF.Check
|
||||
allowed_companies: DF.TableMultiSelect[CompanyRestriction]
|
||||
companies: DF.Table[AllowedToTransactWith]
|
||||
country: DF.Link | None
|
||||
customer_numbers: DF.Table[CustomerNumberAtSupplier]
|
||||
@@ -146,6 +149,7 @@ class Supplier(TransactionBase):
|
||||
self.validate_internal_supplier()
|
||||
self.add_role_for_user()
|
||||
self.validate_currency_for_receivable_payable_and_advance_account()
|
||||
validate_allowed_companies(self)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_supplier_group_details(self):
|
||||
|
||||
@@ -307,6 +307,7 @@
|
||||
"fieldname": "net_rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Net Rate",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -613,7 +614,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-06-17 12:05:52.441645",
|
||||
"modified": "2026-07-15 10:33:24.855979",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation Item",
|
||||
|
||||
@@ -501,7 +501,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-07-03 13:43:50.509039",
|
||||
"modified": "2026-07-14 12:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"module_onboarding": "Buying Onboarding",
|
||||
@@ -754,6 +754,83 @@
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "rocket",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Subcontracting",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"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": "",
|
||||
"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": 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,
|
||||
@@ -910,6 +987,45 @@
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"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,
|
||||
|
||||
@@ -565,6 +565,7 @@ def show_accounting_ledger_preview(company: str, doctype: str, docname: str):
|
||||
|
||||
filters = frappe._dict(company=company, include_dimensions=1)
|
||||
doc = frappe.get_lazy_doc(doctype, docname)
|
||||
doc.check_permission("read")
|
||||
doc.run_method("before_gl_preview")
|
||||
|
||||
gl_columns, gl_data = get_accounting_ledger_preview(doc, filters)
|
||||
@@ -580,6 +581,7 @@ def show_stock_ledger_preview(company: str, doctype: str, docname: str):
|
||||
|
||||
filters = frappe._dict(company=company)
|
||||
doc = frappe.get_lazy_doc(doctype, docname)
|
||||
doc.check_permission("read")
|
||||
doc.run_method("before_sl_preview")
|
||||
|
||||
sl_columns, sl_data = get_stock_ledger_preview(doc, filters)
|
||||
|
||||
@@ -17,6 +17,7 @@ add_to_apps_screen = [
|
||||
"title": app_title,
|
||||
"route": app_home,
|
||||
"has_permission": "erpnext.check_app_permission",
|
||||
"sequence_id": 1,
|
||||
}
|
||||
]
|
||||
|
||||
@@ -307,6 +308,18 @@ sounds = [
|
||||
|
||||
has_upload_permission = {"Employee": "erpnext.setup.doctype.employee.employee.has_upload_permission"}
|
||||
|
||||
permission_query_conditions = {
|
||||
"Item": "erpnext.stock.doctype.company_restriction.company_restriction.get_permission_query_conditions",
|
||||
"Customer": "erpnext.stock.doctype.company_restriction.company_restriction.get_permission_query_conditions",
|
||||
"Supplier": "erpnext.stock.doctype.company_restriction.company_restriction.get_permission_query_conditions",
|
||||
}
|
||||
|
||||
has_permission = {
|
||||
"Item": "erpnext.stock.doctype.company_restriction.company_restriction.has_permission",
|
||||
"Customer": "erpnext.stock.doctype.company_restriction.company_restriction.has_permission",
|
||||
"Supplier": "erpnext.stock.doctype.company_restriction.company_restriction.has_permission",
|
||||
}
|
||||
|
||||
has_website_permission = {
|
||||
"Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
"Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
|
||||
@@ -38,9 +38,10 @@ class WorkOrderCreationService:
|
||||
self.doc = doc
|
||||
|
||||
def get_production_items(self):
|
||||
bom_warehouse_map = self.get_bom_source_warehouse_map(self.doc.po_items)
|
||||
item_dict = {}
|
||||
for d in self.doc.po_items:
|
||||
item_details = self._production_item_details(d)
|
||||
item_details = self._production_item_details(d, bom_warehouse_map)
|
||||
if self.doc.get_items_from == "Material Request":
|
||||
item_details["qty"] = d.planned_qty
|
||||
key = (d.item_code, d.material_request_item, d.warehouse, d.planned_start_date)
|
||||
@@ -52,7 +53,20 @@ class WorkOrderCreationService:
|
||||
item_dict[key] = item_details
|
||||
return item_dict
|
||||
|
||||
def _production_item_details(self, d):
|
||||
def get_bom_source_warehouse_map(self, rows):
|
||||
bom_names = {row.bom_no for row in rows if row.bom_no}
|
||||
if not bom_names:
|
||||
return {}
|
||||
return dict(
|
||||
frappe.get_all(
|
||||
"BOM",
|
||||
filters={"name": ["in", list(bom_names)]},
|
||||
fields=["name", "default_source_warehouse"],
|
||||
as_list=True,
|
||||
)
|
||||
)
|
||||
|
||||
def _production_item_details(self, d, bom_warehouse_map):
|
||||
details = {
|
||||
"production_item": d.item_code,
|
||||
"use_multi_level_bom": d.include_exploded_items,
|
||||
@@ -70,7 +84,7 @@ class WorkOrderCreationService:
|
||||
"product_bundle_item": d.product_bundle_item,
|
||||
"planned_start_date": d.planned_start_date,
|
||||
"project": self.doc.project,
|
||||
"source_warehouse": frappe.get_value("BOM", d.bom_no, "default_source_warehouse"),
|
||||
"source_warehouse": bom_warehouse_map.get(d.bom_no),
|
||||
}
|
||||
if not details["project"] and d.sales_order:
|
||||
details["project"] = frappe.get_cached_value("Sales Order", d.sales_order, "project")
|
||||
@@ -112,6 +126,7 @@ class WorkOrderCreationService:
|
||||
wo_list.append(work_order)
|
||||
|
||||
def make_work_order_for_subassembly_items(self, wo_list, subcontracted_po, default_warehouses):
|
||||
bom_warehouse_map = self.get_bom_source_warehouse_map(self.doc.sub_assembly_items)
|
||||
for row in self.doc.sub_assembly_items:
|
||||
if row.type_of_manufacturing == "Subcontract":
|
||||
subcontracted_po.setdefault(row.supplier, []).append(row)
|
||||
@@ -119,16 +134,16 @@ class WorkOrderCreationService:
|
||||
if row.type_of_manufacturing == "Material Request":
|
||||
continue
|
||||
|
||||
work_order = self._sub_assembly_work_order(row, default_warehouses)
|
||||
work_order = self._sub_assembly_work_order(row, default_warehouses, bom_warehouse_map)
|
||||
if work_order:
|
||||
wo_list.append(work_order)
|
||||
|
||||
def _sub_assembly_work_order(self, row, default_warehouses):
|
||||
def _sub_assembly_work_order(self, row, default_warehouses, bom_warehouse_map):
|
||||
if flt(row.qty) <= flt(row.ordered_qty):
|
||||
return None
|
||||
|
||||
work_order_data = {
|
||||
"source_warehouse": frappe.get_value("BOM", row.bom_no, "default_source_warehouse"),
|
||||
"source_warehouse": bom_warehouse_map.get(row.bom_no),
|
||||
"wip_warehouse": default_warehouses.get("wip_warehouse"),
|
||||
"fg_warehouse": default_warehouses.get("fg_warehouse"),
|
||||
"scrap_warehouse": default_warehouses.get("scrap_warehouse"),
|
||||
|
||||
@@ -261,6 +261,7 @@ erpnext.patches.v14_0.update_proprietorship_to_individual
|
||||
erpnext.patches.v15_0.rename_subcontracting_fields
|
||||
erpnext.patches.v15_0.unset_incorrect_additional_discount_percentage
|
||||
erpnext.patches.v16_0.convert_commission_rate_to_percent
|
||||
erpnext.patches.v16_0.convert_hide_currency_symbol_to_check
|
||||
|
||||
[post_model_sync]
|
||||
erpnext.patches.v15_0.rename_gross_purchase_amount_to_net_purchase_amount
|
||||
@@ -496,4 +497,5 @@ erpnext.patches.v16_0.backfill_pick_list_transferred_qty
|
||||
erpnext.patches.v16_0.create_shop_floor_roles
|
||||
erpnext.patches.v15_0.backfill_sla_link_filters_on_custom_field
|
||||
erpnext.patches.v15_0.backfill_sla_link_filters_on_docfield
|
||||
erpnext.patches.v16_0.crm_settings_handle_allowed_users_for_frappe_crm
|
||||
erpnext.patches.v16_0.crm_settings_handle_allowed_users_for_frappe_crm
|
||||
erpnext.patches.v16_0.access_control_for_project_users
|
||||
|
||||
34
erpnext/patches/v16_0/access_control_for_project_users.py
Normal file
34
erpnext/patches/v16_0/access_control_for_project_users.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
Project = frappe.qb.DocType("Project")
|
||||
ProjectUser = frappe.qb.DocType("Project User")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(Project)
|
||||
.join(ProjectUser)
|
||||
.on(Project.name == ProjectUser.parent)
|
||||
.select(Project.name, ProjectUser.user)
|
||||
.where(Project.status != "Cancelled") # Not considering cancelled Projects.
|
||||
)
|
||||
|
||||
proj_users = query.run(as_dict=1)
|
||||
|
||||
project_mapped_users = get_project_mapped_users(proj_users)
|
||||
|
||||
for d in proj_users:
|
||||
if d.user in project_mapped_users[d.name]:
|
||||
continue
|
||||
|
||||
frappe.share.add_docshare("Project", d.name, user=d.user)
|
||||
|
||||
|
||||
def get_project_mapped_users(proj_users):
|
||||
projects = set([d.name for d in proj_users])
|
||||
project_mapped_users = {}
|
||||
|
||||
for d in projects:
|
||||
project_mapped_users[d] = [d.user for d in frappe.share.get_users("Project", d)]
|
||||
|
||||
return project_mapped_users
|
||||
@@ -0,0 +1,9 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
# runs pre_model_sync: field is still a Select, so this returns the raw "Yes"/"No"
|
||||
old_value = frappe.db.get_single_value("Global Defaults", "hide_currency_symbol")
|
||||
new_value = 1 if old_value == "Yes" else 0
|
||||
frappe.db.set_single_value("Global Defaults", "hide_currency_symbol", new_value)
|
||||
frappe.db.set_default("hide_currency_symbol", new_value)
|
||||
@@ -86,7 +86,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Open\nCompleted\nCancelled",
|
||||
"options": "Open\nOn hold\nCompleted\nCancelled",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
@@ -210,13 +210,15 @@
|
||||
"fieldname": "users",
|
||||
"fieldtype": "Table",
|
||||
"label": "Users",
|
||||
"options": "Project User"
|
||||
"options": "Project User",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "copied_from",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Copied From",
|
||||
"permlevel": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@@ -482,13 +484,25 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"max_attachments": 4,
|
||||
"modified": "2026-05-22 16:45:50.762759",
|
||||
"modified": "2026-07-14 14:32:11.328347",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Project",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"permlevel": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Projects Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
|
||||
@@ -59,7 +59,7 @@ class Project(Document):
|
||||
project_type: DF.Link | None
|
||||
sales_order: DF.Link | None
|
||||
second_email: DF.Time | None
|
||||
status: DF.Literal["Open", "Completed", "Cancelled"]
|
||||
status: DF.Literal["Open", "On hold", "Completed", "Cancelled"]
|
||||
subject: DF.Data | None
|
||||
to_time: DF.Time | None
|
||||
total_billable_amount: DF.Currency
|
||||
@@ -90,6 +90,7 @@ class Project(Document):
|
||||
def validate(self):
|
||||
if not self.is_new():
|
||||
self.copy_from_template()
|
||||
self.control_access_for_project_users()
|
||||
self.send_welcome_email()
|
||||
self.update_costing()
|
||||
self.update_percent_complete()
|
||||
@@ -239,6 +240,7 @@ class Project(Document):
|
||||
def after_insert(self):
|
||||
self.copy_from_template("after_insert")
|
||||
self.link_with_sales_order()
|
||||
self.control_access_for_project_users()
|
||||
|
||||
def link_with_sales_order(self) -> None:
|
||||
"""Back-link the source Sales Order to this project.
|
||||
@@ -311,8 +313,8 @@ class Project(Document):
|
||||
pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum)
|
||||
self.percent_complete = flt(flt(pct_complete), 2)
|
||||
|
||||
# don't update status if it is cancelled
|
||||
if self.status == "Cancelled":
|
||||
# don't update status if it is manually set to cancelled or on hold
|
||||
if self.status in ("Cancelled", "On hold"):
|
||||
return
|
||||
|
||||
self.status = "Completed" if self.percent_complete == 100 else "Open"
|
||||
@@ -434,6 +436,34 @@ class Project(Document):
|
||||
)
|
||||
user.welcome_email_sent = 1
|
||||
|
||||
def control_access_for_project_users(self):
|
||||
def revoke_access_for_project_users(removed_users):
|
||||
users = set([d.user for d in frappe.share.get_users(self.doctype, self.name)])
|
||||
for user in removed_users:
|
||||
if user not in users:
|
||||
continue
|
||||
|
||||
frappe.share.remove(self.doctype, self.name, user)
|
||||
|
||||
def grant_access_for_project_users(new_users):
|
||||
for user in new_users:
|
||||
frappe.share.add_docshare(self.doctype, self.name, user=user)
|
||||
|
||||
current_users = set([d.user for d in self.users])
|
||||
old_doc = self.get_doc_before_save()
|
||||
|
||||
if not old_doc:
|
||||
grant_access_for_project_users(current_users)
|
||||
return
|
||||
|
||||
previous_users = set([d.user for d in old_doc.users])
|
||||
|
||||
new_users = current_users - previous_users
|
||||
removed_users = previous_users - current_users
|
||||
|
||||
revoke_access_for_project_users(removed_users)
|
||||
grant_access_for_project_users(new_users)
|
||||
|
||||
|
||||
def get_timeline_data(doctype: str, name: str) -> dict[int, int]:
|
||||
"""Return timeline for attendance"""
|
||||
|
||||
@@ -4,6 +4,8 @@ frappe.listview_settings["Project"] = {
|
||||
get_indicator: function (doc) {
|
||||
if (doc.status == "Open" && doc.percent_complete) {
|
||||
return [__("{0}%", [cint(doc.percent_complete)]), "orange", "percent_complete,>,0|status,=,Open"];
|
||||
} else if (doc.status == "On hold") {
|
||||
return [__("On hold"), "blue", "status,=,On hold"];
|
||||
} else {
|
||||
return [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status];
|
||||
}
|
||||
|
||||
@@ -436,6 +436,61 @@ class TestProject(ERPNextTestSuite):
|
||||
self.assertEqual(project.total_consumed_material_cost, sum(row.amount for row in issue.items))
|
||||
self.assertGreater(project.total_consumed_material_cost, 0)
|
||||
|
||||
def _create_portal_user(self, email):
|
||||
"""A user with no Project-related role, so read access can only come from
|
||||
control_access_for_project_users() sharing the doc with them."""
|
||||
if not frappe.db.exists("User", email):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "User",
|
||||
"email": email,
|
||||
"first_name": "Portal",
|
||||
"send_welcome_email": 0,
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
return email
|
||||
|
||||
def test_new_project_grants_access_to_its_users(self):
|
||||
member = self._create_portal_user(f"new_proj_member_{frappe.generate_hash(length=6)}@example.com")
|
||||
|
||||
project = frappe.get_doc(
|
||||
doctype="Project",
|
||||
project_name=f"_Test New Project Access {frappe.generate_hash(length=6)}",
|
||||
status="Open",
|
||||
company="_Test Company",
|
||||
)
|
||||
project.append("users", {"user": member, "welcome_email_sent": 1})
|
||||
project.insert() # must not raise
|
||||
|
||||
self.assertTrue(project.has_permission(user=member))
|
||||
shared_with = [d.user for d in frappe.share.get_users("Project", project.name)]
|
||||
self.assertIn(member, shared_with)
|
||||
|
||||
def test_adding_and_removing_project_user_updates_access(self):
|
||||
stays = self._create_portal_user(f"stays_{frappe.generate_hash(length=6)}@example.com")
|
||||
leaves = self._create_portal_user(f"leaves_{frappe.generate_hash(length=6)}@example.com")
|
||||
|
||||
project = frappe.get_doc(
|
||||
doctype="Project",
|
||||
project_name=f"_Test Project User Membership {frappe.generate_hash(length=6)}",
|
||||
status="Open",
|
||||
company="_Test Company",
|
||||
)
|
||||
project.append("users", {"user": stays, "welcome_email_sent": 1})
|
||||
project.insert()
|
||||
self.assertTrue(project.has_permission(user=stays))
|
||||
|
||||
# adding a user on update (not insert) must also grant them access
|
||||
project.append("users", {"user": leaves, "welcome_email_sent": 1})
|
||||
project.save()
|
||||
self.assertTrue(project.has_permission(user=leaves))
|
||||
|
||||
# removing a user must revoke the share that was granted for membership
|
||||
project.users = [d for d in project.users if d.user != leaves]
|
||||
project.save()
|
||||
self.assertFalse(project.has_permission(user=leaves))
|
||||
self.assertTrue(project.has_permission(user=stays))
|
||||
|
||||
|
||||
def get_project(name, template):
|
||||
project = frappe.get_doc(
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
frappe.ui.form.on("Customer", {
|
||||
setup: function (frm) {
|
||||
frm.set_query("allowed_companies", () => ({
|
||||
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
||||
}));
|
||||
frm.custom_make_buttons = {
|
||||
Opportunity: "Opportunity",
|
||||
Quotation: "Quotation",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-06-11 14:26:44",
|
||||
"creation": "2026-07-14 12:46:50.256889",
|
||||
"description": "Buyer of Goods and Services.",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
@@ -65,6 +65,9 @@
|
||||
"tax_withholding_group",
|
||||
"tax_withholding_category",
|
||||
"settings_tab",
|
||||
"company_restrictions_section",
|
||||
"allowed_companies",
|
||||
"section_break_ario",
|
||||
"so_required",
|
||||
"dn_required",
|
||||
"column_break_53",
|
||||
@@ -512,6 +515,20 @@
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Settings"
|
||||
},
|
||||
{
|
||||
"description": "If set, this Customer is only available for transactions in the listed companies. Leave empty for no restriction.",
|
||||
"fieldname": "company_restrictions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Company Restrictions",
|
||||
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
|
||||
},
|
||||
{
|
||||
"fieldname": "allowed_companies",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Allowed Companies",
|
||||
"options": "Company Restriction",
|
||||
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "default_sales_partner",
|
||||
@@ -683,6 +700,10 @@
|
||||
"label": "Alias",
|
||||
"no_copy": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_ario",
|
||||
"fieldtype": "Section Break"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-user",
|
||||
@@ -696,7 +717,7 @@
|
||||
"link_fieldname": "party"
|
||||
}
|
||||
],
|
||||
"modified": "2026-06-27 16:12:10.457900",
|
||||
"modified": "2026-07-14 21:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Customer",
|
||||
|
||||
@@ -25,6 +25,7 @@ from erpnext.accounts.party import (
|
||||
validate_party_currency_before_merging,
|
||||
)
|
||||
from erpnext.controllers.website_list_for_contact import add_role_for_portal_user
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import validate_allowed_companies
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
|
||||
from .mapper import (
|
||||
@@ -51,11 +52,13 @@ class Customer(TransactionBase):
|
||||
from erpnext.selling.doctype.supplier_number_at_customer.supplier_number_at_customer import (
|
||||
SupplierNumberAtCustomer,
|
||||
)
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import CompanyRestriction
|
||||
from erpnext.utilities.doctype.portal_user.portal_user import PortalUser
|
||||
|
||||
account_manager: DF.Link | None
|
||||
accounts: DF.Table[PartyAccount]
|
||||
alias: DF.Data | None
|
||||
allowed_companies: DF.TableMultiSelect[CompanyRestriction]
|
||||
companies: DF.Table[AllowedToTransactWith]
|
||||
credit_limits: DF.Table[CustomerCreditLimit]
|
||||
customer_details: DF.Text | None
|
||||
@@ -186,6 +189,7 @@ class Customer(TransactionBase):
|
||||
self.validate_internal_customer()
|
||||
self.add_role_for_user()
|
||||
self.validate_currency_for_receivable_payable_and_advance_account()
|
||||
validate_allowed_companies(self)
|
||||
|
||||
# set loyalty program tier
|
||||
if not self.is_new() and (customer := self.get_doc_before_save()):
|
||||
|
||||
@@ -17,6 +17,7 @@ from erpnext.selling.doctype.customer.mapper import (
|
||||
make_quotation,
|
||||
parse_full_name,
|
||||
)
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
@@ -29,20 +30,9 @@ class TestCustomer(ERPNextTestSuite):
|
||||
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)
|
||||
# Master data seeds a current-dated exchange rate, so make_quotation should
|
||||
# resolve that rate instead of falling back to the default conversion rate of 1.0.
|
||||
expected_rate = get_exchange_rate(foreign_currency, company_currency, nowdate())
|
||||
|
||||
customer = frappe.get_doc(
|
||||
{
|
||||
@@ -59,7 +49,7 @@ class TestCustomer(ERPNextTestSuite):
|
||||
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)
|
||||
self.assertEqual(flt(quotation.conversion_rate), flt(expected_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
|
||||
|
||||
@@ -5,16 +5,20 @@
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"defaults_section",
|
||||
"default_company",
|
||||
"country",
|
||||
"default_distance_unit",
|
||||
"column_break_8",
|
||||
"default_currency",
|
||||
"default_distance_unit",
|
||||
"demo_company",
|
||||
"general_settings_section",
|
||||
"hide_currency_symbol",
|
||||
"disable_rounded_total",
|
||||
"disable_in_words",
|
||||
"column_break_hnew",
|
||||
"use_posting_datetime_for_naming_documents",
|
||||
"demo_company"
|
||||
"enable_company_wise_masters"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -27,7 +31,7 @@
|
||||
{
|
||||
"fieldname": "country",
|
||||
"fieldtype": "Link",
|
||||
"label": "Country",
|
||||
"label": "Default Country",
|
||||
"options": "Country"
|
||||
},
|
||||
{
|
||||
@@ -51,12 +55,12 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Do not show any symbol like $ etc next to currencies.",
|
||||
"fieldname": "hide_currency_symbol",
|
||||
"fieldtype": "Select",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Hide Currency Symbol",
|
||||
"options": "\nNo\nYes"
|
||||
"label": "Hide Currency Symbol"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@@ -88,6 +92,27 @@
|
||||
"fieldname": "use_posting_datetime_for_naming_documents",
|
||||
"fieldtype": "Check",
|
||||
"label": "Use Posting Datetime for Naming Documents"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "When enabled, Supplier, Customer, and Item records can be restricted to specific companies via their <b>Allowed Companies</b> table. Transactions will only show masters configured for the selected company.",
|
||||
"fieldname": "enable_company_wise_masters",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Company-wise Master Filtering"
|
||||
},
|
||||
{
|
||||
"fieldname": "defaults_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Defaults"
|
||||
},
|
||||
{
|
||||
"fieldname": "general_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "General Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_hnew",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
@@ -96,7 +121,7 @@
|
||||
"in_create": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2026-07-14 13:37:46.177444",
|
||||
"modified": "2026-07-14 18:30:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Global Defaults",
|
||||
|
||||
@@ -18,6 +18,7 @@ keydict = {
|
||||
"account_url": "account_url",
|
||||
"disable_rounded_total": "disable_rounded_total",
|
||||
"disable_in_words": "disable_in_words",
|
||||
"enable_company_wise_masters": "enable_company_wise_masters",
|
||||
}
|
||||
|
||||
ROUNDED_TOTAL_DOCTYPES = (
|
||||
@@ -51,7 +52,8 @@ class GlobalDefaults(Document):
|
||||
demo_company: DF.Link | None
|
||||
disable_in_words: DF.Check
|
||||
disable_rounded_total: DF.Check
|
||||
hide_currency_symbol: DF.Literal["", "No", "Yes"]
|
||||
enable_company_wise_masters: DF.Check
|
||||
hide_currency_symbol: DF.Check
|
||||
use_posting_datetime_for_naming_documents: DF.Check
|
||||
# end: auto-generated types
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-07-03 13:43:50.429297",
|
||||
"modified": "2026-07-14 12:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "ERPNext Settings",
|
||||
@@ -355,6 +355,116 @@
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "building-2",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Organization",
|
||||
"link_type": "DocType",
|
||||
"open_in_new_tab": 0,
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 1,
|
||||
"icon": "building-2",
|
||||
"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,
|
||||
"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": 1,
|
||||
"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": 1,
|
||||
"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": 1,
|
||||
"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": 1,
|
||||
"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": 1,
|
||||
"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,
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
{
|
||||
"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": "building-2",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Organization",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2026-07-03 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": "building-2",
|
||||
"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"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_bulk_edit": 1,
|
||||
"allow_rename": 1,
|
||||
"creation": "2026-07-13 21:39:49.805859",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"company"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2026-07-14 00:15:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Company Restriction",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"row_format": "Dynamic",
|
||||
"rows_threshold_for_grid_search": 20,
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
116
erpnext/stock/doctype/company_restriction/company_restriction.py
Normal file
116
erpnext/stock/doctype/company_restriction/company_restriction.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from pypika.terms import Bracket, ExistsCriterion
|
||||
|
||||
|
||||
class CompanyRestriction(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
company: DF.Link
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
# end: auto-generated types
|
||||
|
||||
|
||||
def get_allowed_companies(user, doctype):
|
||||
from frappe.permissions import get_allowed_docs_for_doctype, get_user_permissions
|
||||
|
||||
if not frappe.get_single_value("Global Defaults", "enable_company_wise_masters"):
|
||||
return None
|
||||
|
||||
user_permissions = get_user_permissions(user or frappe.session.user)
|
||||
if "Company" not in user_permissions:
|
||||
return None
|
||||
return get_allowed_docs_for_doctype(user_permissions["Company"], doctype) or None
|
||||
|
||||
|
||||
def get_permission_query_conditions(user, doctype=None):
|
||||
if not doctype:
|
||||
return None
|
||||
|
||||
allowed_companies = get_allowed_companies(user, doctype)
|
||||
if not allowed_companies:
|
||||
return None
|
||||
|
||||
parent = frappe.qb.DocType(doctype)
|
||||
restriction = frappe.qb.DocType("Company Restriction")
|
||||
restriction_rows = (
|
||||
frappe.qb.from_(restriction)
|
||||
.select(restriction.name)
|
||||
.where(
|
||||
(restriction.parenttype == doctype)
|
||||
& (restriction.parentfield == "allowed_companies")
|
||||
& (restriction.parent == parent.name)
|
||||
)
|
||||
)
|
||||
allowed_rows = restriction_rows.where(restriction.company.isin(allowed_companies))
|
||||
return Bracket(ExistsCriterion(allowed_rows) | ExistsCriterion(restriction_rows).negate())
|
||||
|
||||
|
||||
def has_permission(doc, ptype=None, user=None):
|
||||
allowed_companies = get_allowed_companies(user, doc.doctype)
|
||||
if not allowed_companies:
|
||||
return True
|
||||
|
||||
companies = [row.company for row in doc.get("allowed_companies") or []]
|
||||
if not companies:
|
||||
return True
|
||||
return any(company in allowed_companies for company in companies)
|
||||
|
||||
|
||||
def validate_allowed_companies(doc):
|
||||
if doc.flags.ignore_permissions:
|
||||
return
|
||||
|
||||
allowed_companies = get_allowed_companies(frappe.session.user, doc.doctype)
|
||||
if not allowed_companies:
|
||||
return
|
||||
|
||||
previous_companies = set()
|
||||
if previous_doc := doc.get_doc_before_save():
|
||||
previous_companies = {row.company for row in previous_doc.get("allowed_companies") or []}
|
||||
|
||||
current_companies = {row.company for row in doc.get("allowed_companies") or []}
|
||||
for company in current_companies.symmetric_difference(previous_companies):
|
||||
if company not in allowed_companies:
|
||||
frappe.throw(
|
||||
_("You are not permitted to add or remove Company {0} in Allowed Companies").format(company),
|
||||
frappe.PermissionError,
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def company_query(
|
||||
doctype: str,
|
||||
txt: str,
|
||||
searchfield: str,
|
||||
start: int,
|
||||
page_len: int,
|
||||
filters: dict | str | None = None,
|
||||
):
|
||||
filters = frappe.parse_json(filters) if filters else {}
|
||||
if isinstance(filters, list):
|
||||
filters.append(["Company", "name", "like", f"%{txt}%"])
|
||||
else:
|
||||
filters["name"] = ("like", f"%{txt}%")
|
||||
|
||||
return frappe.get_list(
|
||||
"Company",
|
||||
filters=filters,
|
||||
limit_start=start,
|
||||
limit_page_length=page_len,
|
||||
order_by="name",
|
||||
as_list=True,
|
||||
)
|
||||
@@ -59,6 +59,9 @@ frappe.ui.form.on("Item", {
|
||||
},
|
||||
|
||||
setup: function (frm) {
|
||||
frm.set_query("allowed_companies", () => ({
|
||||
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
||||
}));
|
||||
frm.add_fetch("attribute", "numeric_values", "numeric_values");
|
||||
frm.add_fetch("attribute", "from_range", "from_range");
|
||||
frm.add_fetch("attribute", "to_range", "to_range");
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:item_code",
|
||||
"creation": "2026-02-02 14:41:23.105228",
|
||||
"creation": "2026-07-13 23:00:47.512490",
|
||||
"description": "A Product or a Service that is bought, sold or kept in stock.",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
@@ -40,6 +40,8 @@
|
||||
"over_delivery_receipt_allowance",
|
||||
"column_break_wugd",
|
||||
"over_billing_allowance",
|
||||
"company_restrictions_section",
|
||||
"allowed_companies",
|
||||
"section_break_11",
|
||||
"brand",
|
||||
"description",
|
||||
@@ -240,7 +242,6 @@
|
||||
"description": "ERPNext will make a stock ledger entry for each transaction of this item. Keep unchecked for non-stock or service items.",
|
||||
"fieldname": "is_stock_item",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 0,
|
||||
"label": "Maintain Stock",
|
||||
"oldfieldname": "is_stock_item",
|
||||
"oldfieldtype": "Select",
|
||||
@@ -281,9 +282,9 @@
|
||||
"description": "Enable if this item is a company asset like machinery or furniture.",
|
||||
"fieldname": "is_fixed_asset",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Is Fixed Asset",
|
||||
"read_only_depends_on": "eval:doc.is_stock_item",
|
||||
"in_list_view": 1
|
||||
"read_only_depends_on": "eval:doc.is_stock_item"
|
||||
},
|
||||
{
|
||||
"allow_in_quick_entry": 1,
|
||||
@@ -596,7 +597,7 @@
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
"description": "Minimum stock level to maintain as a buffer. Used to calculate recommended reorder level: Reorder Level = Safety Stock + (Average Daily Consumption × Lead Time).",
|
||||
"description": "Minimum stock level to maintain as a buffer. Used to calculate recommended reorder level: Reorder Level = Safety Stock + (Average Daily Consumption \u00d7 Lead Time).",
|
||||
"fieldname": "safety_stock",
|
||||
"fieldtype": "Float",
|
||||
"label": "Safety Stock",
|
||||
@@ -699,9 +700,9 @@
|
||||
"description": "Allow this item to be used in sales transactions.",
|
||||
"fieldname": "is_sales_item",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Allow Sales",
|
||||
"show_description_on_click": 1,
|
||||
"in_list_view": 1
|
||||
"show_description_on_click": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break3",
|
||||
@@ -1084,6 +1085,20 @@
|
||||
"fieldname": "item_prices_column",
|
||||
"fieldtype": "Column Break",
|
||||
"label": "Item Prices"
|
||||
},
|
||||
{
|
||||
"fieldname": "company_restrictions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Company Restrictions",
|
||||
"description": "If set, this Item is only available for transactions in the listed companies. Leave empty for no restriction.",
|
||||
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
|
||||
},
|
||||
{
|
||||
"fieldname": "allowed_companies",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Allowed Companies",
|
||||
"options": "Company Restriction",
|
||||
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-tag",
|
||||
@@ -1091,7 +1106,7 @@
|
||||
"image_field": "image",
|
||||
"links": [],
|
||||
"make_attachments_public": 1,
|
||||
"modified": "2026-07-05 23:24:45.734144",
|
||||
"modified": "2026-07-14 21:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item",
|
||||
|
||||
@@ -30,6 +30,7 @@ from erpnext.controllers.item_variant import (
|
||||
make_variant_item_code,
|
||||
validate_item_variant_attributes,
|
||||
)
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import validate_allowed_companies
|
||||
from erpnext.stock.doctype.item_default.item_default import ItemDefault
|
||||
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
||||
from erpnext.stock.utils import get_valuation_method
|
||||
@@ -60,6 +61,7 @@ class Item(Document):
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import CompanyRestriction
|
||||
from erpnext.stock.doctype.item_barcode.item_barcode import ItemBarcode
|
||||
from erpnext.stock.doctype.item_customer_detail.item_customer_detail import ItemCustomerDetail
|
||||
from erpnext.stock.doctype.item_default.item_default import ItemDefault
|
||||
@@ -71,6 +73,7 @@ class Item(Document):
|
||||
|
||||
allow_alternative_item: DF.Check
|
||||
allow_negative_stock: DF.Check
|
||||
allowed_companies: DF.TableMultiSelect[CompanyRestriction]
|
||||
asset_category: DF.Link | None
|
||||
asset_naming_series: DF.Literal[None]
|
||||
attributes: DF.Table[ItemVariantAttribute]
|
||||
@@ -242,6 +245,7 @@ class Item(Document):
|
||||
self.validate_serialized_change_with_bundle()
|
||||
self.validate_standard_cost_change()
|
||||
self.validate_item_tax_net_rate_range()
|
||||
validate_allowed_companies(self)
|
||||
|
||||
if not self.is_new():
|
||||
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
|
||||
|
||||
@@ -10,7 +10,7 @@ from frappe.exceptions import QueryDeadlockError, QueryTimeoutError
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder import DocType, Interval
|
||||
from frappe.query_builder.functions import CombineDatetime, Max, Now
|
||||
from frappe.utils import cint, get_link_to_form, get_weekday, getdate, now, nowtime
|
||||
from frappe.utils import cint, get_datetime, get_link_to_form, get_weekday, getdate, now, nowtime
|
||||
from frappe.utils.user import get_users_with_role
|
||||
from rq.timeouts import JobTimeoutException
|
||||
|
||||
@@ -19,6 +19,7 @@ from erpnext.accounts.services.gl_validator import validate_accounting_period
|
||||
from erpnext.accounts.utils import get_future_stock_vouchers, repost_gle_for_stock_vouchers
|
||||
from erpnext.stock.stock_ledger import (
|
||||
get_affected_transactions,
|
||||
get_item_wh_first_reposted_from_reposting_data,
|
||||
get_items_to_be_repost,
|
||||
repost_future_sle,
|
||||
)
|
||||
@@ -343,6 +344,21 @@ class RepostItemValuation(Document):
|
||||
)
|
||||
).run()
|
||||
|
||||
def skip_reposts_covered_by_dependents(self):
|
||||
if self.repost_only_accounting_ledgers:
|
||||
return
|
||||
|
||||
coverage = get_item_wh_first_reposted_from_reposting_data(self)
|
||||
if not coverage:
|
||||
return
|
||||
|
||||
source_datetime = get_combine_datetime(self.posting_date, self.posting_time)
|
||||
mark_covered_item_reposts(self.name, coverage, source_datetime)
|
||||
|
||||
affected = get_affected_transactions(self)
|
||||
if affected:
|
||||
mark_covered_transaction_reposts(self, coverage, affected)
|
||||
|
||||
def _recalculate_valuation_rate(self):
|
||||
doc = frappe.get_doc(self.voucher_type, self.voucher_no)
|
||||
if doc.get("is_internal_supplier"):
|
||||
@@ -376,6 +392,130 @@ def bulk_restart_reposting(names: str | list):
|
||||
frappe.msgprint(_("Repost Item Valuation restarted for selected failed records."))
|
||||
|
||||
|
||||
def repost_coverage_cache_key(name):
|
||||
return f"riv_dependent_coverage::{name}"
|
||||
|
||||
|
||||
def get_queued_item_reposts(source_name, item_codes):
|
||||
return frappe.get_all(
|
||||
"Repost Item Valuation",
|
||||
filters={
|
||||
"name": ("!=", source_name),
|
||||
"based_on": "Item and Warehouse",
|
||||
"status": "Queued",
|
||||
"docstatus": 1,
|
||||
"recalculate_valuation_rate": 0,
|
||||
"recreate_stock_ledgers": 0,
|
||||
"via_landed_cost_voucher": 0,
|
||||
"item_code": ("in", item_codes),
|
||||
},
|
||||
fields=["name", "item_code", "warehouse", "posting_date", "posting_time"],
|
||||
)
|
||||
|
||||
|
||||
def mark_covered_item_reposts(source_name, coverage, source_datetime):
|
||||
item_codes = {item_code for item_code, _ in coverage}
|
||||
|
||||
for row in get_queued_item_reposts(source_name, list(item_codes)):
|
||||
from_datetime = coverage.get((row.item_code, row.warehouse))
|
||||
if not from_datetime:
|
||||
continue
|
||||
|
||||
row_datetime = get_combine_datetime(row.posting_date, row.posting_time)
|
||||
if get_datetime(row_datetime) < get_datetime(source_datetime):
|
||||
continue
|
||||
|
||||
if get_datetime(from_datetime) <= get_datetime(row_datetime):
|
||||
frappe.db.set_value("Repost Item Valuation", row.name, "status", "Skipped")
|
||||
|
||||
|
||||
def get_queued_transaction_reposts(source_name, voucher_nos):
|
||||
return frappe.get_all(
|
||||
"Repost Item Valuation",
|
||||
filters={
|
||||
"name": ("!=", source_name),
|
||||
"based_on": "Transaction",
|
||||
"status": "Queued",
|
||||
"docstatus": 1,
|
||||
"repost_only_accounting_ledgers": 0,
|
||||
"recalculate_valuation_rate": 0,
|
||||
"recreate_stock_ledgers": 0,
|
||||
"via_landed_cost_voucher": 0,
|
||||
"voucher_no": ("in", list(voucher_nos)),
|
||||
},
|
||||
fields=["name", "voucher_type", "voucher_no", "posting_date", "posting_time"],
|
||||
)
|
||||
|
||||
|
||||
def accumulate_repost_coverage(row_name, coverage, row_datetime):
|
||||
cache_key = repost_coverage_cache_key(row_name)
|
||||
acc = frappe.cache().get_value(cache_key) or {}
|
||||
|
||||
for key, from_datetime in coverage.items():
|
||||
if get_datetime(from_datetime) > get_datetime(row_datetime):
|
||||
continue
|
||||
|
||||
existing = acc.get(key)
|
||||
if not existing or get_datetime(from_datetime) < get_datetime(existing):
|
||||
acc[key] = from_datetime
|
||||
|
||||
frappe.cache().set_value(cache_key, acc, expires_in_sec=86400)
|
||||
return acc
|
||||
|
||||
|
||||
def get_repost_items_by_voucher(rows):
|
||||
voucher_nos = {row.voucher_no for row in rows}
|
||||
if not voucher_nos:
|
||||
return {}
|
||||
|
||||
items_by_voucher = {}
|
||||
for sle in frappe.get_all(
|
||||
"Stock Ledger Entry",
|
||||
filters={"voucher_no": ("in", list(voucher_nos))},
|
||||
fields=["voucher_type", "voucher_no", "item_code", "warehouse"],
|
||||
distinct=True,
|
||||
):
|
||||
items_by_voucher.setdefault((sle.voucher_type, sle.voucher_no), set()).add(
|
||||
(sle.item_code, sle.warehouse)
|
||||
)
|
||||
|
||||
return items_by_voucher
|
||||
|
||||
|
||||
def is_transaction_repost_covered(items, acc, row_datetime):
|
||||
if not items:
|
||||
return False
|
||||
|
||||
for key in items:
|
||||
covered = acc.get(key)
|
||||
if not covered or get_datetime(covered) > get_datetime(row_datetime):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def mark_covered_transaction_reposts(source, coverage, affected):
|
||||
source_datetime = get_combine_datetime(source.posting_date, source.posting_time)
|
||||
voucher_nos = {voucher_no for _, voucher_no in affected}
|
||||
|
||||
rows = get_queued_transaction_reposts(source.name, voucher_nos)
|
||||
items_by_voucher = get_repost_items_by_voucher(rows)
|
||||
|
||||
for row in rows:
|
||||
if (row.voucher_type, row.voucher_no) not in affected:
|
||||
continue
|
||||
|
||||
row_datetime = get_combine_datetime(row.posting_date, row.posting_time)
|
||||
if get_datetime(row_datetime) < get_datetime(source_datetime):
|
||||
continue
|
||||
|
||||
acc = accumulate_repost_coverage(row.name, coverage, row_datetime)
|
||||
items = items_by_voucher.get((row.voucher_type, row.voucher_no))
|
||||
if is_transaction_repost_covered(items, acc, row_datetime):
|
||||
frappe.db.set_value("Repost Item Valuation", row.name, "status", "Skipped")
|
||||
frappe.cache().delete_value(repost_coverage_cache_key(row.name))
|
||||
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("Repost Item Valuation", ["warehouse", "item_code"], "item_warehouse")
|
||||
|
||||
@@ -407,6 +547,8 @@ def repost(doc):
|
||||
|
||||
repost_gl_entries(doc)
|
||||
|
||||
doc.skip_reposts_covered_by_dependents()
|
||||
|
||||
doc.set_status("Completed")
|
||||
doc.db_set("reposting_data_file", None)
|
||||
remove_attached_file(doc.name)
|
||||
|
||||
@@ -14,10 +14,11 @@ from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import (
|
||||
in_configured_timeslot,
|
||||
mark_covered_transaction_reposts,
|
||||
)
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.stock.tests.test_utils import StockTestMixin
|
||||
from erpnext.stock.utils import PendingRepostingError
|
||||
from erpnext.stock.utils import PendingRepostingError, get_combine_datetime
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
@@ -171,6 +172,127 @@ class TestRepostItemValuation(ERPNextTestSuite, StockTestMixin):
|
||||
riv4.set_status("Skipped")
|
||||
riv3.set_status("Skipped")
|
||||
|
||||
def _make_queued_transaction_riv(self, voucher):
|
||||
riv = frappe.get_doc(
|
||||
doctype="Repost Item Valuation",
|
||||
based_on="Transaction",
|
||||
voucher_type=voucher.doctype,
|
||||
voucher_no=voucher.name,
|
||||
posting_date=voucher.posting_date,
|
||||
posting_time="00:00:00",
|
||||
)
|
||||
riv.flags.dont_run_in_test = True
|
||||
riv.submit()
|
||||
return riv
|
||||
|
||||
def test_skip_transaction_repost_covered_by_dependent(self):
|
||||
company = "_Test Company with perpetual inventory"
|
||||
warehouse = "Stores - TCP1"
|
||||
|
||||
covered_pr = make_purchase_receipt(
|
||||
company=company, warehouse=warehouse, item_code="_Test Item", qty=5
|
||||
)
|
||||
other_pr = make_purchase_receipt(
|
||||
company=company, warehouse=warehouse, item_code="_Test Item 2", qty=5
|
||||
)
|
||||
|
||||
covered_riv = self._make_queued_transaction_riv(covered_pr)
|
||||
other_riv = self._make_queued_transaction_riv(other_pr)
|
||||
|
||||
earlier_date = add_days(covered_pr.posting_date, -1)
|
||||
source = frappe._dict(name="__test_source_riv__", posting_date=earlier_date, posting_time="00:00:00")
|
||||
coverage = {("_Test Item", warehouse): get_combine_datetime(earlier_date, "00:00:00")}
|
||||
affected = {("Purchase Receipt", covered_pr.name), ("Purchase Receipt", other_pr.name)}
|
||||
|
||||
mark_covered_transaction_reposts(source, coverage, affected)
|
||||
|
||||
covered_riv.reload()
|
||||
other_riv.reload()
|
||||
self.assertEqual(covered_riv.status, "Skipped")
|
||||
self.assertEqual(other_riv.status, "Queued")
|
||||
|
||||
other_riv.db_set("status", "Skipped")
|
||||
|
||||
def _make_dependent_repack(self, company, consumed_items, source_wh, fg_item, fg_wh, qty, posting_date):
|
||||
se = frappe.new_doc("Stock Entry")
|
||||
se.stock_entry_type = "Repack"
|
||||
se.company = company
|
||||
se.set_posting_time = 1
|
||||
se.posting_date = posting_date
|
||||
for item_code in consumed_items:
|
||||
se.append("items", {"item_code": item_code, "s_warehouse": source_wh, "qty": qty})
|
||||
se.append("items", {"item_code": fg_item, "t_warehouse": fg_wh, "qty": qty, "is_finished_item": 1})
|
||||
se.insert()
|
||||
se.submit()
|
||||
return se
|
||||
|
||||
def test_backdated_manufacture_repost_skips_redundant_dependent(self):
|
||||
from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import (
|
||||
execute_reposting_entry,
|
||||
)
|
||||
|
||||
frappe.flags.dont_execute_stock_reposts = True
|
||||
self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts", None)
|
||||
|
||||
original_setting = frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting")
|
||||
frappe.db.set_single_value("Stock Reposting Settings", "item_based_reposting", 1)
|
||||
self.addCleanup(
|
||||
frappe.db.set_single_value, "Stock Reposting Settings", "item_based_reposting", original_setting
|
||||
)
|
||||
|
||||
company = "_Test Company with perpetual inventory"
|
||||
source_wh = "Stores - TCP1"
|
||||
fg_wh = "Finished Goods - TCP1"
|
||||
|
||||
item_a = make_item(properties={"valuation_method": "FIFO"}).name
|
||||
item_b = make_item(properties={"valuation_method": "FIFO"}).name
|
||||
item_c = make_item(properties={"valuation_method": "FIFO"}).name
|
||||
|
||||
def _day(days):
|
||||
return add_days(nowdate(), days)
|
||||
|
||||
make_stock_entry(
|
||||
item_code=item_a, to_warehouse=source_wh, qty=10, rate=100, posting_date=_day(2), company=company
|
||||
)
|
||||
make_stock_entry(
|
||||
item_code=item_b, to_warehouse=source_wh, qty=10, rate=100, posting_date=_day(3), company=company
|
||||
)
|
||||
self._make_dependent_repack(company, [item_a, item_b], source_wh, item_c, fg_wh, 5, _day(10))
|
||||
|
||||
make_stock_entry(
|
||||
item_code=item_a, to_warehouse=source_wh, qty=10, rate=200, posting_date=_day(1), company=company
|
||||
)
|
||||
make_stock_entry(
|
||||
item_code=item_b, to_warehouse=source_wh, qty=10, rate=200, posting_date=_day(1), company=company
|
||||
)
|
||||
self._make_dependent_repack(company, [item_a, item_b], source_wh, item_c, fg_wh, 5, _day(5))
|
||||
|
||||
rivs = frappe.get_all(
|
||||
"Repost Item Valuation",
|
||||
filters={
|
||||
"docstatus": 1,
|
||||
"based_on": "Item and Warehouse",
|
||||
"status": "Queued",
|
||||
"item_code": ("in", [item_a, item_b, item_c]),
|
||||
},
|
||||
fields=["name", "item_code", "warehouse"],
|
||||
order_by="posting_date asc, posting_time asc, creation asc",
|
||||
)
|
||||
self.assertTrue(
|
||||
any(r.item_code == item_c and r.warehouse == fg_wh for r in rivs),
|
||||
msg="Expected a queued repost for the finished good",
|
||||
)
|
||||
|
||||
for r in rivs:
|
||||
execute_reposting_entry(r.name)
|
||||
|
||||
fg_repost_status = frappe.db.get_value(
|
||||
"Repost Item Valuation",
|
||||
{"based_on": "Item and Warehouse", "item_code": item_c, "warehouse": fg_wh, "docstatus": 1},
|
||||
"status",
|
||||
)
|
||||
self.assertEqual(fg_repost_status, "Skipped")
|
||||
|
||||
def test_stock_freeze_validation(self):
|
||||
today = nowdate()
|
||||
|
||||
|
||||
@@ -538,6 +538,65 @@ class TestStockReservationEntry(ERPNextTestSuite):
|
||||
self.assertEqual(row.delivered_qty, 0, "DN cancel must restore the serial/batch reservation")
|
||||
self.assertEqual(row.status, "Reserved")
|
||||
|
||||
@ERPNextTestSuite.change_settings(
|
||||
"Stock Settings",
|
||||
{
|
||||
"allow_negative_stock": 0,
|
||||
"enable_stock_reservation": 1,
|
||||
"auto_reserve_serial_and_batch": 1,
|
||||
"pick_serial_and_batch_based_on": "FIFO",
|
||||
"use_serial_batch_fields": 1,
|
||||
},
|
||||
)
|
||||
def test_batch_shared_across_sales_orders_can_be_delivered(self) -> None:
|
||||
# Regression (#57159): one batch reserved by two Sales Orders. Delivering each order's own
|
||||
# reserved unit must not raise Reserved Batch Conflict — the remainder covers the other order.
|
||||
item_doc = make_batch_item()
|
||||
create_material_receipt(items={item_doc.name: item_doc}, warehouse=self.warehouse, qty=2)
|
||||
|
||||
orders = []
|
||||
for _i in range(2):
|
||||
so = make_sales_order(item_code=item_doc.name, warehouse=self.warehouse, qty=1, rate=100)
|
||||
so.create_stock_reservation_entries()
|
||||
orders.append(so)
|
||||
|
||||
self.assertEqual(
|
||||
len(get_reserved_batch_nos(orders[0].name) | get_reserved_batch_nos(orders[1].name)), 1
|
||||
)
|
||||
|
||||
for so in orders:
|
||||
dn = make_delivery_note(so.name, kwargs={"for_reserved_stock": True})
|
||||
dn.save()
|
||||
dn.submit()
|
||||
self.assertEqual(dn.docstatus, 1)
|
||||
|
||||
@ERPNextTestSuite.change_settings(
|
||||
"Stock Settings",
|
||||
{
|
||||
"allow_negative_stock": 0,
|
||||
"enable_stock_reservation": 1,
|
||||
"auto_reserve_serial_and_batch": 1,
|
||||
"pick_serial_and_batch_based_on": "FIFO",
|
||||
"use_serial_batch_fields": 1,
|
||||
},
|
||||
)
|
||||
def test_delivery_draining_a_batch_reserved_for_another_sales_order_is_blocked(self) -> None:
|
||||
# Guard for #57159 fix: an order without a reservation must still be blocked from draining
|
||||
# a batch below what another order has reserved from it, even if other batches have stock.
|
||||
item_doc = make_batch_item()
|
||||
create_material_receipt(items={item_doc.name: item_doc}, warehouse=self.warehouse, qty=2)
|
||||
create_material_receipt(items={item_doc.name: item_doc}, warehouse=self.warehouse, qty=2)
|
||||
|
||||
so_a = make_sales_order(item_code=item_doc.name, warehouse=self.warehouse, qty=2, rate=100)
|
||||
so_a.create_stock_reservation_entries()
|
||||
(reserved_batch_no,) = get_reserved_batch_nos(so_a.name)
|
||||
|
||||
so_b = make_sales_order(item_code=item_doc.name, warehouse=self.warehouse, qty=2, rate=100)
|
||||
dn = make_delivery_note(so_b.name)
|
||||
dn.items[0].batch_no = reserved_batch_no
|
||||
dn.save()
|
||||
self.assertRaisesRegex(frappe.ValidationError, "is reserved for", dn.submit)
|
||||
|
||||
@ERPNextTestSuite.change_settings(
|
||||
"Stock Settings",
|
||||
{
|
||||
@@ -893,6 +952,33 @@ def create_items() -> dict:
|
||||
return items
|
||||
|
||||
|
||||
def make_batch_item():
|
||||
return make_item(
|
||||
properties={
|
||||
"is_stock_item": 1,
|
||||
"valuation_rate": 100,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "SRBI-.#####.",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def get_reserved_batch_nos(sales_order: str) -> set:
|
||||
sre = frappe.qb.DocType("Stock Reservation Entry")
|
||||
sb_entry = frappe.qb.DocType("Serial and Batch Entry")
|
||||
|
||||
batch_nos = (
|
||||
frappe.qb.from_(sre)
|
||||
.inner_join(sb_entry)
|
||||
.on(sre.name == sb_entry.parent)
|
||||
.select(sb_entry.batch_no)
|
||||
.where((sre.voucher_no == sales_order) & (sre.docstatus == 1))
|
||||
).run(pluck=True)
|
||||
|
||||
return set(batch_nos)
|
||||
|
||||
|
||||
def create_material_receipt(
|
||||
items: dict, warehouse: str = "_Test Warehouse - _TC", qty: float = 100
|
||||
) -> StockEntry:
|
||||
|
||||
@@ -30,6 +30,11 @@ def execute(filters=None):
|
||||
_("Please select either the Item or Warehouse or Warehouse Type filter to generate the report.")
|
||||
)
|
||||
|
||||
if not filters.from_date or not filters.to_date:
|
||||
frappe.throw(
|
||||
_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date")))
|
||||
)
|
||||
|
||||
if filters.from_date > filters.to_date:
|
||||
frappe.throw(_("From Date must be before To Date"))
|
||||
|
||||
|
||||
@@ -34,7 +34,18 @@ def update_filters_with_account(filters: Filters) -> None:
|
||||
|
||||
|
||||
def validate_filters(filters: Filters) -> None:
|
||||
if filters.from_date > filters.to_date:
|
||||
from_date = filters.from_date
|
||||
to_date = filters.to_date
|
||||
|
||||
if not from_date or not to_date:
|
||||
frappe.throw(
|
||||
_("{0} and {1} are mandatory").format(
|
||||
frappe.bold(_("From Date")),
|
||||
frappe.bold(_("To Date")),
|
||||
)
|
||||
)
|
||||
|
||||
if from_date > to_date:
|
||||
frappe.throw(_("From Date must be before To Date"))
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ delegators for methods reached from other doctypes / ``run_method``; internal
|
||||
helpers live here only.
|
||||
"""
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
import frappe
|
||||
from frappe import _, bold
|
||||
from frappe.utils import cstr, flt, get_link_to_form, getdate
|
||||
@@ -604,66 +606,63 @@ class SerialBatchBundleService:
|
||||
if not batches:
|
||||
return
|
||||
|
||||
field_mapper = {
|
||||
"Sales Invoice": [["Sales Order", "sales_order"]],
|
||||
"Delivery Note": [["Sales Order", "against_sales_order"]],
|
||||
"Stock Entry": [
|
||||
["Work Order", "work_order"],
|
||||
["Subcontracting Inward Order", "subcontracting_inward_order"],
|
||||
],
|
||||
reference_fields = {
|
||||
"Sales Invoice": ["sales_order"],
|
||||
"Delivery Note": ["against_sales_order"],
|
||||
"Stock Entry": ["work_order", "subcontracting_inward_order"],
|
||||
}.get(self.doc.doctype)
|
||||
|
||||
qty_field = {
|
||||
"Sales Invoice": "qty",
|
||||
"Delivery Note": "qty",
|
||||
"Stock Entry": "fg_completed_qty",
|
||||
}.get(self.doc.doctype)
|
||||
|
||||
reserved_batches_data = self.get_reserved_batches(batches)
|
||||
items = self.doc.items
|
||||
if self.doc.doctype == "Stock Entry":
|
||||
items = [self.doc]
|
||||
|
||||
for item in items:
|
||||
for field in field_mapper:
|
||||
if not item.get(field[1]):
|
||||
continue
|
||||
own_vouchers = {item.get(field) for item in items for field in reference_fields if item.get(field)}
|
||||
|
||||
value = item.get(field[1])
|
||||
for row in reserved_batches_data:
|
||||
if self.doc.doctype in ["Sales Invoice", "Delivery Note"] and row.item_code != item.get(
|
||||
"item_code"
|
||||
):
|
||||
continue
|
||||
outstanding_qty = defaultdict(float)
|
||||
reservations = defaultdict(list)
|
||||
for row in self.get_reserved_batches(batches):
|
||||
if row.voucher_no in own_vouchers:
|
||||
continue
|
||||
|
||||
if row.voucher_no == value:
|
||||
continue
|
||||
key = (row.batch_no, row.warehouse)
|
||||
outstanding = flt(row.qty) - flt(row.delivered_qty)
|
||||
outstanding_qty[key] += outstanding
|
||||
if outstanding > 0:
|
||||
reservations[key].append(row)
|
||||
|
||||
batch_qty = get_batch_qty(
|
||||
row.batch_no,
|
||||
row.warehouse,
|
||||
posting_date=self.doc.posting_date,
|
||||
posting_time=self.doc.posting_time,
|
||||
consider_negative_batches=True,
|
||||
)
|
||||
for (batch_no, warehouse), reserved_qty in outstanding_qty.items():
|
||||
if flt(reserved_qty, 6) <= 0:
|
||||
continue
|
||||
|
||||
if item.get(qty_field) < batch_qty:
|
||||
continue
|
||||
batch_qty = get_batch_qty(
|
||||
batch_no,
|
||||
warehouse,
|
||||
posting_date=self.doc.posting_date,
|
||||
posting_time=self.doc.posting_time,
|
||||
consider_negative_batches=True,
|
||||
)
|
||||
|
||||
frappe.throw(
|
||||
_(
|
||||
"The batch {0} is already reserved in {1} {2}. So, cannot proceed with the {3} {4}, which is created against the {5} {6}."
|
||||
).format(
|
||||
frappe.bold(row.batch_no),
|
||||
frappe.bold(row.voucher_type),
|
||||
frappe.bold(row.voucher_no),
|
||||
frappe.bold(self.doc.doctype),
|
||||
frappe.bold(self.doc.name),
|
||||
frappe.bold(field[0]),
|
||||
frappe.bold(value),
|
||||
),
|
||||
title=_("Reserved Batch Conflict"),
|
||||
)
|
||||
if flt(batch_qty, 6) >= flt(reserved_qty, 6):
|
||||
continue
|
||||
|
||||
vouchers = ", ".join(
|
||||
f"{frappe.bold(voucher_type)} {frappe.bold(voucher_no)}"
|
||||
for voucher_type, voucher_no in dict.fromkeys(
|
||||
(row.voucher_type, row.voucher_no) for row in reservations[(batch_no, warehouse)]
|
||||
)
|
||||
)
|
||||
frappe.throw(
|
||||
_(
|
||||
"The batch {0} is reserved for {1} in the warehouse {2} and the remaining quantity is not enough to cover the reservations. So, cannot proceed with the {3} {4}."
|
||||
).format(
|
||||
frappe.bold(batch_no),
|
||||
vouchers,
|
||||
frappe.bold(warehouse),
|
||||
frappe.bold(self.doc.doctype),
|
||||
frappe.bold(self.doc.name),
|
||||
),
|
||||
title=_("Reserved Batch Conflict"),
|
||||
)
|
||||
|
||||
def get_reserved_batches(self, batches):
|
||||
doctype = frappe.qb.DocType("Stock Reservation Entry")
|
||||
@@ -675,9 +674,10 @@ class SerialBatchBundleService:
|
||||
.on(doctype.name == child_doc.parent)
|
||||
.select(
|
||||
child_doc.batch_no,
|
||||
child_doc.qty,
|
||||
child_doc.delivered_qty,
|
||||
doctype.voucher_type,
|
||||
doctype.voucher_no,
|
||||
doctype.item_code,
|
||||
doctype.warehouse,
|
||||
)
|
||||
.where((doctype.docstatus == 1) & (child_doc.batch_no.isin(batches)))
|
||||
|
||||
@@ -306,6 +306,7 @@ def repost_future_sle(
|
||||
resume_item_wh_wise_last_posted_sle = (
|
||||
get_item_wh_wise_last_posted_sle_from_reposting_data(doc, reposting_data) or {}
|
||||
)
|
||||
item_wh_first_reposted = get_item_wh_first_reposted_from_reposting_data(doc, reposting_data) or {}
|
||||
if not items_to_be_repost:
|
||||
return
|
||||
|
||||
@@ -328,6 +329,7 @@ def repost_future_sle(
|
||||
"repost_doc": doc,
|
||||
"repost_affected_transaction": repost_affected_transaction,
|
||||
"item_wh_wise_last_posted_sle": resume_item_wh_wise_last_posted_sle,
|
||||
"item_wh_first_reposted": item_wh_first_reposted,
|
||||
},
|
||||
allow_negative_stock=allow_negative_stock,
|
||||
via_landed_cost_voucher=via_landed_cost_voucher,
|
||||
@@ -337,7 +339,14 @@ def repost_future_sle(
|
||||
|
||||
resume_item_wh_wise_last_posted_sle = {}
|
||||
repost_affected_transaction.update(obj.repost_affected_transaction)
|
||||
update_args_in_repost_item_valuation(doc, index, items_to_be_repost, repost_affected_transaction)
|
||||
item_wh_first_reposted = obj.item_wh_first_reposted
|
||||
update_args_in_repost_item_valuation(
|
||||
doc,
|
||||
index,
|
||||
items_to_be_repost,
|
||||
repost_affected_transaction,
|
||||
item_wh_first_reposted=item_wh_first_reposted,
|
||||
)
|
||||
|
||||
|
||||
def update_args_in_repost_item_valuation(
|
||||
@@ -346,11 +355,15 @@ def update_args_in_repost_item_valuation(
|
||||
items_to_be_repost,
|
||||
repost_affected_transaction,
|
||||
item_wh_wise_last_posted_sle=None,
|
||||
item_wh_first_reposted=None,
|
||||
):
|
||||
file_name = ""
|
||||
if not item_wh_wise_last_posted_sle:
|
||||
item_wh_wise_last_posted_sle = {}
|
||||
|
||||
if not item_wh_first_reposted:
|
||||
item_wh_first_reposted = {}
|
||||
|
||||
if doc.reposting_data_file:
|
||||
file_name = get_reposting_file_name(doc.doctype, doc.name)
|
||||
# frappe.delete_doc("File", file_name, ignore_permissions=True, delete_permanently=True)
|
||||
@@ -360,6 +373,7 @@ def update_args_in_repost_item_valuation(
|
||||
"repost_affected_transaction": repost_affected_transaction,
|
||||
"item_wh_wise_last_posted_sle": {str(k): v for k, v in item_wh_wise_last_posted_sle.items()}
|
||||
or {},
|
||||
"item_wh_first_reposted": {str(k): v for k, v in item_wh_first_reposted.items()},
|
||||
},
|
||||
doc,
|
||||
file_name,
|
||||
@@ -495,6 +509,16 @@ def get_item_wh_wise_last_posted_sle_from_reposting_data(doc, reposting_data=Non
|
||||
return frappe._dict()
|
||||
|
||||
|
||||
def get_item_wh_first_reposted_from_reposting_data(doc, reposting_data=None):
|
||||
if not reposting_data and doc and doc.reposting_data_file:
|
||||
reposting_data = get_reposting_data(doc.reposting_data_file)
|
||||
|
||||
if not reposting_data or not reposting_data.get("item_wh_first_reposted"):
|
||||
return {}
|
||||
|
||||
return {frappe.safe_eval(key): value for key, value in reposting_data.item_wh_first_reposted.items()}
|
||||
|
||||
|
||||
def get_reposting_data(file_path) -> dict:
|
||||
file_name = frappe.db.get_value(
|
||||
"File",
|
||||
@@ -688,6 +712,7 @@ class update_entries_after:
|
||||
self.distinct_sles = set()
|
||||
self.distinct_dependant_item_wh = set()
|
||||
self.prev_sle_dict = frappe._dict({})
|
||||
self.item_wh_first_reposted = dict(self.args.get("item_wh_first_reposted") or {})
|
||||
|
||||
def get_item_wh_wise_last_posted_sle(self):
|
||||
if self.args and self.args.get("item_wh_wise_last_posted_sle"):
|
||||
@@ -738,6 +763,10 @@ class update_entries_after:
|
||||
|
||||
i += 1
|
||||
item_wh_key = (sle.item_code, sle.warehouse)
|
||||
sle_datetime = sle.posting_datetime or get_combine_datetime(sle.posting_date, sle.posting_time)
|
||||
existing_datetime = self.item_wh_first_reposted.get(item_wh_key)
|
||||
if not existing_datetime or get_datetime(sle_datetime) < get_datetime(existing_datetime):
|
||||
self.item_wh_first_reposted[item_wh_key] = sle_datetime
|
||||
if item_wh_key not in self.prev_sle_dict:
|
||||
self.prev_sle_dict[item_wh_key] = get_previous_sle_of_current_voucher(sle)
|
||||
|
||||
@@ -832,6 +861,7 @@ class update_entries_after:
|
||||
self.items_to_be_repost,
|
||||
self.repost_affected_transaction,
|
||||
self.item_wh_wise_last_posted_sle,
|
||||
self.item_wh_first_reposted,
|
||||
)
|
||||
|
||||
if not frappe.in_test:
|
||||
|
||||
@@ -1,415 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"charts": [
|
||||
{
|
||||
"chart_name": "Subcontracting Order",
|
||||
"label": "Subcontracting Outward Order"
|
||||
}
|
||||
],
|
||||
"content": "[{\"id\":\"ednT7K5OAg\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Subcontracting Outward Order\",\"col\":12}},{\"id\":\"IlzVs7JD8u\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Subcontracting Outward Order Count\",\"col\":4}},{\"id\":\"wB9idWUvTB\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Subcontracting Inward Order Count\",\"col\":4}},{\"id\":\"4QwMfBRGk8\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Active Subcontracted Items\",\"col\":4}},{\"id\":\"yVEFZMqVwd\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Subcontracting Inward and Outward</b></span>\",\"col\":12}},{\"id\":\"PXXMxfhCfA\",\"type\":\"card\",\"data\":{\"card_name\":\"Subcontracting Inward Order\",\"col\":4}},{\"id\":\"ir3NsTvngO\",\"type\":\"card\",\"data\":{\"card_name\":\"Subcontracting Outward Order\",\"col\":4}},{\"id\":\"CIq-v5f5KC\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
|
||||
"creation": "2020-03-02 17:11:37.032604",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "rocket",
|
||||
"idx": 2,
|
||||
"is_hidden": 0,
|
||||
"label": "Subcontracting",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Reports",
|
||||
"link_count": 0,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Subcontract Order Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Subcontract Order Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Subcontracted Item To Be Received",
|
||||
"link_count": 0,
|
||||
"link_to": "Subcontracted Item To Be Received",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Subcontracted Raw Materials To Be Transferred",
|
||||
"link_count": 0,
|
||||
"link_to": "Subcontracted Raw Materials To Be Transferred",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Subcontracting Inward Order",
|
||||
"link_count": 3,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Sales Order",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Order",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Subcontracting Inward Order",
|
||||
"link_count": 0,
|
||||
"link_to": "Subcontracting Inward Order",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Subcontracting Delivery",
|
||||
"link_count": 0,
|
||||
"link_to": "Stock Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Subcontracting Outward Order",
|
||||
"link_count": 3,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Purchase Order",
|
||||
"link_count": 0,
|
||||
"link_to": "Purchase Order",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Subcontracting Outward Order",
|
||||
"link_count": 0,
|
||||
"link_to": "Subcontracting Order",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Subcontracting Receipt",
|
||||
"link_count": 0,
|
||||
"link_to": "Subcontracting Receipt",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-07-03 13:43:50.289920",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"module_onboarding": "Subcontracting Onboarding",
|
||||
"name": "Subcontracting",
|
||||
"number_cards": [
|
||||
{
|
||||
"label": "Subcontracting Outward Order Count",
|
||||
"number_card_name": "Subcontracting Outward Order Count"
|
||||
},
|
||||
{
|
||||
"label": "Active Subcontracted Items",
|
||||
"number_card_name": "Active Subcontracted Items"
|
||||
},
|
||||
{
|
||||
"label": "Subcontracting Inward Order Count",
|
||||
"number_card_name": "Subcontracting Inward Order Count"
|
||||
}
|
||||
],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"restrict_to_domain": "",
|
||||
"roles": [],
|
||||
"sequence_id": 8.0,
|
||||
"shortcuts": [],
|
||||
"sidebar_items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "house",
|
||||
"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"
|
||||
}
|
||||
@@ -6,21 +6,12 @@ import frappe
|
||||
|
||||
|
||||
def get_context(context):
|
||||
project_user = frappe.db.get_value(
|
||||
"Project User",
|
||||
{"parent": frappe.form_dict.project, "user": frappe.session.user},
|
||||
["user", "view_attachments", "hide_timesheets"],
|
||||
as_dict=True,
|
||||
)
|
||||
if frappe.session.user != "Administrator" and (not project_user or frappe.session.user == "Guest"):
|
||||
raise frappe.PermissionError
|
||||
project_user = validate_and_get_project_user(project=frappe.form_dict.project)
|
||||
|
||||
context.no_cache = 1
|
||||
context.show_sidebar = True
|
||||
project = frappe.get_doc("Project", frappe.form_dict.project)
|
||||
|
||||
project.has_permission("read")
|
||||
|
||||
project.tasks = get_tasks(
|
||||
project.name, start=0, item_status="open", search=frappe.form_dict.get("search")
|
||||
)
|
||||
@@ -66,6 +57,7 @@ def get_tasks(project, start=0, search=None, item_status=None):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_task_html(project: str, start: int = 0, item_status: str | None = None):
|
||||
validate_and_get_project_user(project=project)
|
||||
return frappe.render_template(
|
||||
"erpnext/templates/includes/projects/project_tasks.html",
|
||||
{
|
||||
@@ -106,6 +98,7 @@ def get_timesheets(project, start=0, search=None):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_timesheet_html(project: str, start: int = 0):
|
||||
validate_and_get_project_user(project=project)
|
||||
return frappe.render_template(
|
||||
"erpnext/templates/includes/projects/project_timesheets.html",
|
||||
{"doc": {"timesheets": get_timesheets(project, start)}},
|
||||
@@ -119,3 +112,12 @@ def get_attachments(project):
|
||||
filters={"attached_to_name": project, "attached_to_doctype": "Project", "is_private": 0},
|
||||
fields=["file_name", "file_url", "file_size"],
|
||||
)
|
||||
|
||||
|
||||
def validate_and_get_project_user(project: str):
|
||||
project_doc = frappe.get_doc("Project", project)
|
||||
project_doc.check_permission()
|
||||
|
||||
project_user = next((d for d in project_doc.users if d.user == frappe.session.user), None)
|
||||
|
||||
return project_user
|
||||
|
||||
72
erpnext/templates/pages/test_projects.py
Normal file
72
erpnext/templates/pages/test_projects.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.projects.doctype.project.test_project import make_project
|
||||
from erpnext.templates.pages.projects import validate_and_get_project_user
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestProjectsPage(ERPNextTestSuite):
|
||||
"""validate_and_get_project_user() gates the /projects portal page. It must raise
|
||||
frappe.PermissionError for a user who can't read the Project, and otherwise return
|
||||
that user's Project User row (or None if they're permitted but not listed as one --
|
||||
e.g. an internal Projects Manager browsing the portal)."""
|
||||
|
||||
def _create_user(self, email):
|
||||
if not frappe.db.exists("User", email):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "User",
|
||||
"email": email,
|
||||
"first_name": "Portal",
|
||||
"send_welcome_email": 0,
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
return email
|
||||
|
||||
def test_raises_permission_error_for_user_without_access(self):
|
||||
project = make_project({"project_name": f"_Test Portal Access {frappe.generate_hash(length=6)}"})
|
||||
outsider = self._create_user(f"outsider_{frappe.generate_hash(length=6)}@example.com")
|
||||
|
||||
with self.set_user(outsider):
|
||||
self.assertRaises(frappe.PermissionError, validate_and_get_project_user, project.name)
|
||||
|
||||
def test_allows_user_listed_as_project_user_and_returns_their_row(self):
|
||||
# Being a Project User shares the Project with that user (see
|
||||
# Project.control_access_for_project_users), which is what lets them past
|
||||
# check_permission() here.
|
||||
member = self._create_user(f"member_{frappe.generate_hash(length=6)}@example.com")
|
||||
|
||||
project = frappe.get_doc(
|
||||
doctype="Project",
|
||||
project_name=f"_Test Portal Access {frappe.generate_hash(length=6)}",
|
||||
status="Open",
|
||||
company="_Test Company",
|
||||
)
|
||||
project.append(
|
||||
"users", {"user": member, "view_attachments": 1, "hide_timesheets": 1, "welcome_email_sent": 1}
|
||||
)
|
||||
project.insert()
|
||||
|
||||
with self.set_user(member):
|
||||
project_user = validate_and_get_project_user(project.name)
|
||||
|
||||
self.assertIsNotNone(project_user)
|
||||
self.assertEqual(project_user.user, member)
|
||||
self.assertEqual(project_user.view_attachments, 1)
|
||||
self.assertEqual(project_user.hide_timesheets, 1)
|
||||
|
||||
def test_allows_internally_permitted_user_not_listed_as_project_user(self):
|
||||
# The permission gate must be the real permission system (check_permission()),
|
||||
# not "is this user in the Project's users child table" -- a Projects Manager
|
||||
# can open any project's portal page without ever being added as its user.
|
||||
project = make_project({"project_name": f"_Test Portal Access {frappe.generate_hash(length=6)}"})
|
||||
manager = self._create_user(f"manager_{frappe.generate_hash(length=6)}@example.com")
|
||||
frappe.get_doc("User", manager).add_roles("Projects Manager")
|
||||
|
||||
with self.set_user(manager):
|
||||
project_user = validate_and_get_project_user(project.name)
|
||||
|
||||
self.assertIsNone(project_user)
|
||||
@@ -181,6 +181,7 @@ class BootStrapTestData:
|
||||
self.make_location()
|
||||
self.make_price_list()
|
||||
self.make_item_price()
|
||||
self.make_currency_exchange()
|
||||
self.make_loyalty_program()
|
||||
self.make_shareholder()
|
||||
self.make_sales_taxes_template()
|
||||
@@ -2533,6 +2534,38 @@ class BootStrapTestData:
|
||||
]
|
||||
self.make_records(["item_code", "price_list", "price_list_rate"], records)
|
||||
|
||||
def make_currency_exchange(self):
|
||||
"""Seed current-dated USD<->INR rates so foreign-currency documents
|
||||
transacted on ``today()`` resolve an exchange rate deterministically.
|
||||
|
||||
Without this, ``get_exchange_rate`` finds no in-window Currency Exchange
|
||||
record and falls back to an external API that is unreachable in CI,
|
||||
returning ``0`` and breaking tests that create USD documents. The rates
|
||||
mirror the latest values in the Currency Exchange ``test_records`` so
|
||||
cost calculations stay unchanged regardless of which record is picked.
|
||||
"""
|
||||
records = [
|
||||
{
|
||||
"doctype": "Currency Exchange",
|
||||
"date": today(),
|
||||
"from_currency": "USD",
|
||||
"to_currency": "INR",
|
||||
"exchange_rate": 62.9,
|
||||
"for_buying": 1,
|
||||
"for_selling": 1,
|
||||
},
|
||||
{
|
||||
"doctype": "Currency Exchange",
|
||||
"date": today(),
|
||||
"from_currency": "INR",
|
||||
"to_currency": "USD",
|
||||
"exchange_rate": 0.0167,
|
||||
"for_buying": 1,
|
||||
"for_selling": 1,
|
||||
},
|
||||
]
|
||||
self.make_records(["from_currency", "to_currency", "date", "for_buying", "for_selling"], records)
|
||||
|
||||
def make_operation(self):
|
||||
records = [
|
||||
{"doctype": "Operation", "name": "_Test Operation 1", "workstation": "_Test Workstation 1"}
|
||||
|
||||
@@ -1,312 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"creation": "2026-01-23 14:36:51.659571",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace Sidebar",
|
||||
"header_icon": "database",
|
||||
"idx": 1,
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"modified": "2026-06-12 14:50:50.262533",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module_onboarding": "Accounting Onboarding",
|
||||
"name": "Accounts Setup",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "Accounts Setup"
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"creation": "2025-11-12 14:55:28.092635",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace Sidebar",
|
||||
"header_icon": "circle-dollar-sign",
|
||||
"idx": 0,
|
||||
"items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "book-open-check",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank Clearance",
|
||||
"link_to": "Bank Clearance",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "wrench",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank Reconciliation",
|
||||
"link_to": "Bank Reconciliation Tool",
|
||||
"link_type": "DocType",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank",
|
||||
"link_to": "Bank",
|
||||
"link_type": "DocType",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bank Guarantee",
|
||||
"link_to": "Bank Guarantee",
|
||||
"link_type": "DocType",
|
||||
"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",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "scroll-text",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Dunning",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dunning",
|
||||
"link_to": "Dunning",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Dunning Type",
|
||||
"link_to": "Dunning Type",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-07-03 00:06:13.017457",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Banking",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "Banking"
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"creation": "2025-11-10 16:53:45.409587",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace Sidebar",
|
||||
"header_icon": "accounting",
|
||||
"idx": 0,
|
||||
"items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"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,
|
||||
"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,
|
||||
"icon": "wallet",
|
||||
"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,
|
||||
"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,
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"modified": "2026-07-03 00:06:13.032297",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Budgeting",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "Budgeting"
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"creation": "2026-02-24 17:39:43.793115",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace Sidebar",
|
||||
"header_icon": "organization",
|
||||
"idx": 1,
|
||||
"items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"default_workspace": 1,
|
||||
"icon": "building-2",
|
||||
"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-07-03 00:37:22.942285",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"module_onboarding": "Organization Onboarding",
|
||||
"name": "Organization",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "Organization"
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"creation": "2025-11-10 16:49:07.269956",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace Sidebar",
|
||||
"header_icon": "",
|
||||
"idx": 0,
|
||||
"items": [
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"icon": "user",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Shareholder",
|
||||
"link_to": "Shareholder",
|
||||
"link_type": "DocType",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-07-03 00:06:13.040767",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Share Management",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "Share Management"
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"creation": "2025-11-17 14:49:59.811213",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace Sidebar",
|
||||
"header_icon": "getting-started",
|
||||
"idx": 0,
|
||||
"items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "house",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Subcontracting",
|
||||
"link_type": "Workspace",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 0,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Section Break"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 1,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Bill of Materials",
|
||||
"link_to": "BOM",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "notepad-text",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_type": "DocType",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-07-03 20:22:17.130321",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"module_onboarding": "Subcontracting Onboarding",
|
||||
"name": "Subcontracting",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "Subcontracting"
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"creation": "2025-11-10 16:08:50.904116",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace Sidebar",
|
||||
"header_icon": "accounting",
|
||||
"idx": 0,
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"modified": "2026-01-10 00:06:13.048591",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Subscriptions",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "Subscriptions"
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
{
|
||||
"app": "erpnext",
|
||||
"creation": "2025-11-12 15:03:06.180114",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace Sidebar",
|
||||
"header_icon": "money-coins-1",
|
||||
"idx": 0,
|
||||
"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": "",
|
||||
"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",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "package",
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Item Tax Template",
|
||||
"link_to": "Item Tax Template",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "database",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Setup",
|
||||
"link_type": "DocType",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"icon": "sheet",
|
||||
"indent": 1,
|
||||
"keep_closed": 1,
|
||||
"label": "Reports",
|
||||
"link_to": "",
|
||||
"link_type": "DocType",
|
||||
"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",
|
||||
"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",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-07-03 18:36:08.105306",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module_onboarding": "Accounting Onboarding",
|
||||
"name": "Taxes",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "Taxes"
|
||||
}
|
||||
Reference in New Issue
Block a user